@agentmeshhq/agent 0.4.2 → 0.4.4
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/LICENSE +21 -0
- package/dist/__tests__/cli-command-uniqueness.test.d.ts +10 -0
- package/dist/__tests__/cli-command-uniqueness.test.js +67 -0
- package/dist/__tests__/cli-command-uniqueness.test.js.map +1 -0
- package/dist/__tests__/evicted-cleanup.test.d.ts +10 -0
- package/dist/__tests__/evicted-cleanup.test.js +459 -0
- package/dist/__tests__/evicted-cleanup.test.js.map +1 -0
- package/dist/__tests__/local.test.d.ts +1 -0
- package/dist/__tests__/local.test.js +124 -0
- package/dist/__tests__/local.test.js.map +1 -0
- package/dist/__tests__/tmux-send.test.d.ts +10 -0
- package/dist/__tests__/tmux-send.test.js +96 -0
- package/dist/__tests__/tmux-send.test.js.map +1 -0
- package/dist/cli/inbox.d.ts +5 -0
- package/dist/cli/inbox.js +123 -0
- package/dist/cli/inbox.js.map +1 -0
- package/dist/cli/index.js +263 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/issue.d.ts +42 -0
- package/dist/cli/issue.js +297 -0
- package/dist/cli/issue.js.map +1 -0
- package/dist/cli/local.d.ts +27 -6
- package/dist/cli/local.js +319 -36
- package/dist/cli/local.js.map +1 -1
- package/dist/cli/ready.d.ts +5 -0
- package/dist/cli/ready.js +131 -0
- package/dist/cli/ready.js.map +1 -0
- package/dist/cli/sync.d.ts +8 -0
- package/dist/cli/sync.js +154 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli/token.js +242 -9
- package/dist/cli/token.js.map +1 -1
- package/dist/cli/whoami.d.ts +6 -0
- package/dist/cli/whoami.js +109 -5
- package/dist/cli/whoami.js.map +1 -1
- package/dist/core/cleanup/eligibility.d.ts +41 -0
- package/dist/core/cleanup/eligibility.js +64 -0
- package/dist/core/cleanup/eligibility.js.map +1 -0
- package/dist/core/cleanup/scheduler.d.ts +50 -0
- package/dist/core/cleanup/scheduler.js +120 -0
- package/dist/core/cleanup/scheduler.js.map +1 -0
- package/dist/core/cleanup/worker.d.ts +63 -0
- package/dist/core/cleanup/worker.js +191 -0
- package/dist/core/cleanup/worker.js.map +1 -0
- package/dist/core/daemon.d.ts +1 -0
- package/dist/core/daemon.js +18 -0
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/heartbeat.d.ts +6 -1
- package/dist/core/heartbeat.js +44 -39
- package/dist/core/heartbeat.js.map +1 -1
- package/dist/core/issue-cache.d.ts +44 -0
- package/dist/core/issue-cache.js +75 -0
- package/dist/core/issue-cache.js.map +1 -0
- package/dist/core/registry.d.ts +1 -0
- package/dist/core/registry.js +1 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/core/token-lifecycle.d.ts +81 -0
- package/dist/core/token-lifecycle.js +210 -0
- package/dist/core/token-lifecycle.js.map +1 -0
- package/dist/core/token-lifecycle.test.d.ts +10 -0
- package/dist/core/token-lifecycle.test.js +309 -0
- package/dist/core/token-lifecycle.test.js.map +1 -0
- package/package.json +11 -12
package/dist/cli/whoami.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pc from "picocolors";
|
|
2
2
|
import { loadConfig, loadState } from "../config/loader.js";
|
|
3
|
+
import { fetchOnboard } from "../core/registry.js";
|
|
3
4
|
import { getRunnerDisplayName } from "../core/runner.js";
|
|
4
5
|
import { getTokenExpiry } from "../utils/jwt.js";
|
|
5
6
|
export async function whoami(agentName) {
|
|
@@ -11,21 +12,18 @@ export async function whoami(agentName) {
|
|
|
11
12
|
const state = loadState();
|
|
12
13
|
// If no agent name provided, check environment or show all
|
|
13
14
|
if (!agentName) {
|
|
14
|
-
// Check if running as an agent (env vars set)
|
|
15
15
|
const envAgentId = process.env.AGENTMESH_AGENT_ID;
|
|
16
16
|
const envToken = process.env.AGENT_TOKEN;
|
|
17
17
|
if (envAgentId && envToken) {
|
|
18
|
-
// We're running inside an agent session
|
|
18
|
+
// We're running inside an agent session — show canonical context
|
|
19
19
|
const expiry = getTokenExpiry(envToken);
|
|
20
20
|
const expiryStr = formatExpiry(expiry);
|
|
21
|
-
// Try to find agent in state for runtime info
|
|
22
21
|
const agentState = state.agents.find((a) => a.agentId === envAgentId);
|
|
23
22
|
console.log(pc.bold("Current Agent"));
|
|
24
23
|
console.log(` ID: ${pc.cyan(envAgentId)}`);
|
|
25
24
|
console.log(` Workspace: ${pc.dim(config.workspace)}`);
|
|
26
25
|
console.log(` Token: ${expiryStr}`);
|
|
27
26
|
console.log(` Hub: ${pc.dim(config.hubUrl)}`);
|
|
28
|
-
// Show runtime model info if available
|
|
29
27
|
if (agentState?.runtimeModel) {
|
|
30
28
|
const runnerName = agentState.runnerType
|
|
31
29
|
? getRunnerDisplayName(agentState.runnerType)
|
|
@@ -33,6 +31,8 @@ export async function whoami(agentName) {
|
|
|
33
31
|
console.log(` Model: ${pc.cyan(agentState.runtimeModel)}`);
|
|
34
32
|
console.log(` Runner: ${pc.dim(runnerName)}`);
|
|
35
33
|
}
|
|
34
|
+
// Extended canonical context from HQ
|
|
35
|
+
await printCanonicalContext(config.hubUrl, envToken, " ");
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
// Show all registered agents
|
|
@@ -78,7 +78,6 @@ export async function whoami(agentName) {
|
|
|
78
78
|
console.log(` Session: ${pc.dim(agent.tmuxSession || "none")}`);
|
|
79
79
|
console.log(` Started: ${pc.dim(agent.startedAt || "unknown")}`);
|
|
80
80
|
console.log(` Hub: ${pc.dim(config.hubUrl)}`);
|
|
81
|
-
// Show runtime model info
|
|
82
81
|
if (agent.runtimeModel) {
|
|
83
82
|
const runnerName = agent.runnerType
|
|
84
83
|
? getRunnerDisplayName(agent.runnerType)
|
|
@@ -86,6 +85,111 @@ export async function whoami(agentName) {
|
|
|
86
85
|
console.log(` Model: ${pc.cyan(agent.runtimeModel)}`);
|
|
87
86
|
console.log(` Runner: ${pc.dim(runnerName)}`);
|
|
88
87
|
}
|
|
88
|
+
// Extended canonical context from HQ when token is available
|
|
89
|
+
if (agent.token) {
|
|
90
|
+
await printCanonicalContext(config.hubUrl, agent.token, " ");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Fetch and print canonical workspace/team/role context from the HQ onboard API.
|
|
95
|
+
* Prints nothing on error so it degrades gracefully.
|
|
96
|
+
*/
|
|
97
|
+
async function printCanonicalContext(hubUrl, token, indent) {
|
|
98
|
+
try {
|
|
99
|
+
const onboard = await fetchOnboard(hubUrl, token);
|
|
100
|
+
if (!onboard)
|
|
101
|
+
return;
|
|
102
|
+
const { assignment, project, team } = onboard;
|
|
103
|
+
if (assignment) {
|
|
104
|
+
console.log(`${indent}Role: ${pc.dim(assignment.role)}`);
|
|
105
|
+
}
|
|
106
|
+
if (project) {
|
|
107
|
+
console.log(`${indent}Project: ${pc.dim(`${project.name} (${project.code})`)}`);
|
|
108
|
+
}
|
|
109
|
+
if (team && team.length > 0) {
|
|
110
|
+
const teammates = team
|
|
111
|
+
.filter((t) => t.status === "online" || t.status === "idle")
|
|
112
|
+
.map((t) => t.display_name)
|
|
113
|
+
.slice(0, 5);
|
|
114
|
+
if (teammates.length > 0) {
|
|
115
|
+
console.log(`${indent}Team: ${pc.dim(teammates.join(", "))}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Silently ignore — canonical context is best-effort
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Resolve the canonical Product Owner agent ID for the current team/workspace.
|
|
125
|
+
* Usage: agentmesh po resolve
|
|
126
|
+
* agentmesh whoami --po
|
|
127
|
+
*/
|
|
128
|
+
export async function resolvePO(agentName) {
|
|
129
|
+
const config = loadConfig();
|
|
130
|
+
if (!config) {
|
|
131
|
+
console.log(pc.red("No config found. Run 'agentmesh init' first."));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
// Resolve token
|
|
135
|
+
let token;
|
|
136
|
+
const envToken = process.env.AGENT_TOKEN;
|
|
137
|
+
if (envToken && !agentName) {
|
|
138
|
+
token = envToken;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const state = loadState();
|
|
142
|
+
const agent = agentName ? state.agents.find((a) => a.name === agentName) : state.agents[0];
|
|
143
|
+
if (!agent) {
|
|
144
|
+
console.log(pc.red("No agent found."));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
token = agent.token;
|
|
148
|
+
}
|
|
149
|
+
if (!token) {
|
|
150
|
+
console.log(pc.red("No token available. Run 'agentmesh token refresh' first."));
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const onboard = await fetchOnboard(config.hubUrl, token);
|
|
155
|
+
if (!onboard) {
|
|
156
|
+
console.log(pc.red("Could not fetch workspace context from HQ."));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
// Find PO in team (role === "po" or display_name contains "po")
|
|
160
|
+
const po = onboard.team.find((t) => t.role?.toLowerCase() === "po" ||
|
|
161
|
+
t.role?.toLowerCase() === "product-owner" ||
|
|
162
|
+
t.display_name?.toLowerCase().includes("forge-po") ||
|
|
163
|
+
t.display_name?.toLowerCase().includes("product owner"));
|
|
164
|
+
if (po) {
|
|
165
|
+
console.log(pc.bold("Product Owner"));
|
|
166
|
+
console.log(` ID: ${pc.cyan(po.agent_id)}`);
|
|
167
|
+
console.log(` Name: ${pc.dim(po.display_name)}`);
|
|
168
|
+
console.log(` Status: ${formatOnlineStatus(po.status)}`);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// Fallback: print all team members so caller can identify PO
|
|
172
|
+
console.log(pc.yellow("No explicit PO role found. Team members:"));
|
|
173
|
+
for (const member of onboard.team) {
|
|
174
|
+
console.log(` ${pc.cyan(member.agent_id)} — ${member.display_name} (${member.role})`);
|
|
175
|
+
}
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
console.log(pc.red(`Failed to resolve PO: ${err.message}`));
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function formatOnlineStatus(status) {
|
|
185
|
+
switch (status) {
|
|
186
|
+
case "online":
|
|
187
|
+
return pc.green("online");
|
|
188
|
+
case "idle":
|
|
189
|
+
return pc.yellow("idle");
|
|
190
|
+
default:
|
|
191
|
+
return pc.dim(status);
|
|
192
|
+
}
|
|
89
193
|
}
|
|
90
194
|
function formatExpiry(expiry) {
|
|
91
195
|
if (!expiry) {
|
package/dist/cli/whoami.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/cli/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,SAAkB;IAC7C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAE1B,2DAA2D;IAC3D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/cli/whoami.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Eligibility — GH-421
|
|
3
|
+
*
|
|
4
|
+
* Determines which agents are eligible for filesystem cleanup.
|
|
5
|
+
*
|
|
6
|
+
* Rules:
|
|
7
|
+
* - Agent status must be "evicted"
|
|
8
|
+
* - Agent must have been evicted for at least `retentionMs` (default 2 hours)
|
|
9
|
+
* - Active/online/stale agents are always skipped
|
|
10
|
+
* - Agents with no lastHeartbeatAt are skipped (unknown state)
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_RETENTION_MS: number;
|
|
13
|
+
export interface AgentRecord {
|
|
14
|
+
agentId: string;
|
|
15
|
+
/** Display name — used to locate ~/.agentmesh/opencode-data/<name> */
|
|
16
|
+
displayName: string;
|
|
17
|
+
status: string;
|
|
18
|
+
/** ISO timestamp of last heartbeat (used to infer eviction age) */
|
|
19
|
+
lastHeartbeatAt: string | null;
|
|
20
|
+
workspaceId: string;
|
|
21
|
+
}
|
|
22
|
+
export interface EligibilityResult {
|
|
23
|
+
eligible: boolean;
|
|
24
|
+
reason: string;
|
|
25
|
+
evictedAgeMs?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check whether a single agent is eligible for cleanup.
|
|
29
|
+
*
|
|
30
|
+
* @param agent - Agent record from Hub API
|
|
31
|
+
* @param retentionMs - Minimum eviction age before cleanup (default 2h)
|
|
32
|
+
* @param now - Override for current time (testing)
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkEligibility(agent: AgentRecord, retentionMs?: number, now?: Date): EligibilityResult;
|
|
35
|
+
/**
|
|
36
|
+
* Filter a list of agents to those eligible for cleanup.
|
|
37
|
+
*/
|
|
38
|
+
export declare function filterEligible(agents: AgentRecord[], retentionMs?: number, now?: Date): Array<{
|
|
39
|
+
agent: AgentRecord;
|
|
40
|
+
eligibility: EligibilityResult;
|
|
41
|
+
}>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Eligibility — GH-421
|
|
3
|
+
*
|
|
4
|
+
* Determines which agents are eligible for filesystem cleanup.
|
|
5
|
+
*
|
|
6
|
+
* Rules:
|
|
7
|
+
* - Agent status must be "evicted"
|
|
8
|
+
* - Agent must have been evicted for at least `retentionMs` (default 2 hours)
|
|
9
|
+
* - Active/online/stale agents are always skipped
|
|
10
|
+
* - Agents with no lastHeartbeatAt are skipped (unknown state)
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_RETENTION_MS = 2 * 60 * 60 * 1000; // 2 hours
|
|
13
|
+
/** Statuses that indicate the agent is safe to clean up */
|
|
14
|
+
const EVICTED_STATUS = "evicted";
|
|
15
|
+
/** Statuses that MUST never be cleaned */
|
|
16
|
+
const SAFE_STATUSES = new Set(["online", "idle", "active", "stale"]);
|
|
17
|
+
/**
|
|
18
|
+
* Check whether a single agent is eligible for cleanup.
|
|
19
|
+
*
|
|
20
|
+
* @param agent - Agent record from Hub API
|
|
21
|
+
* @param retentionMs - Minimum eviction age before cleanup (default 2h)
|
|
22
|
+
* @param now - Override for current time (testing)
|
|
23
|
+
*/
|
|
24
|
+
export function checkEligibility(agent, retentionMs = DEFAULT_RETENTION_MS, now = new Date()) {
|
|
25
|
+
// Safety guard: never touch running agents
|
|
26
|
+
if (SAFE_STATUSES.has(agent.status)) {
|
|
27
|
+
return { eligible: false, reason: `status=${agent.status} (safe — skipped)` };
|
|
28
|
+
}
|
|
29
|
+
// Must be explicitly evicted
|
|
30
|
+
if (agent.status !== EVICTED_STATUS) {
|
|
31
|
+
return { eligible: false, reason: `status=${agent.status} (not evicted)` };
|
|
32
|
+
}
|
|
33
|
+
// Need a heartbeat timestamp to measure eviction age
|
|
34
|
+
if (!agent.lastHeartbeatAt) {
|
|
35
|
+
return { eligible: false, reason: "no lastHeartbeatAt — unknown eviction time" };
|
|
36
|
+
}
|
|
37
|
+
const lastSeen = new Date(agent.lastHeartbeatAt);
|
|
38
|
+
if (Number.isNaN(lastSeen.getTime())) {
|
|
39
|
+
return { eligible: false, reason: `invalid lastHeartbeatAt: ${agent.lastHeartbeatAt}` };
|
|
40
|
+
}
|
|
41
|
+
const evictedAgeMs = now.getTime() - lastSeen.getTime();
|
|
42
|
+
if (evictedAgeMs < retentionMs) {
|
|
43
|
+
const remainingMin = Math.ceil((retentionMs - evictedAgeMs) / 60_000);
|
|
44
|
+
return {
|
|
45
|
+
eligible: false,
|
|
46
|
+
reason: `evicted only ${Math.floor(evictedAgeMs / 60_000)}m ago (need ${Math.floor(retentionMs / 60_000)}m) — ${remainingMin}m remaining`,
|
|
47
|
+
evictedAgeMs,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
eligible: true,
|
|
52
|
+
reason: `evicted for ${Math.floor(evictedAgeMs / 60_000)}m (threshold ${Math.floor(retentionMs / 60_000)}m)`,
|
|
53
|
+
evictedAgeMs,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Filter a list of agents to those eligible for cleanup.
|
|
58
|
+
*/
|
|
59
|
+
export function filterEligible(agents, retentionMs = DEFAULT_RETENTION_MS, now = new Date()) {
|
|
60
|
+
return agents
|
|
61
|
+
.map((agent) => ({ agent, eligibility: checkEligibility(agent, retentionMs, now) }))
|
|
62
|
+
.filter(({ eligibility }) => eligibility.eligible);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=eligibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.js","sourceRoot":"","sources":["../../../src/core/cleanup/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,UAAU;AAElE,2DAA2D;AAC3D,MAAM,cAAc,GAAG,SAAS,CAAC;AAEjC,0CAA0C;AAC1C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAkBrE;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAkB,EAClB,WAAW,GAAG,oBAAoB,EAClC,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,2CAA2C;IAC3C,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,KAAK,CAAC,MAAM,mBAAmB,EAAE,CAAC;IAChF,CAAC;IAED,6BAA6B;IAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QACpC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,KAAK,CAAC,MAAM,gBAAgB,EAAE,CAAC;IAC7E,CAAC;IAED,qDAAqD;IACrD,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;QAC3B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;IACnF,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,4BAA4B,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;IAC1F,CAAC;IAED,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;IAExD,IAAI,YAAY,GAAG,WAAW,EAAE,CAAC;QAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,GAAG,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC;QACtE,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,gBAAgB,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,eAAe,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,QAAQ,YAAY,aAAa;YACzI,YAAY;SACb,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,eAAe,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI;QAC5G,YAAY;KACb,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAqB,EACrB,WAAW,GAAG,oBAAoB,EAClC,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;SACnF,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Scheduler — GH-421
|
|
3
|
+
*
|
|
4
|
+
* Periodic job that:
|
|
5
|
+
* 1. Fetches all agents in the workspace from the Hub API
|
|
6
|
+
* 2. Filters to eviction-eligible agents (status=evicted, age>=2h)
|
|
7
|
+
* 3. Runs cleanupAgents() for each eligible agent
|
|
8
|
+
*
|
|
9
|
+
* Designed to run as a setInterval loop inside the agent daemon,
|
|
10
|
+
* configurable via env vars:
|
|
11
|
+
* AGENTMESH_CLEANUP_INTERVAL_MS (default 10 minutes)
|
|
12
|
+
* AGENTMESH_CLEANUP_RETENTION_MS (default 2 hours)
|
|
13
|
+
* AGENTMESH_CLEANUP_DRY_RUN (default false)
|
|
14
|
+
*/
|
|
15
|
+
export interface CleanupSchedulerConfig {
|
|
16
|
+
/** Hub base URL, e.g. https://agentmeshhq.dev */
|
|
17
|
+
hubUrl: string;
|
|
18
|
+
/** Bearer token for Hub API calls */
|
|
19
|
+
token: string;
|
|
20
|
+
/** Workspace ID to query */
|
|
21
|
+
workspace: string;
|
|
22
|
+
/** How often to run (ms). Default: 10 min */
|
|
23
|
+
intervalMs?: number;
|
|
24
|
+
/** Minimum eviction age before cleanup (ms). Default: 2h */
|
|
25
|
+
retentionMs?: number;
|
|
26
|
+
/** Dry-run mode: log but don't delete. Default: false */
|
|
27
|
+
dryRun?: boolean;
|
|
28
|
+
/** Max log lines to archive per agent. Default: 200 */
|
|
29
|
+
archiveLogLines?: number;
|
|
30
|
+
/** Override home dir (testing) */
|
|
31
|
+
homeDir?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SchedulerRunResult {
|
|
34
|
+
checkedAt: string;
|
|
35
|
+
totalAgents: number;
|
|
36
|
+
eligibleCount: number;
|
|
37
|
+
processed: number;
|
|
38
|
+
succeeded: number;
|
|
39
|
+
failed: number;
|
|
40
|
+
errors: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Execute one cleanup cycle: fetch agents → filter eligible → clean.
|
|
44
|
+
*/
|
|
45
|
+
export declare function runCleanupCycle(config: CleanupSchedulerConfig): Promise<SchedulerRunResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Start the cleanup scheduler as a recurring setInterval.
|
|
48
|
+
* Returns a stop function (clears the interval).
|
|
49
|
+
*/
|
|
50
|
+
export declare function startCleanupScheduler(config: CleanupSchedulerConfig): () => void;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Scheduler — GH-421
|
|
3
|
+
*
|
|
4
|
+
* Periodic job that:
|
|
5
|
+
* 1. Fetches all agents in the workspace from the Hub API
|
|
6
|
+
* 2. Filters to eviction-eligible agents (status=evicted, age>=2h)
|
|
7
|
+
* 3. Runs cleanupAgents() for each eligible agent
|
|
8
|
+
*
|
|
9
|
+
* Designed to run as a setInterval loop inside the agent daemon,
|
|
10
|
+
* configurable via env vars:
|
|
11
|
+
* AGENTMESH_CLEANUP_INTERVAL_MS (default 10 minutes)
|
|
12
|
+
* AGENTMESH_CLEANUP_RETENTION_MS (default 2 hours)
|
|
13
|
+
* AGENTMESH_CLEANUP_DRY_RUN (default false)
|
|
14
|
+
*/
|
|
15
|
+
import { DEFAULT_RETENTION_MS, filterEligible } from "./eligibility.js";
|
|
16
|
+
import { cleanupAgents } from "./worker.js";
|
|
17
|
+
const DEFAULT_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
|
18
|
+
// ─── Hub API fetch ─────────────────────────────────────────────────────────────
|
|
19
|
+
async function fetchWorkspaceAgents(hubUrl, workspace, token) {
|
|
20
|
+
const resp = await fetch(`${hubUrl}/api/v1/workspaces/${workspace}/agents`, {
|
|
21
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
22
|
+
});
|
|
23
|
+
if (!resp.ok) {
|
|
24
|
+
throw new Error(`Failed to fetch agents: ${resp.status} ${resp.statusText}`);
|
|
25
|
+
}
|
|
26
|
+
const data = (await resp.json());
|
|
27
|
+
const rows = data.agents ?? data.data ?? [];
|
|
28
|
+
return rows.map((r) => ({
|
|
29
|
+
agentId: r.agent_id,
|
|
30
|
+
displayName: r.display_name,
|
|
31
|
+
status: r.status,
|
|
32
|
+
lastHeartbeatAt: r.last_heartbeat_at,
|
|
33
|
+
workspaceId: r.workspace_id,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Execute one cleanup cycle: fetch agents → filter eligible → clean.
|
|
38
|
+
*/
|
|
39
|
+
export async function runCleanupCycle(config) {
|
|
40
|
+
const retentionMs = config.retentionMs ?? DEFAULT_RETENTION_MS;
|
|
41
|
+
const dryRun = config.dryRun ?? false;
|
|
42
|
+
const opts = {
|
|
43
|
+
dryRun,
|
|
44
|
+
archiveLogLines: config.archiveLogLines ?? 200,
|
|
45
|
+
homeDir: config.homeDir,
|
|
46
|
+
};
|
|
47
|
+
const checkedAt = new Date().toISOString();
|
|
48
|
+
const errors = [];
|
|
49
|
+
let allAgents = [];
|
|
50
|
+
try {
|
|
51
|
+
allAgents = await fetchWorkspaceAgents(config.hubUrl, config.workspace, config.token);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const msg = `[cleanup] Failed to fetch agents: ${err instanceof Error ? err.message : String(err)}`;
|
|
55
|
+
console.error(msg);
|
|
56
|
+
return {
|
|
57
|
+
checkedAt,
|
|
58
|
+
totalAgents: 0,
|
|
59
|
+
eligibleCount: 0,
|
|
60
|
+
processed: 0,
|
|
61
|
+
succeeded: 0,
|
|
62
|
+
failed: 0,
|
|
63
|
+
errors: [msg],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const eligible = filterEligible(allAgents, retentionMs);
|
|
67
|
+
if (eligible.length === 0) {
|
|
68
|
+
return {
|
|
69
|
+
checkedAt,
|
|
70
|
+
totalAgents: allAgents.length,
|
|
71
|
+
eligibleCount: 0,
|
|
72
|
+
processed: 0,
|
|
73
|
+
succeeded: 0,
|
|
74
|
+
failed: 0,
|
|
75
|
+
errors: [],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
console.log(`[cleanup] ${eligible.length} agent(s) eligible for cleanup (${dryRun ? "DRY-RUN" : "LIVE"})`);
|
|
79
|
+
const batch = await cleanupAgents(eligible.map(({ agent }) => agent), opts);
|
|
80
|
+
for (const r of batch.results) {
|
|
81
|
+
if (!r.success && r.error) {
|
|
82
|
+
errors.push(`${r.agentName}: ${r.error}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (batch.succeeded > 0 || batch.failed > 0) {
|
|
86
|
+
console.log(`[cleanup] done — succeeded=${batch.succeeded} failed=${batch.failed}`, errors.length ? errors : "");
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
checkedAt,
|
|
90
|
+
totalAgents: allAgents.length,
|
|
91
|
+
eligibleCount: eligible.length,
|
|
92
|
+
processed: batch.processed,
|
|
93
|
+
succeeded: batch.succeeded,
|
|
94
|
+
failed: batch.failed,
|
|
95
|
+
errors,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// ─── scheduler ────────────────────────────────────────────────────────────────
|
|
99
|
+
/**
|
|
100
|
+
* Start the cleanup scheduler as a recurring setInterval.
|
|
101
|
+
* Returns a stop function (clears the interval).
|
|
102
|
+
*/
|
|
103
|
+
export function startCleanupScheduler(config) {
|
|
104
|
+
const intervalMs = config.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
105
|
+
const run = async () => {
|
|
106
|
+
try {
|
|
107
|
+
await runCleanupCycle(config);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
console.error("[cleanup] Unhandled error in cleanup cycle:", err);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const id = setInterval(run, intervalMs);
|
|
114
|
+
console.log(`[cleanup] Scheduler started — interval=${intervalMs}ms retention=${config.retentionMs ?? DEFAULT_RETENTION_MS}ms dryRun=${config.dryRun ?? false}`);
|
|
115
|
+
return () => {
|
|
116
|
+
clearInterval(id);
|
|
117
|
+
console.log("[cleanup] Scheduler stopped");
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../../src/core/cleanup/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Worker — GH-421
|
|
3
|
+
*
|
|
4
|
+
* Executes filesystem cleanup for eviction-eligible agents:
|
|
5
|
+
* 1. Stop any residual tmux session for the agent
|
|
6
|
+
* 2. Optionally archive diagnostics (last N log lines)
|
|
7
|
+
* 3. Remove ~/.agentmesh/opencode-data/<agentName>
|
|
8
|
+
* 4. Remove ~/.agentmesh/agents/<agentName>.waiting signal file if present
|
|
9
|
+
* 5. Emit a structured audit event for every action
|
|
10
|
+
*
|
|
11
|
+
* Safety guarantees:
|
|
12
|
+
* - Dry-run mode: logs actions without touching filesystem
|
|
13
|
+
* - Idempotent: re-running on an already-cleaned agent is a no-op
|
|
14
|
+
* - Never removes paths outside ~/.agentmesh/opencode-data/
|
|
15
|
+
* - Never removes the base ~/.agentmesh/ directory
|
|
16
|
+
*/
|
|
17
|
+
import type { AgentRecord } from "./eligibility.js";
|
|
18
|
+
export interface CleanupOptions {
|
|
19
|
+
/** When true, log actions but make no filesystem changes (default false) */
|
|
20
|
+
dryRun?: boolean;
|
|
21
|
+
/** Max lines to archive from the most recent log file (default 200, 0 = skip) */
|
|
22
|
+
archiveLogLines?: number;
|
|
23
|
+
/** Override home directory for testing */
|
|
24
|
+
homeDir?: string;
|
|
25
|
+
}
|
|
26
|
+
export type CleanupActionType = "session_killed" | "session_not_found" | "logs_archived" | "dir_removed" | "dir_not_found" | "waiting_file_removed" | "skipped_dry_run" | "error";
|
|
27
|
+
export interface CleanupAction {
|
|
28
|
+
type: CleanupActionType;
|
|
29
|
+
agentId: string;
|
|
30
|
+
agentName: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
detail?: string;
|
|
33
|
+
timestamp: string;
|
|
34
|
+
dryRun: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface CleanupResult {
|
|
37
|
+
agentId: string;
|
|
38
|
+
agentName: string;
|
|
39
|
+
success: boolean;
|
|
40
|
+
actions: CleanupAction[];
|
|
41
|
+
error?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Clean up the filesystem artifacts for a single eviction-eligible agent.
|
|
45
|
+
*
|
|
46
|
+
* Steps (all logged as CleanupAction entries):
|
|
47
|
+
* 1. Kill residual tmux session (if present)
|
|
48
|
+
* 2. Archive last N log lines to ~/.agentmesh/logs/cleanup-<agent>-<ts>.txt
|
|
49
|
+
* 3. Remove ~/.agentmesh/opencode-data/<agentName>/ recursively
|
|
50
|
+
* 4. Remove ~/.agentmesh/agents/<agentName>.waiting (if exists)
|
|
51
|
+
*/
|
|
52
|
+
export declare function cleanupAgent(agent: AgentRecord, opts?: CleanupOptions): Promise<CleanupResult>;
|
|
53
|
+
export interface BatchCleanupResult {
|
|
54
|
+
processed: number;
|
|
55
|
+
succeeded: number;
|
|
56
|
+
failed: number;
|
|
57
|
+
results: CleanupResult[];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Run cleanup for multiple eligible agents.
|
|
61
|
+
* Failures on one agent never abort the others.
|
|
62
|
+
*/
|
|
63
|
+
export declare function cleanupAgents(agents: AgentRecord[], opts?: CleanupOptions): Promise<BatchCleanupResult>;
|