@memnexus-ai/mx-agent-cli 0.1.167 → 0.1.168
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__/config-command.test.d.ts +9 -0
- package/dist/__tests__/config-command.test.d.ts.map +1 -0
- package/dist/__tests__/config-command.test.js +55 -0
- package/dist/__tests__/config-command.test.js.map +1 -0
- package/dist/__tests__/coordination-client.test.d.ts +9 -0
- package/dist/__tests__/coordination-client.test.d.ts.map +1 -0
- package/dist/__tests__/coordination-client.test.js +72 -0
- package/dist/__tests__/coordination-client.test.js.map +1 -0
- package/dist/__tests__/coordination-command.test.d.ts +9 -0
- package/dist/__tests__/coordination-command.test.d.ts.map +1 -0
- package/dist/__tests__/coordination-command.test.js +108 -0
- package/dist/__tests__/coordination-command.test.js.map +1 -0
- package/dist/__tests__/coordination-fallback.test.d.ts +9 -0
- package/dist/__tests__/coordination-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/coordination-fallback.test.js +158 -0
- package/dist/__tests__/coordination-fallback.test.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +5 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/coordination.d.ts +49 -0
- package/dist/commands/coordination.d.ts.map +1 -0
- package/dist/commands/coordination.js +128 -0
- package/dist/commands/coordination.js.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/coordination-client.d.ts +74 -0
- package/dist/lib/coordination-client.d.ts.map +1 -0
- package/dist/lib/coordination-client.js +83 -0
- package/dist/lib/coordination-client.js.map +1 -0
- package/dist/lib/coordination-fallback.d.ts +75 -0
- package/dist/lib/coordination-fallback.d.ts.map +1 -0
- package/dist/lib/coordination-fallback.js +224 -0
- package/dist/lib/coordination-fallback.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mx-agent config -- secret masking (BLOCKER-1).
|
|
3
|
+
*
|
|
4
|
+
* The coordination-service-token must be masked on set / get / list; the raw
|
|
5
|
+
* token must never appear in stdout. Uses a mocked config store so the real
|
|
6
|
+
* ~/.mx-agent/config.json is untouched.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=config-command.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-command.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-command.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mx-agent config -- secret masking (BLOCKER-1).
|
|
3
|
+
*
|
|
4
|
+
* The coordination-service-token must be masked on set / get / list; the raw
|
|
5
|
+
* token must never appear in stdout. Uses a mocked config store so the real
|
|
6
|
+
* ~/.mx-agent/config.json is untouched.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
9
|
+
const store = vi.hoisted(() => ({ data: {} }));
|
|
10
|
+
vi.mock('../lib/config.js', () => ({
|
|
11
|
+
loadConfig: () => ({ ...store.data }),
|
|
12
|
+
saveConfig: (c) => { store.data = { ...c }; },
|
|
13
|
+
getConfigValue: (k) => store.data[k],
|
|
14
|
+
setConfigValue: (k, v) => { store.data[k] = v; },
|
|
15
|
+
deleteConfigValue: (k) => { if (k in store.data) {
|
|
16
|
+
delete store.data[k];
|
|
17
|
+
return true;
|
|
18
|
+
} return false; },
|
|
19
|
+
}));
|
|
20
|
+
import { runConfig } from '../commands/config.js';
|
|
21
|
+
const SECRET = 'coord-tok-DEADBEEF-must-not-leak';
|
|
22
|
+
let logs;
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
store.data = {};
|
|
25
|
+
logs = [];
|
|
26
|
+
vi.spyOn(console, 'log').mockImplementation((m) => { logs.push(String(m)); });
|
|
27
|
+
vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
28
|
+
});
|
|
29
|
+
afterEach(() => vi.restoreAllMocks());
|
|
30
|
+
describe('config secret masking (BLOCKER-1)', () => {
|
|
31
|
+
it('masks coordination-service-token on set', () => {
|
|
32
|
+
runConfig({ subcommand: 'set', key: 'coordination-service-token', value: SECRET });
|
|
33
|
+
const out = logs.join('\n');
|
|
34
|
+
expect(out).toContain('***');
|
|
35
|
+
expect(out).not.toContain(SECRET);
|
|
36
|
+
});
|
|
37
|
+
it('masks coordination-service-token on get', () => {
|
|
38
|
+
store.data['coordination-service-token'] = SECRET;
|
|
39
|
+
runConfig({ subcommand: 'get', key: 'coordination-service-token' });
|
|
40
|
+
const out = logs.join('\n');
|
|
41
|
+
expect(out).toContain('***');
|
|
42
|
+
expect(out).not.toContain(SECRET);
|
|
43
|
+
});
|
|
44
|
+
it('masks coordination-service-token on list', () => {
|
|
45
|
+
store.data['coordination-service-token'] = SECRET;
|
|
46
|
+
store.data['coordination-service-url'] = 'https://coord.example:8443';
|
|
47
|
+
runConfig({ subcommand: 'list' });
|
|
48
|
+
const out = logs.join('\n');
|
|
49
|
+
expect(out).not.toContain(SECRET);
|
|
50
|
+
expect(out).toContain('***');
|
|
51
|
+
// Non-secret url is shown in the clear.
|
|
52
|
+
expect(out).toContain('https://coord.example:8443');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=config-command.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-command.test.js","sourceRoot":"","sources":["../../src/__tests__/config-command.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination-client -- HTTP client for the coordination service (FR-7).
|
|
3
|
+
*
|
|
4
|
+
* Uses a stubbed global fetch to assert the client builds correct requests and
|
|
5
|
+
* maps transport failures to ServiceUnreachableError and non-2xx to
|
|
6
|
+
* ServiceHttpError.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=coordination-client.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordination-client.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination-client -- HTTP client for the coordination service (FR-7).
|
|
3
|
+
*
|
|
4
|
+
* Uses a stubbed global fetch to assert the client builds correct requests and
|
|
5
|
+
* maps transport failures to ServiceUnreachableError and non-2xx to
|
|
6
|
+
* ServiceHttpError.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
9
|
+
import { HttpCoordinationClient, ServiceUnreachableError, resolveClientConfig, } from '../lib/coordination-client.js';
|
|
10
|
+
const cfg = { url: 'https://coord.example:8443', token: 'tok-abc' };
|
|
11
|
+
function stubFetch(impl) {
|
|
12
|
+
vi.stubGlobal('fetch', vi.fn((url, init) => Promise.resolve(impl(url, init))));
|
|
13
|
+
}
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.unstubAllGlobals();
|
|
16
|
+
delete process.env.MX_COORD_SERVICE_URL;
|
|
17
|
+
delete process.env.MX_COORD_SERVICE_TOKEN;
|
|
18
|
+
});
|
|
19
|
+
describe('coordination-client', () => {
|
|
20
|
+
it('enqueue posts to /enqueue with bearer auth and JSON body', async () => {
|
|
21
|
+
let seenUrl = '';
|
|
22
|
+
let seenInit = {};
|
|
23
|
+
stubFetch((url, init) => {
|
|
24
|
+
seenUrl = url;
|
|
25
|
+
seenInit = init;
|
|
26
|
+
return new Response(JSON.stringify({ messageId: 'm1', duplicate: false }), { status: 200 });
|
|
27
|
+
});
|
|
28
|
+
const client = new HttpCoordinationClient(cfg);
|
|
29
|
+
const res = await client.enqueue({ to: 'platform', priority: 'p1', body: 'hi' });
|
|
30
|
+
expect(res.messageId).toBe('m1');
|
|
31
|
+
expect(seenUrl).toBe('https://coord.example:8443/enqueue');
|
|
32
|
+
expect(seenInit.headers.authorization).toBe('Bearer tok-abc');
|
|
33
|
+
expect(JSON.parse(seenInit.body)).toMatchObject({ to: 'platform', priority: 'p1', body: 'hi' });
|
|
34
|
+
});
|
|
35
|
+
it('drain returns messages + batchId', async () => {
|
|
36
|
+
stubFetch(() => new Response(JSON.stringify({ messages: [{ id: 'm1', from: 'mcp', to: 'platform', priority: 'p1', body: 'x', timestamp: 't' }], remaining: 0, batchId: 'batch_1' }), { status: 200 }));
|
|
37
|
+
const client = new HttpCoordinationClient(cfg);
|
|
38
|
+
const res = await client.drain({ limit: 5 });
|
|
39
|
+
expect(res.messages).toHaveLength(1);
|
|
40
|
+
expect(res.batchId).toBe('batch_1');
|
|
41
|
+
});
|
|
42
|
+
it('confirmDrain posts the batchId to /drain/confirm', async () => {
|
|
43
|
+
let seenUrl = '';
|
|
44
|
+
let seenBody;
|
|
45
|
+
stubFetch((url, init) => {
|
|
46
|
+
seenUrl = url;
|
|
47
|
+
seenBody = JSON.parse(init.body);
|
|
48
|
+
return new Response(JSON.stringify({ confirmed: true }), { status: 200 });
|
|
49
|
+
});
|
|
50
|
+
const client = new HttpCoordinationClient(cfg);
|
|
51
|
+
await client.confirmDrain('batch_9');
|
|
52
|
+
expect(seenUrl).toBe('https://coord.example:8443/drain/confirm');
|
|
53
|
+
expect(seenBody).toEqual({ batchId: 'batch_9' });
|
|
54
|
+
});
|
|
55
|
+
it('maps a network failure to ServiceUnreachableError', async () => {
|
|
56
|
+
vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new Error('ECONNREFUSED'))));
|
|
57
|
+
const client = new HttpCoordinationClient(cfg);
|
|
58
|
+
await expect(client.enqueue({ to: 'platform', priority: 'p2', body: 'x' })).rejects.toBeInstanceOf(ServiceUnreachableError);
|
|
59
|
+
});
|
|
60
|
+
it('maps a non-2xx response to ServiceHttpError with the service error message', async () => {
|
|
61
|
+
stubFetch(() => new Response(JSON.stringify({ error: 'rate limit exceeded' }), { status: 429 }));
|
|
62
|
+
const client = new HttpCoordinationClient(cfg);
|
|
63
|
+
await expect(client.enqueue({ to: 'platform', priority: 'p0', body: 'x' })).rejects.toMatchObject({ name: 'ServiceHttpError', status: 429 });
|
|
64
|
+
});
|
|
65
|
+
it('resolveClientConfig reads URL + token from the environment and strips a trailing slash', () => {
|
|
66
|
+
process.env.MX_COORD_SERVICE_URL = 'https://coord.example:8443/';
|
|
67
|
+
process.env.MX_COORD_SERVICE_TOKEN = 'env-tok';
|
|
68
|
+
const resolved = resolveClientConfig();
|
|
69
|
+
expect(resolved).toEqual({ url: 'https://coord.example:8443', token: 'env-tok' });
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=coordination-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-client.test.js","sourceRoot":"","sources":["../../src/__tests__/coordination-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination commands -- FR-7 escalate/drain + FR-8 degraded mode.
|
|
3
|
+
*
|
|
4
|
+
* runEscalate writes to the local fallback when the service is unreachable
|
|
5
|
+
* (non-lossy, exit 0), sends normally when reachable, and fails cleanly on a
|
|
6
|
+
* symlinked fallback path. runDrain confirms the batch only after a full render.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=coordination-command.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-command.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordination-command.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination commands -- FR-7 escalate/drain + FR-8 degraded mode.
|
|
3
|
+
*
|
|
4
|
+
* runEscalate writes to the local fallback when the service is unreachable
|
|
5
|
+
* (non-lossy, exit 0), sends normally when reachable, and fails cleanly on a
|
|
6
|
+
* symlinked fallback path. runDrain confirms the batch only after a full render.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
|
+
import { mkdtempSync, rmSync, symlinkSync, writeFileSync, readFileSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { tmpdir } from 'os';
|
|
12
|
+
import { runEscalate, runDrain } from '../commands/coordination.js';
|
|
13
|
+
import { readFallbackEntries } from '../lib/coordination-fallback.js';
|
|
14
|
+
import { ServiceUnreachableError } from '../lib/coordination-client.js';
|
|
15
|
+
let dir;
|
|
16
|
+
let logs;
|
|
17
|
+
let errs;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
dir = mkdtempSync(join(tmpdir(), 'coord-cmd-'));
|
|
20
|
+
process.env.CLAUDE_WORKTREE_PATH = dir;
|
|
21
|
+
process.env.CLAUDE_WORKTREE_NAME = 'mcp';
|
|
22
|
+
logs = [];
|
|
23
|
+
errs = [];
|
|
24
|
+
vi.spyOn(console, 'log').mockImplementation((m) => { logs.push(String(m)); });
|
|
25
|
+
vi.spyOn(console, 'error').mockImplementation((m) => { errs.push(String(m)); });
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
vi.restoreAllMocks();
|
|
29
|
+
rmSync(dir, { recursive: true, force: true });
|
|
30
|
+
delete process.env.CLAUDE_WORKTREE_PATH;
|
|
31
|
+
delete process.env.CLAUDE_WORKTREE_NAME;
|
|
32
|
+
});
|
|
33
|
+
/** Build a client stub; override individual methods per test. */
|
|
34
|
+
function stubClient(over = {}) {
|
|
35
|
+
return {
|
|
36
|
+
async enqueue() { return { messageId: 'm1', duplicate: false }; },
|
|
37
|
+
async drain() { return { messages: [], remaining: 0, batchId: '' }; },
|
|
38
|
+
async confirmDrain() { },
|
|
39
|
+
async ack() { },
|
|
40
|
+
...over,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const unreachableClient = stubClient({
|
|
44
|
+
async enqueue() { throw new ServiceUnreachableError(new Error('down')); },
|
|
45
|
+
async drain() { throw new ServiceUnreachableError(new Error('down')); },
|
|
46
|
+
});
|
|
47
|
+
describe('runEscalate (FR-7 + FR-8)', () => {
|
|
48
|
+
it('degrades to the local fallback when the service is unreachable, non-lossy', async () => {
|
|
49
|
+
await runEscalate({ to: 'platform', priority: 'p1', body: 'help' }, { client: unreachableClient });
|
|
50
|
+
expect(logs.join('\n')).toContain('queued locally, will flush on reconnect');
|
|
51
|
+
const entries = readFallbackEntries(join(dir, '.mx-coordination-fallback.jsonl'));
|
|
52
|
+
expect(entries).toHaveLength(1);
|
|
53
|
+
expect(entries[0]).toMatchObject({ team: 'mcp', to: 'platform', priority: 'p1', body: 'help' });
|
|
54
|
+
});
|
|
55
|
+
it('sends normally when the service is reachable', async () => {
|
|
56
|
+
const sent = [];
|
|
57
|
+
const okClient = stubClient({ async enqueue(req) { sent.push(req.body); return { messageId: 'm1', duplicate: false }; } });
|
|
58
|
+
await runEscalate({ to: 'platform', priority: 'p2', body: 'live' }, { client: okClient });
|
|
59
|
+
expect(logs.join('\n')).toContain('escalated: m1');
|
|
60
|
+
expect(sent).toContain('live');
|
|
61
|
+
});
|
|
62
|
+
it('fails CLEANLY (no stack, non-zero exit) when the fallback path is a symlink (MINOR-6)', async () => {
|
|
63
|
+
const fp = join(dir, '.mx-coordination-fallback.jsonl');
|
|
64
|
+
const target = join(dir, 'evil');
|
|
65
|
+
writeFileSync(target, '');
|
|
66
|
+
symlinkSync(target, fp);
|
|
67
|
+
const exit = vi.spyOn(process, 'exit').mockImplementation((() => { throw new Error('exit'); }));
|
|
68
|
+
const errWrites = [];
|
|
69
|
+
vi.spyOn(process.stderr, 'write').mockImplementation(((c) => { errWrites.push(String(c)); return true; }));
|
|
70
|
+
await expect(runEscalate({ to: 'platform', priority: 'p1', body: 'x' }, { client: unreachableClient })).rejects.toThrow('exit');
|
|
71
|
+
expect(exit).toHaveBeenCalledWith(1);
|
|
72
|
+
expect(errWrites.join('')).toMatch(/refusing to write; escalation NOT queued/);
|
|
73
|
+
expect(readFileSync(target, 'utf8')).toBe(''); // symlink target untouched
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('runDrain (FR-7 + confirm)', () => {
|
|
77
|
+
it('prints the empty-inbox line when nothing is pending', async () => {
|
|
78
|
+
await runDrain({}, { client: stubClient() });
|
|
79
|
+
expect(logs.join('\n')).toContain('no cross-team coordination pending');
|
|
80
|
+
});
|
|
81
|
+
it('prints messages, a "more pending" hint, and confirms the batch after render (MAJOR-2)', async () => {
|
|
82
|
+
const confirmed = [];
|
|
83
|
+
const drainRes = { messages: [{ id: 'm1', from: 'retrieval', to: 'mcp', priority: 'p0', body: 'urgent', timestamp: 't' }], remaining: 4, batchId: 'b1' };
|
|
84
|
+
const client = stubClient({ async drain() { return drainRes; }, async confirmDrain(id) { confirmed.push(id); } });
|
|
85
|
+
await runDrain({ limit: '1' }, { client });
|
|
86
|
+
const out = logs.join('\n');
|
|
87
|
+
expect(out).toContain('[p0] m1 from retrieval: urgent');
|
|
88
|
+
expect(out).toContain('4 more pending');
|
|
89
|
+
expect(confirmed).toEqual(['b1']); // confirmed with the returned batchId
|
|
90
|
+
});
|
|
91
|
+
it('does NOT confirm when rendering fails mid-way (batch stays pending — safety net)', async () => {
|
|
92
|
+
const confirmed = [];
|
|
93
|
+
const drainRes = { messages: [{ id: 'm1', from: 'x', to: 'mcp', priority: 'p1', body: 'b', timestamp: 't' }], remaining: 0, batchId: 'b1' };
|
|
94
|
+
const client = stubClient({ async drain() { return drainRes; }, async confirmDrain(id) { confirmed.push(id); } });
|
|
95
|
+
// Make the render throw.
|
|
96
|
+
console.log.mockImplementation(() => { throw new Error('render failed'); });
|
|
97
|
+
await expect(runDrain({}, { client })).rejects.toThrow('render failed');
|
|
98
|
+
expect(confirmed).toEqual([]); // never confirmed
|
|
99
|
+
});
|
|
100
|
+
it('does NOT confirm when --no-confirm is set', async () => {
|
|
101
|
+
const confirmed = [];
|
|
102
|
+
const drainRes = { messages: [{ id: 'm1', from: 'x', to: 'mcp', priority: 'p2', body: 'b', timestamp: 't' }], remaining: 0, batchId: 'b1' };
|
|
103
|
+
const client = stubClient({ async drain() { return drainRes; }, async confirmDrain(id) { confirmed.push(id); } });
|
|
104
|
+
await runDrain({ noConfirm: true }, { client });
|
|
105
|
+
expect(confirmed).toEqual([]);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=coordination-command.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-command.test.js","sourceRoot":"","sources":["../../src/__tests__/coordination-command.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination-fallback -- FR-8 degraded-mode durable fallback.
|
|
3
|
+
*
|
|
4
|
+
* Covers: outage write is durable, a pre-planted symlink is rejected (O_NOFOLLOW),
|
|
5
|
+
* flush is idempotent and clears the file, an interrupted flush (service still
|
|
6
|
+
* down mid-batch) keeps remaining entries, and a stale lease is re-acquired.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=coordination-fallback.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-fallback.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordination-fallback.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* coordination-fallback -- FR-8 degraded-mode durable fallback.
|
|
3
|
+
*
|
|
4
|
+
* Covers: outage write is durable, a pre-planted symlink is rejected (O_NOFOLLOW),
|
|
5
|
+
* flush is idempotent and clears the file, an interrupted flush (service still
|
|
6
|
+
* down mid-batch) keeps remaining entries, and a stale lease is re-acquired.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
|
+
import { mkdtempSync, rmSync, existsSync, symlinkSync, writeFileSync, readFileSync, utimesSync, statSync, chmodSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { tmpdir } from 'os';
|
|
12
|
+
import { appendFallbackEntry, readFallbackEntries, flushFallback, acquireLease, releaseLease, idempotencyKeyFor, } from '../lib/coordination-fallback.js';
|
|
13
|
+
import { ServiceUnreachableError } from '../lib/coordination-client.js';
|
|
14
|
+
let dir;
|
|
15
|
+
let fp;
|
|
16
|
+
let lp;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
dir = mkdtempSync(join(tmpdir(), 'coord-fb-'));
|
|
19
|
+
fp = join(dir, '.mx-coordination-fallback.jsonl');
|
|
20
|
+
lp = join(dir, '.mx-coordination-fallback.lock');
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
rmSync(dir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
/** A client that records enqueues, optionally failing after N calls (outage). */
|
|
26
|
+
function recordingClient(failAfter = Infinity) {
|
|
27
|
+
const sent = [];
|
|
28
|
+
const client = {
|
|
29
|
+
async enqueue(req) {
|
|
30
|
+
if (sent.length >= failAfter)
|
|
31
|
+
throw new ServiceUnreachableError(new Error('down'));
|
|
32
|
+
sent.push(req);
|
|
33
|
+
return { messageId: `m${sent.length}`, duplicate: false };
|
|
34
|
+
},
|
|
35
|
+
async drain() {
|
|
36
|
+
return { messages: [], remaining: 0, batchId: '' };
|
|
37
|
+
},
|
|
38
|
+
async confirmDrain() { },
|
|
39
|
+
async ack() { },
|
|
40
|
+
};
|
|
41
|
+
return { client, sent };
|
|
42
|
+
}
|
|
43
|
+
describe('coordination-fallback', () => {
|
|
44
|
+
it('writes an outage entry durably with an idempotency key (0600)', () => {
|
|
45
|
+
const entry = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'help' }, fp);
|
|
46
|
+
expect(existsSync(fp)).toBe(true);
|
|
47
|
+
expect(entry.idempotencyKey).toBe(idempotencyKeyFor('mcp', entry.localTimestamp, 'help'));
|
|
48
|
+
const back = readFallbackEntries(fp);
|
|
49
|
+
expect(back).toHaveLength(1);
|
|
50
|
+
expect(back[0].body).toBe('help');
|
|
51
|
+
});
|
|
52
|
+
it('rejects a pre-planted symlink at the fallback path (O_NOFOLLOW, KI-COORD-SF2)', () => {
|
|
53
|
+
const target = join(dir, 'evil-target');
|
|
54
|
+
writeFileSync(target, '');
|
|
55
|
+
symlinkSync(target, fp);
|
|
56
|
+
expect(() => appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'x' }, fp)).toThrow();
|
|
57
|
+
// The symlink target was not written through.
|
|
58
|
+
expect(readFileSync(target, 'utf8')).toBe('');
|
|
59
|
+
});
|
|
60
|
+
it('flushes all entries idempotently and clears the file', async () => {
|
|
61
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
62
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'b' }, fp);
|
|
63
|
+
const { client, sent } = recordingClient();
|
|
64
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
65
|
+
expect(res).toEqual({ flushed: 2, remaining: 0, skipped: false });
|
|
66
|
+
expect(sent.map((s) => s.idempotencyKey).every(Boolean)).toBe(true);
|
|
67
|
+
expect(existsSync(fp)).toBe(false); // cleared
|
|
68
|
+
// Re-flush is a no-op (file empty) — safe re-run.
|
|
69
|
+
const again = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
70
|
+
expect(again.flushed).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
it('keeps remaining entries when an interrupted flush hits a still-down service', async () => {
|
|
73
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
74
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'b' }, fp);
|
|
75
|
+
const { client, sent } = recordingClient(1); // first succeeds, second throws unreachable
|
|
76
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
77
|
+
expect(res.flushed).toBe(1);
|
|
78
|
+
expect(res.remaining).toBe(1);
|
|
79
|
+
expect(sent).toHaveLength(1);
|
|
80
|
+
// The unsent entry is still on disk for the next flush.
|
|
81
|
+
const left = readFallbackEntries(fp);
|
|
82
|
+
expect(left).toHaveLength(1);
|
|
83
|
+
expect(left[0].body).toBe('b');
|
|
84
|
+
});
|
|
85
|
+
it('skips flushing when another flusher holds a fresh lease', async () => {
|
|
86
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
87
|
+
expect(acquireLease(lp, 30_000)).toBe(true); // simulate another holder
|
|
88
|
+
const { client, sent } = recordingClient();
|
|
89
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp });
|
|
90
|
+
expect(res.skipped).toBe(true);
|
|
91
|
+
expect(sent).toHaveLength(0);
|
|
92
|
+
releaseLease(lp);
|
|
93
|
+
});
|
|
94
|
+
it('re-acquires (steals) a stale lease', async () => {
|
|
95
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
96
|
+
acquireLease(lp, 30_000);
|
|
97
|
+
// Age the lease well past the stale window.
|
|
98
|
+
const old = new Date(Date.now() - 10 * 60 * 1000);
|
|
99
|
+
utimesSync(lp, old, old);
|
|
100
|
+
const { client, sent } = recordingClient();
|
|
101
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp, staleMs: 1_000 });
|
|
102
|
+
expect(res.skipped).toBe(false);
|
|
103
|
+
expect(res.flushed).toBe(1);
|
|
104
|
+
expect(sent).toHaveLength(1);
|
|
105
|
+
});
|
|
106
|
+
it('tightens a pre-existing 0666 fallback file to 0600 (MINOR-5)', () => {
|
|
107
|
+
writeFileSync(fp, '', { mode: 0o666 });
|
|
108
|
+
chmodSync(fp, 0o666); // ensure group/other bits despite umask
|
|
109
|
+
appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p2', body: 'x' }, fp);
|
|
110
|
+
expect(statSync(fp).mode & 0o777).toBe(0o600);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('coordination-fallback — corrupt line quarantine (MAJOR-4)', () => {
|
|
114
|
+
let cp;
|
|
115
|
+
beforeEach(() => { cp = join(dir, '.mx-coordination-fallback.corrupt.jsonl'); });
|
|
116
|
+
afterEach(() => vi.restoreAllMocks());
|
|
117
|
+
it('quarantines a corrupt line, keeps parsing the rest, and warns to stderr (0600)', () => {
|
|
118
|
+
const warn = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
119
|
+
const good = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'a' }, fp);
|
|
120
|
+
// Append a corrupt (non-JSON) line by hand.
|
|
121
|
+
writeFileSync(fp, `${JSON.stringify(good)}\n{ this is not json\n`, { mode: 0o600 });
|
|
122
|
+
const entries = readFallbackEntries(fp, cp);
|
|
123
|
+
expect(entries).toHaveLength(1);
|
|
124
|
+
expect(entries[0].body).toBe('a');
|
|
125
|
+
expect(existsSync(cp)).toBe(true);
|
|
126
|
+
expect(readFileSync(cp, 'utf8')).toContain('not json');
|
|
127
|
+
expect(statSync(cp).mode & 0o777).toBe(0o600);
|
|
128
|
+
expect((warn.mock.calls.map((c) => String(c[0])).join(''))).toMatch(/corrupt fallback entr.* quarantined/);
|
|
129
|
+
});
|
|
130
|
+
it('flushes the valid entries even when a corrupt line is present', async () => {
|
|
131
|
+
vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
132
|
+
const good = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'keepme' }, fp);
|
|
133
|
+
writeFileSync(fp, `nonsense-line\n${JSON.stringify(good)}\n`, { mode: 0o600 });
|
|
134
|
+
const { client, sent } = recordingClient();
|
|
135
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp, corruptPath: cp });
|
|
136
|
+
expect(res.flushed).toBe(1);
|
|
137
|
+
expect(sent[0].body).toBe('keepme');
|
|
138
|
+
expect(existsSync(cp)).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
it('does not follow a symlink at the quarantine path — warns inline, target untouched, valid entries still flush', async () => {
|
|
141
|
+
const warn = [];
|
|
142
|
+
vi.spyOn(process.stderr, 'write').mockImplementation((c) => { warn.push(String(c)); return true; });
|
|
143
|
+
// Pre-plant a symlink at the quarantine path pointing at a victim file.
|
|
144
|
+
const victim = join(dir, 'victim-target');
|
|
145
|
+
writeFileSync(victim, 'original');
|
|
146
|
+
symlinkSync(victim, cp);
|
|
147
|
+
const good = appendFallbackEntry({ team: 'mcp', to: 'platform', priority: 'p1', body: 'keepme' }, fp);
|
|
148
|
+
writeFileSync(fp, `not-json-corrupt\n${JSON.stringify(good)}\n`, { mode: 0o600 });
|
|
149
|
+
const { client, sent } = recordingClient();
|
|
150
|
+
const res = await flushFallback(client, { fallbackPath: fp, leasePath: lp, corruptPath: cp });
|
|
151
|
+
// Valid entry still flushed; corrupt line surfaced inline, symlink not followed.
|
|
152
|
+
expect(res.flushed).toBe(1);
|
|
153
|
+
expect(sent[0].body).toBe('keepme');
|
|
154
|
+
expect(warn.join('')).toMatch(/could not quarantine .* inline: not-json-corrupt/);
|
|
155
|
+
expect(readFileSync(victim, 'utf8')).toBe('original'); // target untouched
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
//# sourceMappingURL=coordination-fallback.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-fallback.test.js","sourceRoot":"","sources":["../../src/__tests__/coordination-fallback.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsCD,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAgFtD"}
|
package/dist/commands/config.js
CHANGED
|
@@ -15,7 +15,11 @@ const KNOWN_KEYS = {
|
|
|
15
15
|
'slack-webhook': 'Slack incoming webhook URL for pager notifications',
|
|
16
16
|
'observability-api-url': 'Observability API URL for mx-agent sync (default: http://localhost:3100)',
|
|
17
17
|
'observability-api-token': 'Bearer token for mx-agent sync authentication (required when server has auth enabled)',
|
|
18
|
+
'coordination-service-url': 'MX Agent Coordination Service base URL (https://...)',
|
|
19
|
+
'coordination-service-token': 'Per-team bearer token for the MX Agent Coordination Service',
|
|
18
20
|
};
|
|
21
|
+
/** Keys whose value is a secret and must never be echoed in the clear. */
|
|
22
|
+
const SECRET_KEYS = new Set(['observability-api-token', 'coordination-service-token']);
|
|
19
23
|
function validateKey(key) {
|
|
20
24
|
if (!KNOWN_KEYS[key]) {
|
|
21
25
|
const known = Object.keys(KNOWN_KEYS).join(', ');
|
|
@@ -37,7 +41,7 @@ function displayValue(key, value) {
|
|
|
37
41
|
// Mask sensitive values in display
|
|
38
42
|
if (key === 'slack-webhook')
|
|
39
43
|
return maskWebhookUrl(value);
|
|
40
|
-
if (key
|
|
44
|
+
if (SECRET_KEYS.has(key))
|
|
41
45
|
return '***';
|
|
42
46
|
return value;
|
|
43
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAUjG,MAAM,UAAU,GAA2B;IACzC,eAAe,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mx-agent coordination commands (FR-7) + FR-8 degraded fallback.
|
|
3
|
+
*
|
|
4
|
+
* mx-agent escalate --to <team> --priority p0..p3 --body "..."
|
|
5
|
+
* mx-agent coordination drain [--all] [--limit N] [--no-confirm]
|
|
6
|
+
* mx-agent coordination ack <messageId>
|
|
7
|
+
*
|
|
8
|
+
* These map 1:1 to the standalone service's enqueue / drain / ack operations.
|
|
9
|
+
* `escalate` degrades safely when the service is unreachable (FR-8): it writes
|
|
10
|
+
* only a local durable fallback file and flushes it on the next success.
|
|
11
|
+
*/
|
|
12
|
+
import { type CoordinationClient } from '../lib/coordination-client.js';
|
|
13
|
+
export interface EscalateOptions {
|
|
14
|
+
to: string;
|
|
15
|
+
priority: string;
|
|
16
|
+
body: string;
|
|
17
|
+
team?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* FR-7 enqueue + FR-8 degraded fallback. On a transport failure the message is
|
|
21
|
+
* written to the local durable fallback file and reported as queued-locally
|
|
22
|
+
* (exit 0, non-lossy). On success, any previously queued-locally entries are
|
|
23
|
+
* flushed first.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runEscalate(opts: EscalateOptions, deps?: {
|
|
26
|
+
client?: CoordinationClient;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
export interface DrainOptions {
|
|
29
|
+
all?: boolean;
|
|
30
|
+
limit?: string;
|
|
31
|
+
team?: string;
|
|
32
|
+
/** Skip the post-render confirm (explicit at-least-once re-inspection). */
|
|
33
|
+
noConfirm?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* FR-7 drain. `--all` returns the full backlog; `--limit N` caps it. After the
|
|
37
|
+
* messages are FULLY rendered to stdout, the batch is confirmed (MAJOR-2) so it
|
|
38
|
+
* is not redelivered. If the process dies mid-render the batch stays pending and
|
|
39
|
+
* is re-queued by the service — the designed at-least-once safety net.
|
|
40
|
+
* `--no-confirm` leaves the batch unconfirmed for deliberate re-inspection.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runDrain(opts: DrainOptions, deps?: {
|
|
43
|
+
client?: CoordinationClient;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
/** FR-7 ack. */
|
|
46
|
+
export declare function runAck(messageId: string, deps?: {
|
|
47
|
+
client?: CoordinationClient;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
//# sourceMappingURL=coordination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination.d.ts","sourceRoot":"","sources":["../../src/commands/coordination.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAIL,KAAK,kBAAkB,EAExB,MAAM,+BAA+B,CAAC;AA8CvC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiC9G;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBxG;AAED,gBAAgB;AAChB,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrG"}
|