@agentmeshhq/agent 0.4.12 → 0.4.14
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/__tests__/context-template.test.js +9 -0
- package/dist/__tests__/context-template.test.js.map +1 -1
- package/dist/__tests__/daemon-hub-resilience.test.d.ts +12 -0
- package/dist/__tests__/daemon-hub-resilience.test.js +144 -0
- package/dist/__tests__/daemon-hub-resilience.test.js.map +1 -0
- package/dist/__tests__/inbox-poll.test.d.ts +15 -0
- package/dist/__tests__/inbox-poll.test.js +93 -0
- package/dist/__tests__/inbox-poll.test.js.map +1 -0
- package/dist/__tests__/injector.test.js +49 -2
- package/dist/__tests__/injector.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +35 -3
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/tmux-send.test.js +9 -0
- package/dist/__tests__/tmux-send.test.js.map +1 -1
- package/dist/__tests__/token-rejection-recovery.test.d.ts +16 -0
- package/dist/__tests__/token-rejection-recovery.test.js +241 -0
- package/dist/__tests__/token-rejection-recovery.test.js.map +1 -0
- package/dist/__tests__/watcher-401-recovery.test.d.ts +1 -0
- package/dist/__tests__/watcher-401-recovery.test.js +146 -0
- package/dist/__tests__/watcher-401-recovery.test.js.map +1 -0
- package/dist/cli/attach.js +55 -0
- package/dist/cli/attach.js.map +1 -1
- package/dist/cli/index.js +23 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/watcher.d.ts +7 -0
- package/dist/cli/watcher.js +239 -25
- package/dist/cli/watcher.js.map +1 -1
- package/dist/core/daemon/context-template.d.ts +2 -0
- package/dist/core/daemon/context-template.js +2 -2
- package/dist/core/daemon/context-template.js.map +1 -1
- package/dist/core/daemon.d.ts +1 -0
- package/dist/core/daemon.js +92 -60
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/heartbeat.d.ts +3 -1
- package/dist/core/heartbeat.js +5 -6
- package/dist/core/heartbeat.js.map +1 -1
- package/dist/core/injector.d.ts +1 -1
- package/dist/core/injector.js +20 -8
- package/dist/core/injector.js.map +1 -1
- package/dist/core/project-watcher-loop.d.ts +4 -0
- package/dist/core/project-watcher-loop.js +11 -1
- package/dist/core/project-watcher-loop.js.map +1 -1
- package/dist/core/tmux.js +3 -0
- package/dist/core/tmux.js.map +1 -1
- package/dist/core/token-lifecycle.d.ts +7 -0
- package/dist/core/token-lifecycle.js +14 -0
- package/dist/core/token-lifecycle.js.map +1 -1
- package/dist/core/watcher-reauth.d.ts +18 -0
- package/dist/core/watcher-reauth.js +29 -0
- package/dist/core/watcher-reauth.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for token rejection self-healing (#490)
|
|
3
|
+
*
|
|
4
|
+
* Covers the scenario where the hub restarts with a new JWT secret, making
|
|
5
|
+
* all existing tokens invalid. Agents must detect the 401 and immediately
|
|
6
|
+
* re-register without waiting for token expiry.
|
|
7
|
+
*
|
|
8
|
+
* Tests:
|
|
9
|
+
* 1. notifyRejected() triggers re-register when token is rejected by hub
|
|
10
|
+
* 2. notifyRejected() is a no-op if refresh is already in progress (no concurrent attempts)
|
|
11
|
+
* 3. notifyRejected() is a no-op after stop()
|
|
12
|
+
* 4. Heartbeat 401 response calls notifyRejected() on lifecycle manager
|
|
13
|
+
* 5. Re-registered token is accepted by subsequent heartbeats
|
|
14
|
+
* 6. notifyRejected() → refresh endpoint also 401 → falls through to re-register
|
|
15
|
+
*/
|
|
16
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
17
|
+
import { Heartbeat } from "../core/heartbeat.js";
|
|
18
|
+
import { TokenLifecycleManager } from "../core/token-lifecycle.js";
|
|
19
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
20
|
+
/** Build a non-expiring JWT stub (exp = year 2099) */
|
|
21
|
+
function makeToken(sub = "agent-1") {
|
|
22
|
+
const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
|
|
23
|
+
const payload = Buffer.from(JSON.stringify({ sub, exp: Math.floor(new Date("2099-01-01").getTime() / 1000) })).toString("base64url");
|
|
24
|
+
return `${header}.${payload}.sig`;
|
|
25
|
+
}
|
|
26
|
+
const BOOTSTRAP = {
|
|
27
|
+
apiKey: "test-api-key",
|
|
28
|
+
agentId: "agent-1",
|
|
29
|
+
displayName: "test-agent",
|
|
30
|
+
model: "claude-sonnet-4-6",
|
|
31
|
+
workspace: "test-ws",
|
|
32
|
+
};
|
|
33
|
+
// ─── TokenLifecycleManager.notifyRejected() ──────────────────────────────────
|
|
34
|
+
describe("TokenLifecycleManager.notifyRejected()", () => {
|
|
35
|
+
it("immediately triggers re-register when token is rejected by hub", async () => {
|
|
36
|
+
const newToken = makeToken("agent-new");
|
|
37
|
+
const fetchMock = vi.fn().mockImplementation((url) => {
|
|
38
|
+
if (url.includes("/token/refresh")) {
|
|
39
|
+
// Refresh endpoint also returns 401 (hub has new secret)
|
|
40
|
+
return Promise.resolve({ ok: false, status: 401, json: async () => ({}) });
|
|
41
|
+
}
|
|
42
|
+
if (url.includes("/agents/register")) {
|
|
43
|
+
return Promise.resolve({
|
|
44
|
+
ok: true,
|
|
45
|
+
status: 200,
|
|
46
|
+
json: async () => ({ token: newToken, expires_at: "2099-01-01T00:00:00Z" }),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return Promise.resolve({ ok: true, status: 200, json: async () => ({}) });
|
|
50
|
+
});
|
|
51
|
+
const onTokenUpdate = vi.fn();
|
|
52
|
+
const manager = new TokenLifecycleManager({
|
|
53
|
+
hubUrl: "https://hub.test",
|
|
54
|
+
initialToken: makeToken(),
|
|
55
|
+
bootstrap: BOOTSTRAP,
|
|
56
|
+
onTokenUpdate,
|
|
57
|
+
fetch: fetchMock,
|
|
58
|
+
checkIntervalMs: 60_000, // long interval — should not fire during test
|
|
59
|
+
minReregisterIntervalMs: 0, // allow immediate re-register
|
|
60
|
+
});
|
|
61
|
+
manager.start();
|
|
62
|
+
manager.notifyRejected();
|
|
63
|
+
// Give the async chain time to complete
|
|
64
|
+
await vi.waitFor(() => expect(onTokenUpdate).toHaveBeenCalledWith(newToken, "2099-01-01T00:00:00Z"), {
|
|
65
|
+
timeout: 2000,
|
|
66
|
+
});
|
|
67
|
+
expect(manager.getToken()).toBe(newToken);
|
|
68
|
+
manager.stop();
|
|
69
|
+
});
|
|
70
|
+
it("does not start a concurrent refresh if one is already in progress", async () => {
|
|
71
|
+
let registerCallCount = 0;
|
|
72
|
+
let resolveRegister;
|
|
73
|
+
const registerPromise = new Promise((r) => {
|
|
74
|
+
resolveRegister = r;
|
|
75
|
+
});
|
|
76
|
+
const fetchMock = vi.fn().mockImplementation((url) => {
|
|
77
|
+
if (url.includes("/token/refresh")) {
|
|
78
|
+
return Promise.resolve({ ok: false, status: 401, json: async () => ({}) });
|
|
79
|
+
}
|
|
80
|
+
if (url.includes("/agents/register")) {
|
|
81
|
+
registerCallCount++;
|
|
82
|
+
return registerPromise.then(() => ({
|
|
83
|
+
ok: true,
|
|
84
|
+
status: 200,
|
|
85
|
+
json: async () => ({ token: makeToken("new"), expires_at: "2099-01-01T00:00:00Z" }),
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
return Promise.resolve({ ok: true, status: 200, json: async () => ({}) });
|
|
89
|
+
});
|
|
90
|
+
const manager = new TokenLifecycleManager({
|
|
91
|
+
hubUrl: "https://hub.test",
|
|
92
|
+
initialToken: makeToken(),
|
|
93
|
+
bootstrap: BOOTSTRAP,
|
|
94
|
+
onTokenUpdate: vi.fn(),
|
|
95
|
+
fetch: fetchMock,
|
|
96
|
+
checkIntervalMs: 60_000,
|
|
97
|
+
minReregisterIntervalMs: 0,
|
|
98
|
+
});
|
|
99
|
+
manager.start();
|
|
100
|
+
// Call twice rapidly
|
|
101
|
+
manager.notifyRejected();
|
|
102
|
+
manager.notifyRejected();
|
|
103
|
+
// Allow microtasks to settle
|
|
104
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
105
|
+
// Only one register attempt should have started
|
|
106
|
+
expect(registerCallCount).toBe(1);
|
|
107
|
+
resolveRegister(undefined);
|
|
108
|
+
manager.stop();
|
|
109
|
+
});
|
|
110
|
+
it("is a no-op after stop()", async () => {
|
|
111
|
+
const fetchMock = vi.fn();
|
|
112
|
+
const manager = new TokenLifecycleManager({
|
|
113
|
+
hubUrl: "https://hub.test",
|
|
114
|
+
initialToken: makeToken(),
|
|
115
|
+
bootstrap: BOOTSTRAP,
|
|
116
|
+
onTokenUpdate: vi.fn(),
|
|
117
|
+
fetch: fetchMock,
|
|
118
|
+
checkIntervalMs: 60_000,
|
|
119
|
+
minReregisterIntervalMs: 0,
|
|
120
|
+
});
|
|
121
|
+
manager.start();
|
|
122
|
+
manager.stop();
|
|
123
|
+
manager.notifyRejected();
|
|
124
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
125
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
// ─── Heartbeat → notifyRejected() integration ────────────────────────────────
|
|
129
|
+
describe("Heartbeat 401 → notifyRejected()", () => {
|
|
130
|
+
let heartbeat;
|
|
131
|
+
afterEach(() => {
|
|
132
|
+
heartbeat?.stop();
|
|
133
|
+
});
|
|
134
|
+
it("calls lifecycle notifyRejected() when heartbeat returns 401", async () => {
|
|
135
|
+
const newToken = makeToken("agent-refreshed");
|
|
136
|
+
let heartbeatCallCount = 0;
|
|
137
|
+
const fetchMock = vi.fn().mockImplementation((url) => {
|
|
138
|
+
if (url.includes("/heartbeat")) {
|
|
139
|
+
heartbeatCallCount++;
|
|
140
|
+
// First call: 401 (hub restarted). Subsequent: 200 (after re-register).
|
|
141
|
+
const status = heartbeatCallCount === 1 ? 401 : 200;
|
|
142
|
+
return Promise.resolve({ ok: status === 200, status, json: async () => ({}) });
|
|
143
|
+
}
|
|
144
|
+
if (url.includes("/token/refresh")) {
|
|
145
|
+
return Promise.resolve({ ok: false, status: 401, json: async () => ({}) });
|
|
146
|
+
}
|
|
147
|
+
if (url.includes("/agents/register")) {
|
|
148
|
+
return Promise.resolve({
|
|
149
|
+
ok: true,
|
|
150
|
+
status: 200,
|
|
151
|
+
json: async () => ({ token: newToken, expires_at: "2099-01-01T00:00:00Z" }),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return Promise.resolve({ ok: true, status: 200, json: async () => ({}) });
|
|
155
|
+
});
|
|
156
|
+
const onTokenRefresh = vi.fn();
|
|
157
|
+
const lifecycleManager = new TokenLifecycleManager({
|
|
158
|
+
hubUrl: "https://hub.test",
|
|
159
|
+
initialToken: makeToken(),
|
|
160
|
+
bootstrap: BOOTSTRAP,
|
|
161
|
+
onTokenUpdate: (token, _expiresAt) => {
|
|
162
|
+
onTokenRefresh(token);
|
|
163
|
+
},
|
|
164
|
+
fetch: fetchMock,
|
|
165
|
+
checkIntervalMs: 60_000,
|
|
166
|
+
minReregisterIntervalMs: 0,
|
|
167
|
+
});
|
|
168
|
+
heartbeat = new Heartbeat({
|
|
169
|
+
url: "https://hub.test",
|
|
170
|
+
token: makeToken(),
|
|
171
|
+
intervalMs: 60_000, // long — we'll trigger manually
|
|
172
|
+
agentName: "test-agent",
|
|
173
|
+
agentId: "agent-1",
|
|
174
|
+
apiKey: "test-api-key",
|
|
175
|
+
workspace: "test-ws",
|
|
176
|
+
onTokenRefresh,
|
|
177
|
+
lifecycleManager,
|
|
178
|
+
fetch: fetchMock,
|
|
179
|
+
});
|
|
180
|
+
heartbeat.start();
|
|
181
|
+
// Wait for re-register to complete after the 401
|
|
182
|
+
await vi.waitFor(() => expect(onTokenRefresh).toHaveBeenCalledWith(newToken), {
|
|
183
|
+
timeout: 3000,
|
|
184
|
+
});
|
|
185
|
+
expect(heartbeat.getToken()).toBe(newToken);
|
|
186
|
+
});
|
|
187
|
+
it("re-registered token survives subsequent heartbeats", async () => {
|
|
188
|
+
const newToken = makeToken("agent-reregistered");
|
|
189
|
+
let heartbeatCount = 0;
|
|
190
|
+
const fetchMock = vi.fn().mockImplementation((url) => {
|
|
191
|
+
if (url.includes("/heartbeat")) {
|
|
192
|
+
heartbeatCount++;
|
|
193
|
+
const status = heartbeatCount === 1 ? 401 : 200;
|
|
194
|
+
return Promise.resolve({ ok: status === 200, status, json: async () => ({}) });
|
|
195
|
+
}
|
|
196
|
+
if (url.includes("/token/refresh")) {
|
|
197
|
+
return Promise.resolve({ ok: false, status: 401, json: async () => ({}) });
|
|
198
|
+
}
|
|
199
|
+
if (url.includes("/agents/register")) {
|
|
200
|
+
return Promise.resolve({
|
|
201
|
+
ok: true,
|
|
202
|
+
status: 200,
|
|
203
|
+
json: async () => ({ token: newToken, expires_at: "2099-01-01T00:00:00Z" }),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return Promise.resolve({ ok: true, status: 200, json: async () => ({}) });
|
|
207
|
+
});
|
|
208
|
+
const onTokenRefresh = vi.fn();
|
|
209
|
+
const lifecycleManager = new TokenLifecycleManager({
|
|
210
|
+
hubUrl: "https://hub.test",
|
|
211
|
+
initialToken: makeToken(),
|
|
212
|
+
bootstrap: BOOTSTRAP,
|
|
213
|
+
onTokenUpdate: (token) => onTokenRefresh(token),
|
|
214
|
+
fetch: fetchMock,
|
|
215
|
+
checkIntervalMs: 60_000,
|
|
216
|
+
minReregisterIntervalMs: 0,
|
|
217
|
+
});
|
|
218
|
+
heartbeat = new Heartbeat({
|
|
219
|
+
url: "https://hub.test",
|
|
220
|
+
token: makeToken(),
|
|
221
|
+
intervalMs: 60_000,
|
|
222
|
+
agentName: "test-agent",
|
|
223
|
+
agentId: "agent-1",
|
|
224
|
+
apiKey: "test-api-key",
|
|
225
|
+
workspace: "test-ws",
|
|
226
|
+
onTokenRefresh,
|
|
227
|
+
lifecycleManager,
|
|
228
|
+
fetch: fetchMock,
|
|
229
|
+
});
|
|
230
|
+
heartbeat.start();
|
|
231
|
+
await vi.waitFor(() => expect(onTokenRefresh).toHaveBeenCalledWith(newToken), {
|
|
232
|
+
timeout: 3000,
|
|
233
|
+
});
|
|
234
|
+
// After re-register, the token in use should be the new one
|
|
235
|
+
expect(heartbeat.getToken()).toBe(newToken);
|
|
236
|
+
// New token should be used in subsequent Authorization headers
|
|
237
|
+
const registerCall = fetchMock.mock.calls.find((args) => args[0].includes("/agents/register"));
|
|
238
|
+
expect(registerCall).toBeDefined();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
//# sourceMappingURL=token-rejection-recovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-rejection-recovery.test.js","sourceRoot":"","sources":["../../src/__tests__/token-rejection-recovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { reAuthWithApiKey } from "../core/watcher-reauth.js";
|
|
3
|
+
describe("reAuthWithApiKey", () => {
|
|
4
|
+
it("returns token from successful registration response", async () => {
|
|
5
|
+
const mockFetch = async (_url, _init) => ({
|
|
6
|
+
ok: true,
|
|
7
|
+
status: 200,
|
|
8
|
+
json: async () => ({ token: "new-jwt-token-abc" }),
|
|
9
|
+
});
|
|
10
|
+
const token = await reAuthWithApiKey({
|
|
11
|
+
hubUrl: "https://hub.example.com",
|
|
12
|
+
workspace: "ws-1",
|
|
13
|
+
agentId: "agent-abc",
|
|
14
|
+
agentName: "my-agent",
|
|
15
|
+
apiKey: "secret-key",
|
|
16
|
+
model: "claude-sonnet-4-6",
|
|
17
|
+
fetch: mockFetch,
|
|
18
|
+
});
|
|
19
|
+
expect(token).toBe("new-jwt-token-abc");
|
|
20
|
+
});
|
|
21
|
+
it("sends API key in x-agentmesh-secret header", async () => {
|
|
22
|
+
let capturedHeaders = {};
|
|
23
|
+
const mockFetch = async (_url, init) => {
|
|
24
|
+
capturedHeaders = (init.headers ?? {});
|
|
25
|
+
return {
|
|
26
|
+
ok: true,
|
|
27
|
+
status: 200,
|
|
28
|
+
json: async () => ({ token: "tok" }),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
await reAuthWithApiKey({
|
|
32
|
+
hubUrl: "https://hub.example.com",
|
|
33
|
+
workspace: "ws-1",
|
|
34
|
+
agentId: "agent-abc",
|
|
35
|
+
agentName: "my-agent",
|
|
36
|
+
apiKey: "my-api-key",
|
|
37
|
+
model: "claude-sonnet-4-6",
|
|
38
|
+
fetch: mockFetch,
|
|
39
|
+
});
|
|
40
|
+
expect(capturedHeaders["x-agentmesh-secret"]).toBe("my-api-key");
|
|
41
|
+
});
|
|
42
|
+
it("sends correct request body", async () => {
|
|
43
|
+
let capturedBody = {};
|
|
44
|
+
const mockFetch = async (_url, init) => {
|
|
45
|
+
capturedBody = JSON.parse(init.body);
|
|
46
|
+
return {
|
|
47
|
+
ok: true,
|
|
48
|
+
status: 200,
|
|
49
|
+
json: async () => ({ token: "tok" }),
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
await reAuthWithApiKey({
|
|
53
|
+
hubUrl: "https://hub.example.com",
|
|
54
|
+
workspace: "ws-test",
|
|
55
|
+
agentId: "agent-xyz",
|
|
56
|
+
agentName: "watcher-agent",
|
|
57
|
+
apiKey: "key",
|
|
58
|
+
model: "claude-opus-4-6",
|
|
59
|
+
fetch: mockFetch,
|
|
60
|
+
});
|
|
61
|
+
expect(capturedBody.agent_id).toBe("agent-xyz");
|
|
62
|
+
expect(capturedBody.workspace).toBe("ws-test");
|
|
63
|
+
expect(capturedBody.display_name).toBe("watcher-agent");
|
|
64
|
+
expect(capturedBody.model).toBe("claude-opus-4-6");
|
|
65
|
+
});
|
|
66
|
+
it("falls back to agentId as display_name when agentName is undefined", async () => {
|
|
67
|
+
let capturedBody = {};
|
|
68
|
+
const mockFetch = async (_url, init) => {
|
|
69
|
+
capturedBody = JSON.parse(init.body);
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
status: 200,
|
|
73
|
+
json: async () => ({ token: "tok" }),
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
await reAuthWithApiKey({
|
|
77
|
+
hubUrl: "https://hub.example.com",
|
|
78
|
+
workspace: "ws-1",
|
|
79
|
+
agentId: "agent-fallback",
|
|
80
|
+
agentName: undefined,
|
|
81
|
+
apiKey: "key",
|
|
82
|
+
model: "claude-sonnet-4-6",
|
|
83
|
+
fetch: mockFetch,
|
|
84
|
+
});
|
|
85
|
+
expect(capturedBody.display_name).toBe("agent-fallback");
|
|
86
|
+
});
|
|
87
|
+
it("falls back to 'watcher' as display_name when both agentName and agentId are undefined", async () => {
|
|
88
|
+
let capturedBody = {};
|
|
89
|
+
const mockFetch = async (_url, init) => {
|
|
90
|
+
capturedBody = JSON.parse(init.body);
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
status: 200,
|
|
94
|
+
json: async () => ({ token: "tok" }),
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
await reAuthWithApiKey({
|
|
98
|
+
hubUrl: "https://hub.example.com",
|
|
99
|
+
workspace: "ws-1",
|
|
100
|
+
agentId: undefined,
|
|
101
|
+
agentName: undefined,
|
|
102
|
+
apiKey: "key",
|
|
103
|
+
model: "claude-sonnet-4-6",
|
|
104
|
+
fetch: mockFetch,
|
|
105
|
+
});
|
|
106
|
+
expect(capturedBody.display_name).toBe("watcher");
|
|
107
|
+
});
|
|
108
|
+
it("throws when registration response is not ok", async () => {
|
|
109
|
+
const mockFetch = async () => ({
|
|
110
|
+
ok: false,
|
|
111
|
+
status: 401,
|
|
112
|
+
json: async () => ({}),
|
|
113
|
+
});
|
|
114
|
+
await expect(reAuthWithApiKey({
|
|
115
|
+
hubUrl: "https://hub.example.com",
|
|
116
|
+
workspace: "ws-1",
|
|
117
|
+
agentId: "agent-abc",
|
|
118
|
+
agentName: "my-agent",
|
|
119
|
+
apiKey: "bad-key",
|
|
120
|
+
model: "claude-sonnet-4-6",
|
|
121
|
+
fetch: mockFetch,
|
|
122
|
+
})).rejects.toThrow("Re-register failed (401)");
|
|
123
|
+
});
|
|
124
|
+
it("posts to the correct registration endpoint", async () => {
|
|
125
|
+
let capturedUrl = "";
|
|
126
|
+
const mockFetch = async (url, _init) => {
|
|
127
|
+
capturedUrl = url;
|
|
128
|
+
return {
|
|
129
|
+
ok: true,
|
|
130
|
+
status: 200,
|
|
131
|
+
json: async () => ({ token: "tok" }),
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
await reAuthWithApiKey({
|
|
135
|
+
hubUrl: "https://hub.example.com",
|
|
136
|
+
workspace: "ws-1",
|
|
137
|
+
agentId: "agent-abc",
|
|
138
|
+
agentName: "my-agent",
|
|
139
|
+
apiKey: "key",
|
|
140
|
+
model: "claude-sonnet-4-6",
|
|
141
|
+
fetch: mockFetch,
|
|
142
|
+
});
|
|
143
|
+
expect(capturedUrl).toBe("https://hub.example.com/api/v1/agents/register");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=watcher-401-recovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher-401-recovery.test.js","sourceRoot":"","sources":["../../src/__tests__/watcher-401-recovery.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/attach.js
CHANGED
|
@@ -1,10 +1,65 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
1
4
|
import pc from "picocolors";
|
|
2
5
|
import { attachSession, getSessionName, sessionExists } from "../core/tmux.js";
|
|
6
|
+
const WATCHER_STATE_PATH = path.join(process.env.HOME || ".", ".agentmesh", "watcher-state.json");
|
|
7
|
+
function loadWatcherState() {
|
|
8
|
+
try {
|
|
9
|
+
if (!fs.existsSync(WATCHER_STATE_PATH))
|
|
10
|
+
return { watchers: [] };
|
|
11
|
+
return JSON.parse(fs.readFileSync(WATCHER_STATE_PATH, "utf-8"));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return { watchers: [] };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function tmuxSessionExists(sessionName) {
|
|
18
|
+
try {
|
|
19
|
+
execSync(`tmux has-session -t "${sessionName}" 2>/dev/null`);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
3
26
|
export function attach(name) {
|
|
4
27
|
if (!name) {
|
|
5
28
|
console.log(pc.red("Agent name is required."));
|
|
6
29
|
process.exit(1);
|
|
7
30
|
}
|
|
31
|
+
// Handle watcher sessions: `watcher-<code>` names bypass the agentmesh- prefix
|
|
32
|
+
if (name.startsWith("watcher-")) {
|
|
33
|
+
const watcherSession = name; // session is named exactly as given (e.g. "watcher-mesh")
|
|
34
|
+
if (tmuxSessionExists(watcherSession)) {
|
|
35
|
+
console.log(`Attaching to ${watcherSession}...`);
|
|
36
|
+
console.log(pc.dim("Detach with: Ctrl+B, D\n"));
|
|
37
|
+
try {
|
|
38
|
+
execSync(`tmux attach-session -t "${watcherSession}"`, { stdio: "inherit" });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// tmux attach exits non-zero when user detaches — that's expected
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Not found — check watcher-state.json for a matching entry
|
|
46
|
+
const state = loadWatcherState();
|
|
47
|
+
const entry = state.watchers.find((w) => w.sessionName === name);
|
|
48
|
+
if (entry) {
|
|
49
|
+
const stateMsg = entry.status === "stopped" ? " (stopped)" : " (not running)";
|
|
50
|
+
console.log(pc.red(`Watcher session "${name}" not found in tmux${stateMsg}.`));
|
|
51
|
+
if (entry.projectId) {
|
|
52
|
+
console.log(` Start with: ${pc.cyan(`agentmesh watcher start --project-id ${entry.projectId}`)}`);
|
|
53
|
+
console.log(` Status: ${pc.cyan(`agentmesh watcher status --project-id ${entry.projectId}`)}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log(pc.red(`Watcher session "${name}" not found. Is the watcher running?`));
|
|
58
|
+
console.log(` Start with: ${pc.cyan("agentmesh watcher start --project-id <id>")}`);
|
|
59
|
+
}
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
// Standard agent attach
|
|
8
63
|
const sessionName = getSessionName(name);
|
|
9
64
|
if (!sessionExists(sessionName)) {
|
|
10
65
|
console.log(pc.red(`Agent "${name}" is not running.`));
|
package/dist/cli/attach.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/cli/attach.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE/E,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAEzC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,IAAI,mBAAmB,CAAC,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,0BAA0B,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,WAAW,KAAK,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC,CAAC;IAEhD,aAAa,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC"}
|
|
1
|
+
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/cli/attach.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import { stop } from "./stop.js";
|
|
|
27
27
|
import { sync } from "./sync.js";
|
|
28
28
|
import { test } from "./test.js";
|
|
29
29
|
import { token } from "./token.js";
|
|
30
|
-
import { pullWatcher, startProjectWatcher, startWatcher, stopWatcher, watcherStatus, } from "./watcher.js";
|
|
30
|
+
import { projectWatcherStatus, pullWatcher, startProjectWatcher, startWatcher, stopWatcher, watcherStatus, } from "./watcher.js";
|
|
31
31
|
import { resolvePO, whoami } from "./whoami.js";
|
|
32
32
|
import { pauseWorkerAutomation, resumeWorkerAutomation } from "./worker.js";
|
|
33
33
|
const require = createRequire(import.meta.url);
|
|
@@ -278,6 +278,7 @@ watcherCommand
|
|
|
278
278
|
.option("--interval-seconds <seconds>", "Snapshot publish interval in seconds (default: 15)")
|
|
279
279
|
.option("--heartbeat-seconds <seconds>", "Lease renewal interval in seconds (default: 10)")
|
|
280
280
|
.option("--ttl-seconds <seconds>", "Lease TTL in seconds (default: 30)")
|
|
281
|
+
.option("--dev", "Dev mode: short tick interval (10s), relaxed SLA for local testing")
|
|
281
282
|
.action(async (options) => {
|
|
282
283
|
try {
|
|
283
284
|
if (options.projectId) {
|
|
@@ -286,6 +287,7 @@ watcherCommand
|
|
|
286
287
|
name: options.name,
|
|
287
288
|
foreground: options.foreground,
|
|
288
289
|
intervalSeconds: options.interval ? parseInt(options.interval, 10) : undefined,
|
|
290
|
+
dev: options.dev,
|
|
289
291
|
});
|
|
290
292
|
}
|
|
291
293
|
else if (options.teamId) {
|
|
@@ -327,21 +329,31 @@ watcherCommand
|
|
|
327
329
|
});
|
|
328
330
|
watcherCommand
|
|
329
331
|
.command("status")
|
|
330
|
-
.description("Show watcher status for a team")
|
|
331
|
-
.
|
|
332
|
+
.description("Show watcher status for a team or project")
|
|
333
|
+
.option("--team-id <teamId>", "Team ID (team-scoped watcher)")
|
|
334
|
+
.option("--project-id <projectId>", "Project ID (project-scoped watcher)")
|
|
332
335
|
.option("-n, --name <name>", "Agent name")
|
|
333
336
|
.option("--json", "Output JSON")
|
|
334
337
|
.option("--stale-after-seconds <seconds>", "Override stale threshold seconds")
|
|
335
338
|
.action(async (options) => {
|
|
336
339
|
try {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
:
|
|
344
|
-
|
|
340
|
+
if (options.projectId) {
|
|
341
|
+
projectWatcherStatus({ projectId: options.projectId });
|
|
342
|
+
}
|
|
343
|
+
else if (options.teamId) {
|
|
344
|
+
await watcherStatus({
|
|
345
|
+
teamId: options.teamId,
|
|
346
|
+
name: options.name,
|
|
347
|
+
json: options.json,
|
|
348
|
+
staleAfterSeconds: options.staleAfterSeconds
|
|
349
|
+
? parseInt(options.staleAfterSeconds, 10)
|
|
350
|
+
: undefined,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
console.error(pc.red("Either --team-id or --project-id is required."));
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
345
357
|
}
|
|
346
358
|
catch (error) {
|
|
347
359
|
console.error(pc.red(error.message));
|