@memnexus-ai/mx-agent-cli 0.1.207 → 0.1.209
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__/start-report-integration.test.d.ts +12 -0
- package/dist/__tests__/start-report-integration.test.d.ts.map +1 -0
- package/dist/__tests__/start-report-integration.test.js +123 -0
- package/dist/__tests__/start-report-integration.test.js.map +1 -0
- package/dist/__tests__/startup-health-render.test.d.ts +11 -0
- package/dist/__tests__/startup-health-render.test.d.ts.map +1 -0
- package/dist/__tests__/startup-health-render.test.js +60 -0
- package/dist/__tests__/startup-health-render.test.js.map +1 -0
- package/dist/__tests__/startup-report.test.d.ts +13 -0
- package/dist/__tests__/startup-report.test.d.ts.map +1 -0
- package/dist/__tests__/startup-report.test.js +278 -0
- package/dist/__tests__/startup-report.test.js.map +1 -0
- package/dist/__tests__/startup-shipper.test.d.ts +13 -0
- package/dist/__tests__/startup-shipper.test.d.ts.map +1 -0
- package/dist/__tests__/startup-shipper.test.js +149 -0
- package/dist/__tests__/startup-shipper.test.js.map +1 -0
- package/dist/commands/dashboard.d.ts +21 -0
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +72 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +50 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +17 -0
- package/dist/commands/sync.js.map +1 -1
- package/dist/lib/claude.d.ts.map +1 -1
- package/dist/lib/claude.js +15 -3
- package/dist/lib/claude.js.map +1 -1
- package/dist/lib/startup-report.d.ts +143 -0
- package/dist/lib/startup-report.d.ts.map +1 -0
- package/dist/lib/startup-report.js +389 -0
- package/dist/lib/startup-report.js.map +1 -0
- package/dist/lib/startup-shipper.d.ts +55 -0
- package/dist/lib/startup-shipper.d.ts.map +1 -0
- package/dist/lib/startup-shipper.js +177 -0
- package/dist/lib/startup-shipper.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: driving runStart to a successful hand-off writes ONE durable
|
|
3
|
+
* StartupReport JSONL line capturing the start-path output (platform v2.58 Phase
|
|
4
|
+
* 1, #4350).
|
|
5
|
+
*
|
|
6
|
+
* Every boundary is stubbed (git, network, filesystem sync, locking, launcher)
|
|
7
|
+
* so runStart reaches the `finalize('ok')` immediately before session launch.
|
|
8
|
+
* We then read the JSONL file from a temp HOME and assert the documented schema
|
|
9
|
+
* plus a non-empty entries[] captured from the start command's own output.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=start-report-integration.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-report-integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/start-report-integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: driving runStart to a successful hand-off writes ONE durable
|
|
3
|
+
* StartupReport JSONL line capturing the start-path output (platform v2.58 Phase
|
|
4
|
+
* 1, #4350).
|
|
5
|
+
*
|
|
6
|
+
* Every boundary is stubbed (git, network, filesystem sync, locking, launcher)
|
|
7
|
+
* so runStart reaches the `finalize('ok')` immediately before session launch.
|
|
8
|
+
* We then read the JSONL file from a temp HOME and assert the documented schema
|
|
9
|
+
* plus a non-empty entries[] captured from the start command's own output.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
12
|
+
import { mkdtempSync, readFileSync, rmSync } from 'fs';
|
|
13
|
+
import { tmpdir } from 'os';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
// hasOrigin=false (git remote probe returns non-zero) → skip the fetch/rebase
|
|
16
|
+
// block and land on the config-sync + launch tail deterministically.
|
|
17
|
+
vi.mock('child_process', () => ({
|
|
18
|
+
spawnSync: vi.fn(() => ({ status: 1, stdout: '', stderr: '' })),
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('../lib/worktree.js', () => ({
|
|
21
|
+
findProjectRoot: vi.fn(() => '/fake/project'),
|
|
22
|
+
findWorktreePath: vi.fn(() => '/fake/project/.worktrees/demo-wt'),
|
|
23
|
+
getSlot: vi.fn(() => 0),
|
|
24
|
+
getCurrentBranch: vi.fn(() => 'worktree/demo'),
|
|
25
|
+
}));
|
|
26
|
+
// claude.js is real for the log helpers but we don't want real config sync or a
|
|
27
|
+
// real session launch, so stub the heavy operations to no-ops. The direct
|
|
28
|
+
// console.log calls inside runStart itself still run and get captured by the tee.
|
|
29
|
+
vi.mock('../lib/claude.js', () => ({
|
|
30
|
+
syncClaudeConfig: vi.fn(),
|
|
31
|
+
deployBaseHooks: vi.fn(),
|
|
32
|
+
deployClaudeMd: vi.fn(),
|
|
33
|
+
writeClaudeLocal: vi.fn(),
|
|
34
|
+
launchClaudeSession: vi.fn(async () => { }),
|
|
35
|
+
logSuccess: vi.fn(),
|
|
36
|
+
logDim: vi.fn(),
|
|
37
|
+
logWarning: vi.fn(),
|
|
38
|
+
}));
|
|
39
|
+
vi.mock('../lib/agent-session.js', () => ({
|
|
40
|
+
launchAgentSession: vi.fn(async () => { }),
|
|
41
|
+
}));
|
|
42
|
+
vi.mock('../lib/catalog.js', () => ({
|
|
43
|
+
getTeam: vi.fn(() => undefined),
|
|
44
|
+
}));
|
|
45
|
+
vi.mock('../lib/project-config.js', () => ({
|
|
46
|
+
loadProjectConfig: vi.fn(() => ({})),
|
|
47
|
+
resolveProjectConfig: vi.fn(() => ({
|
|
48
|
+
baseBranch: 'main',
|
|
49
|
+
worktreeDir: '.worktrees',
|
|
50
|
+
teamsDir: 'mx-agent-system/teams',
|
|
51
|
+
roleguidesDir: 'mx-agent-system/roleguides',
|
|
52
|
+
configSource: 'mx-agent-system/agent-config',
|
|
53
|
+
coordinationMode: 'off',
|
|
54
|
+
})),
|
|
55
|
+
}));
|
|
56
|
+
vi.mock('../lib/config-generation.js', () => ({
|
|
57
|
+
checkConfigGeneration: vi.fn(),
|
|
58
|
+
}));
|
|
59
|
+
vi.mock('../lib/config.js', () => ({
|
|
60
|
+
getConfigValue: vi.fn(() => undefined),
|
|
61
|
+
setConfigValue: vi.fn(),
|
|
62
|
+
}));
|
|
63
|
+
vi.mock('../lib/leader-lock.js', () => ({
|
|
64
|
+
claimLock: vi.fn(() => ({ ok: true })),
|
|
65
|
+
checkLiveness: vi.fn(() => ({ state: 'stale', lockAgeSeconds: null })),
|
|
66
|
+
formatLockEvidence: vi.fn(() => ''),
|
|
67
|
+
lockPath: vi.fn(() => '/fake/project/.worktrees/demo-wt/.leader.lock'),
|
|
68
|
+
readLock: vi.fn(() => ({ lock: null, parseError: null })),
|
|
69
|
+
removeLock: vi.fn(),
|
|
70
|
+
writeLock: vi.fn(),
|
|
71
|
+
updateChildPid: vi.fn(),
|
|
72
|
+
}));
|
|
73
|
+
vi.mock('../lib/coordination-token.js', () => ({
|
|
74
|
+
resolveCoordinationToken: vi.fn(() => ({ status: 'not-on-roster' })),
|
|
75
|
+
}));
|
|
76
|
+
import { runStart } from '../commands/start.js';
|
|
77
|
+
import { startupReport } from '../lib/startup-report.js';
|
|
78
|
+
let home;
|
|
79
|
+
const OLD_HOME = process.env.HOME;
|
|
80
|
+
let logSpy;
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
home = mkdtempSync(join(tmpdir(), 'mx-startlog-int-'));
|
|
83
|
+
// Set HOME as a property so Node's env proxy stays intact (os.homedir()
|
|
84
|
+
// reads it); a wholesale `process.env = {...}` would break that.
|
|
85
|
+
process.env.HOME = home;
|
|
86
|
+
startupReport._reset();
|
|
87
|
+
vi.clearAllMocks();
|
|
88
|
+
// Neutralize the npm version check network call.
|
|
89
|
+
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('no network in test')));
|
|
90
|
+
logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
91
|
+
});
|
|
92
|
+
afterEach(() => {
|
|
93
|
+
logSpy.mockRestore();
|
|
94
|
+
vi.unstubAllGlobals();
|
|
95
|
+
startupReport._reset();
|
|
96
|
+
rmSync(home, { recursive: true, force: true });
|
|
97
|
+
if (OLD_HOME === undefined)
|
|
98
|
+
delete process.env.HOME;
|
|
99
|
+
else
|
|
100
|
+
process.env.HOME = OLD_HOME;
|
|
101
|
+
});
|
|
102
|
+
describe('runStart → durable StartupReport', () => {
|
|
103
|
+
it('writes one JSONL report with schema_version, outcome ok, and entries[]', async () => {
|
|
104
|
+
await runStart({ name: 'demo' });
|
|
105
|
+
const file = join(home, '.mx-agent', 'logs', 'startup', 'demo-wt.jsonl');
|
|
106
|
+
const lines = readFileSync(file, 'utf-8').trim().split('\n');
|
|
107
|
+
expect(lines).toHaveLength(1);
|
|
108
|
+
const report = JSON.parse(lines[0]);
|
|
109
|
+
expect(report.schema_version).toBe(1);
|
|
110
|
+
expect(report.outcome).toBe('ok');
|
|
111
|
+
expect(report.team).toBe('demo');
|
|
112
|
+
expect(report.worktree).toBe('demo-wt');
|
|
113
|
+
expect(report.project).toBe('project'); // basename('/fake/project')
|
|
114
|
+
expect(typeof report.cli_version).toBe('string');
|
|
115
|
+
expect(Array.isArray(report.entries)).toBe(true);
|
|
116
|
+
expect(report.entries.length).toBeGreaterThan(0);
|
|
117
|
+
// The banner line the command prints is captured verbatim (ANSI stripped).
|
|
118
|
+
const messages = report.entries.map((e) => e.message).join('\n');
|
|
119
|
+
expect(messages).toContain('Starting Agent Team');
|
|
120
|
+
expect(messages).toContain('Team:');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=start-report-integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-report-integration.test.js","sourceRoot":"","sources":["../../src/__tests__/start-report-integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CWE-150 render-layer test (#4350 security review).
|
|
3
|
+
*
|
|
4
|
+
* renderStartupHealth() prints fleet-supplied fields (team, latest_outcome,
|
|
5
|
+
* warning messages) to the operator's terminal. Any worktree can POST crafted
|
|
6
|
+
* control sequences straight to the API with the shared token, so the render
|
|
7
|
+
* layer must strip them before printing. This asserts the rendered output
|
|
8
|
+
* contains none of the injected sequences.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=startup-health-render.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-health-render.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/startup-health-render.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CWE-150 render-layer test (#4350 security review).
|
|
3
|
+
*
|
|
4
|
+
* renderStartupHealth() prints fleet-supplied fields (team, latest_outcome,
|
|
5
|
+
* warning messages) to the operator's terminal. Any worktree can POST crafted
|
|
6
|
+
* control sequences straight to the API with the shared token, so the render
|
|
7
|
+
* layer must strip them before printing. This asserts the rendered output
|
|
8
|
+
* contains none of the injected sequences.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { renderStartupHealth } from '../commands/dashboard.js';
|
|
13
|
+
const ESC = String.fromCharCode(27); // \x1b
|
|
14
|
+
const BEL = String.fromCharCode(7); // \x07
|
|
15
|
+
const SCREEN_CLEAR = ESC + '[2J';
|
|
16
|
+
const OSC_TITLE = ESC + ']0;OWNED' + BEL;
|
|
17
|
+
let logged;
|
|
18
|
+
const origLevel = chalk.level;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
// Disable chalk styling so the only escape sequences that could appear in the
|
|
21
|
+
// output are ones that leaked through from the (hostile) input fields.
|
|
22
|
+
chalk.level = 0;
|
|
23
|
+
logged = '';
|
|
24
|
+
vi.spyOn(console, 'log').mockImplementation((...args) => {
|
|
25
|
+
logged += args.map((a) => String(a)).join(' ') + '\n';
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
vi.restoreAllMocks();
|
|
30
|
+
chalk.level = origLevel;
|
|
31
|
+
});
|
|
32
|
+
describe('renderStartupHealth — control-sequence stripping', () => {
|
|
33
|
+
it('renders team, outcome, and warning message with the injected sequences absent', () => {
|
|
34
|
+
renderStartupHealth({
|
|
35
|
+
window_days: 7,
|
|
36
|
+
generated_at: new Date().toISOString(),
|
|
37
|
+
teams: [
|
|
38
|
+
{
|
|
39
|
+
team: `evil${SCREEN_CLEAR}team`,
|
|
40
|
+
latest_outcome: `ok${OSC_TITLE}`,
|
|
41
|
+
latest_started_at: new Date().toISOString(),
|
|
42
|
+
latest_warning_count: 1,
|
|
43
|
+
latest_error_count: 0,
|
|
44
|
+
report_count: 1,
|
|
45
|
+
total_warning_count: 1,
|
|
46
|
+
total_error_count: 0,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
top_warnings: [{ message: `boom${SCREEN_CLEAR}${OSC_TITLE}bar`, count: 3 }],
|
|
50
|
+
});
|
|
51
|
+
// No ESC byte, no screen-clear, no OSC payload anywhere in the printed output.
|
|
52
|
+
expect(logged.includes(ESC)).toBe(false);
|
|
53
|
+
expect(logged.includes('[2J')).toBe(false);
|
|
54
|
+
expect(logged.includes('OWNED')).toBe(false);
|
|
55
|
+
// The benign parts of each field still render.
|
|
56
|
+
expect(logged).toContain('evilteam');
|
|
57
|
+
expect(logged).toContain('boombar');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=startup-health-render.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-health-render.test.js","sourceRoot":"","sources":["../../src/__tests__/startup-health-render.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO;AAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO;AAC3C,MAAM,YAAY,GAAG,GAAG,GAAG,KAAK,CAAC;AACjC,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,GAAG,GAAG,CAAC;AAEzC,IAAI,MAAc,CAAC;AACnB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC;AAE9B,UAAU,CAAC,GAAG,EAAE;IACd,8EAA8E;IAC9E,uEAAuE;IACvE,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;IAChB,MAAM,GAAG,EAAE,CAAC;IACZ,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,CAAC,GAAG,IAAe,EAAE,EAAE;QACjE,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,eAAe,EAAE,CAAC;IACrB,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAChE,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,mBAAmB,CAAC;YAClB,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACtC,KAAK,EAAE;gBACL;oBACE,IAAI,EAAE,OAAO,YAAY,MAAM;oBAC/B,cAAc,EAAE,KAAK,SAAS,EAAE;oBAChC,iBAAiB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBAC3C,oBAAoB,EAAE,CAAC;oBACvB,kBAAkB,EAAE,CAAC;oBACrB,YAAY,EAAE,CAAC;oBACf,mBAAmB,EAAE,CAAC;oBACtB,iBAAiB,EAAE,CAAC;iBACrB;aACF;YACD,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,YAAY,GAAG,SAAS,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;SAC5E,CAAC,CAAC;QAEH,+EAA+E;QAC/E,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE7C,+CAA+C;QAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the durable startup-log collector (platform v2.58 Phase 1, #4350).
|
|
3
|
+
*
|
|
4
|
+
* Covers the module's contract:
|
|
5
|
+
* - entry recording (level + optional code)
|
|
6
|
+
* - finalize writes one JSON line with the documented schema; idempotent
|
|
7
|
+
* - two-generation rotation at the 5 MB threshold
|
|
8
|
+
* - finalize NEVER throws, even when the log directory is unwritable
|
|
9
|
+
* - level inference from glyphs and ANSI colors
|
|
10
|
+
* - the console-capture tee records every print without altering output
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=startup-report.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-report.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/startup-report.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the durable startup-log collector (platform v2.58 Phase 1, #4350).
|
|
3
|
+
*
|
|
4
|
+
* Covers the module's contract:
|
|
5
|
+
* - entry recording (level + optional code)
|
|
6
|
+
* - finalize writes one JSON line with the documented schema; idempotent
|
|
7
|
+
* - two-generation rotation at the 5 MB threshold
|
|
8
|
+
* - finalize NEVER throws, even when the log directory is unwritable
|
|
9
|
+
* - level inference from glyphs and ANSI colors
|
|
10
|
+
* - the console-capture tee records every print without altering output
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
13
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, readdirSync, existsSync, rmSync, chmodSync } from 'fs';
|
|
14
|
+
import { tmpdir } from 'os';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { startupReport, inferLevel, stripAnsi, redactUrlCredentials } from '../lib/startup-report.js';
|
|
17
|
+
let home;
|
|
18
|
+
const OLD_HOME = process.env.HOME;
|
|
19
|
+
/** Path where finalize writes for a given worktree/team name under the temp HOME. */
|
|
20
|
+
function logPath(name) {
|
|
21
|
+
return join(home, '.mx-agent', 'logs', 'startup', `${name}.jsonl`);
|
|
22
|
+
}
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
home = mkdtempSync(join(tmpdir(), 'mx-startlog-home-'));
|
|
25
|
+
// Set HOME as a property (keeps Node's env proxy → os.homedir() sees it).
|
|
26
|
+
// A wholesale `process.env = {...}` would drop the proxy and break homedir().
|
|
27
|
+
process.env.HOME = home; // os.homedir() honors $HOME on POSIX
|
|
28
|
+
startupReport._reset();
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
startupReport._reset();
|
|
32
|
+
rmSync(home, { recursive: true, force: true });
|
|
33
|
+
if (OLD_HOME === undefined)
|
|
34
|
+
delete process.env.HOME;
|
|
35
|
+
else
|
|
36
|
+
process.env.HOME = OLD_HOME;
|
|
37
|
+
});
|
|
38
|
+
describe('stripAnsi', () => {
|
|
39
|
+
it('removes SGR color/style escapes', () => {
|
|
40
|
+
expect(stripAnsi('\x1b[32m✓ ok\x1b[39m')).toBe('✓ ok');
|
|
41
|
+
expect(stripAnsi('plain')).toBe('plain');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('inferLevel', () => {
|
|
45
|
+
it('reads glyph markers first (survives colors off)', () => {
|
|
46
|
+
expect(inferLevel(' ✓ Deployed AGENTS.md', 'out')).toBe('success');
|
|
47
|
+
expect(inferLevel(' ⚠ Could not fetch', 'out')).toBe('warning');
|
|
48
|
+
expect(inferLevel(' ✗ Refusing to start', 'out')).toBe('error');
|
|
49
|
+
});
|
|
50
|
+
it('falls back to ANSI color when no glyph', () => {
|
|
51
|
+
expect(inferLevel('\x1b[31mError: boom\x1b[39m', 'out')).toBe('error');
|
|
52
|
+
expect(inferLevel('\x1b[33mheads up\x1b[39m', 'out')).toBe('warning');
|
|
53
|
+
expect(inferLevel('\x1b[32mgreen\x1b[39m', 'out')).toBe('success');
|
|
54
|
+
expect(inferLevel('\x1b[2mdim line\x1b[22m', 'out')).toBe('dim');
|
|
55
|
+
expect(inferLevel('\x1b[90mgray\x1b[39m', 'out')).toBe('dim');
|
|
56
|
+
});
|
|
57
|
+
it('biases by stream when plain text has no marker', () => {
|
|
58
|
+
expect(inferLevel('Team: demo', 'out')).toBe('info');
|
|
59
|
+
expect(inferLevel('[mx-agent] skipping behavioral content', 'err')).toBe('warning');
|
|
60
|
+
expect(inferLevel('Error: not found', 'error-console')).toBe('error');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('URL credential redaction', () => {
|
|
64
|
+
it('strips userinfo from a credentialed https remote', () => {
|
|
65
|
+
expect(redactUrlCredentials('https://x-access-token:SECRET@github.com/org/repo.git'))
|
|
66
|
+
.toBe('https://***@github.com/org/repo.git');
|
|
67
|
+
});
|
|
68
|
+
it('leaves a plain https URL unchanged', () => {
|
|
69
|
+
expect(redactUrlCredentials('https://github.com/org/repo.git'))
|
|
70
|
+
.toBe('https://github.com/org/repo.git');
|
|
71
|
+
});
|
|
72
|
+
it('does NOT mangle an ssh-style git@github.com: remote (no scheme://)', () => {
|
|
73
|
+
expect(redactUrlCredentials('git@github.com:org/repo.git'))
|
|
74
|
+
.toBe('git@github.com:org/repo.git');
|
|
75
|
+
});
|
|
76
|
+
it('record() persists the scrubbed message', () => {
|
|
77
|
+
startupReport.record('warning', 'fatal: could not read from https://user:pw@example.com/x.git');
|
|
78
|
+
const entries = startupReport._entries();
|
|
79
|
+
expect(entries[0].message).toBe('fatal: could not read from https://***@example.com/x.git');
|
|
80
|
+
expect(entries[0].message).not.toContain('pw');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('record + finalize', () => {
|
|
84
|
+
it('buffers entries with level and optional code', () => {
|
|
85
|
+
startupReport.record('info', 'starting');
|
|
86
|
+
startupReport.record('warning', 'heads up', 'obs-token-missing');
|
|
87
|
+
const entries = startupReport._entries();
|
|
88
|
+
expect(entries).toHaveLength(2);
|
|
89
|
+
expect(entries[0]).toMatchObject({ level: 'info', message: 'starting' });
|
|
90
|
+
expect(entries[0].code).toBeUndefined();
|
|
91
|
+
expect(entries[1]).toMatchObject({ level: 'warning', message: 'heads up', code: 'obs-token-missing' });
|
|
92
|
+
expect(typeof entries[0].ts).toBe('string');
|
|
93
|
+
});
|
|
94
|
+
it('writes one JSON line with the documented schema', () => {
|
|
95
|
+
startupReport.setContext({
|
|
96
|
+
project: 'memnexus',
|
|
97
|
+
team: 'demo',
|
|
98
|
+
worktree: 'demo-20260721',
|
|
99
|
+
cliVersion: '9.9.9',
|
|
100
|
+
machine: 'test-host',
|
|
101
|
+
});
|
|
102
|
+
startupReport.record('info', 'line one');
|
|
103
|
+
startupReport.record('success', 'line two');
|
|
104
|
+
startupReport.finalize('ok');
|
|
105
|
+
const raw = readFileSync(logPath('demo-20260721'), 'utf-8').trim().split('\n');
|
|
106
|
+
expect(raw).toHaveLength(1);
|
|
107
|
+
const report = JSON.parse(raw[0]);
|
|
108
|
+
expect(report.schema_version).toBe(1);
|
|
109
|
+
expect(report.project).toBe('memnexus');
|
|
110
|
+
expect(report.team).toBe('demo');
|
|
111
|
+
expect(report.worktree).toBe('demo-20260721');
|
|
112
|
+
expect(report.cli_version).toBe('9.9.9');
|
|
113
|
+
expect(report.machine).toBe('test-host');
|
|
114
|
+
expect(report.outcome).toBe('ok');
|
|
115
|
+
expect(typeof report.started_at).toBe('string');
|
|
116
|
+
expect(typeof report.finished_at).toBe('string');
|
|
117
|
+
expect(report.entries).toHaveLength(2);
|
|
118
|
+
expect(report.entries[1]).toMatchObject({ level: 'success', message: 'line two' });
|
|
119
|
+
});
|
|
120
|
+
it('is idempotent — a second finalize is a no-op (single line total)', () => {
|
|
121
|
+
startupReport.setContext({ team: 'demo' });
|
|
122
|
+
startupReport.record('info', 'once');
|
|
123
|
+
startupReport.finalize('ok');
|
|
124
|
+
startupReport.finalize('error'); // must be ignored
|
|
125
|
+
const raw = readFileSync(logPath('demo'), 'utf-8').trim().split('\n');
|
|
126
|
+
expect(raw).toHaveLength(1);
|
|
127
|
+
expect(JSON.parse(raw[0]).outcome).toBe('ok');
|
|
128
|
+
});
|
|
129
|
+
it('persists messages with terminal control sequences stripped (CWE-150)', () => {
|
|
130
|
+
const ESC = String.fromCharCode(27);
|
|
131
|
+
const BEL = String.fromCharCode(7);
|
|
132
|
+
startupReport.setContext({ worktree: 'ctrl-wt' });
|
|
133
|
+
startupReport.record('warning', `boom${ESC}[2J${ESC}]0;OWNED${BEL}bar`);
|
|
134
|
+
startupReport.finalize('ok');
|
|
135
|
+
const raw = readFileSync(logPath('ctrl-wt'), 'utf-8').trim();
|
|
136
|
+
// The whole persisted line must be free of ESC, screen-clear, and OSC payload.
|
|
137
|
+
expect(raw.includes(ESC)).toBe(false);
|
|
138
|
+
expect(raw.includes('[2J')).toBe(false);
|
|
139
|
+
expect(raw.includes('OWNED')).toBe(false);
|
|
140
|
+
const report = JSON.parse(raw);
|
|
141
|
+
expect(report.entries[0].message).toBe('boombar');
|
|
142
|
+
});
|
|
143
|
+
it('spools a copy of the report for central shipping (#4350 Phase 2)', () => {
|
|
144
|
+
startupReport.setContext({ worktree: 'ship-wt', team: 'shipteam' });
|
|
145
|
+
startupReport.record('warning', 'w');
|
|
146
|
+
startupReport.finalize('ok');
|
|
147
|
+
const spool = join(home, '.mx-agent', 'logs', 'startup', 'spool');
|
|
148
|
+
const files = readdirSync(spool).filter((f) => f.endsWith('.json'));
|
|
149
|
+
expect(files).toHaveLength(1);
|
|
150
|
+
const spooled = JSON.parse(readFileSync(join(spool, files[0]), 'utf-8'));
|
|
151
|
+
expect(spooled.worktree).toBe('ship-wt');
|
|
152
|
+
expect(spooled.outcome).toBe('ok');
|
|
153
|
+
});
|
|
154
|
+
it('fires the ship hook once on finalize (fire-and-forget)', () => {
|
|
155
|
+
const hook = vi.fn();
|
|
156
|
+
startupReport.setShipHook(hook);
|
|
157
|
+
startupReport.setContext({ worktree: 'hook-wt' });
|
|
158
|
+
startupReport.finalize('ok');
|
|
159
|
+
startupReport.finalize('error'); // idempotent — hook must not fire twice
|
|
160
|
+
expect(hook).toHaveBeenCalledTimes(1);
|
|
161
|
+
});
|
|
162
|
+
it('falls back to team, then "unknown", for the file name', () => {
|
|
163
|
+
startupReport.setContext({ team: 'teamx' }); // no worktree
|
|
164
|
+
startupReport.record('info', 'x');
|
|
165
|
+
startupReport.finalize('refused');
|
|
166
|
+
expect(existsSync(logPath('teamx'))).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
it('appends across invocations (multiple JSON lines accumulate)', () => {
|
|
169
|
+
startupReport.setContext({ worktree: 'wt' });
|
|
170
|
+
startupReport.record('info', 'first');
|
|
171
|
+
startupReport.finalize('ok');
|
|
172
|
+
startupReport._reset();
|
|
173
|
+
startupReport.setContext({ worktree: 'wt' });
|
|
174
|
+
startupReport.record('info', 'second');
|
|
175
|
+
startupReport.finalize('refused');
|
|
176
|
+
const lines = readFileSync(logPath('wt'), 'utf-8').trim().split('\n');
|
|
177
|
+
expect(lines).toHaveLength(2);
|
|
178
|
+
expect(JSON.parse(lines[0]).outcome).toBe('ok');
|
|
179
|
+
expect(JSON.parse(lines[1]).outcome).toBe('refused');
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
describe('rotation at the 5 MB threshold', () => {
|
|
183
|
+
it('renames the current file to .1 before appending when over 5 MB', () => {
|
|
184
|
+
const dir = join(home, '.mx-agent', 'logs', 'startup');
|
|
185
|
+
mkdirSync(dir, { recursive: true });
|
|
186
|
+
const target = logPath('big');
|
|
187
|
+
// Pre-seed a file just over 5 MB.
|
|
188
|
+
writeFileSync(target, 'x'.repeat(5 * 1024 * 1024 + 10));
|
|
189
|
+
startupReport.setContext({ worktree: 'big' });
|
|
190
|
+
startupReport.record('info', 'after rotation');
|
|
191
|
+
startupReport.finalize('ok');
|
|
192
|
+
expect(existsSync(`${target}.1`)).toBe(true);
|
|
193
|
+
// The new primary file holds exactly the one fresh report line.
|
|
194
|
+
const lines = readFileSync(target, 'utf-8').trim().split('\n');
|
|
195
|
+
expect(lines).toHaveLength(1);
|
|
196
|
+
expect(JSON.parse(lines[0]).entries[0].message).toBe('after rotation');
|
|
197
|
+
});
|
|
198
|
+
it('does not rotate a small file', () => {
|
|
199
|
+
startupReport.setContext({ worktree: 'small' });
|
|
200
|
+
startupReport.record('info', 'a');
|
|
201
|
+
startupReport.finalize('ok');
|
|
202
|
+
expect(existsSync(`${logPath('small')}.1`)).toBe(false);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('never throws on unwritable log dir', () => {
|
|
206
|
+
it('finalize swallows I/O errors when the home dir is read-only', () => {
|
|
207
|
+
// Make HOME read-only so mkdirSync(~/.mx-agent/...) fails with EACCES.
|
|
208
|
+
// (Skipped implicitly when running as root, where perms are ignored — the
|
|
209
|
+
// assertion below still holds because finalize never throws regardless.)
|
|
210
|
+
chmodSync(home, 0o555);
|
|
211
|
+
startupReport.setContext({ worktree: 'ro' });
|
|
212
|
+
startupReport.record('error', 'boom');
|
|
213
|
+
expect(() => startupReport.finalize('error')).not.toThrow();
|
|
214
|
+
// Restore perms so afterEach cleanup can remove the dir.
|
|
215
|
+
chmodSync(home, 0o755);
|
|
216
|
+
});
|
|
217
|
+
it('record never throws', () => {
|
|
218
|
+
expect(() => startupReport.record('info', 'ok')).not.toThrow();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe('console-capture tee', () => {
|
|
222
|
+
it('records every print with an inferred level and still emits output', () => {
|
|
223
|
+
startupReport.setContext({ worktree: 'tee' });
|
|
224
|
+
// Spy the REAL console before installing the tee so we can prove output
|
|
225
|
+
// still reaches the terminal exactly once per call.
|
|
226
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
227
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
228
|
+
startupReport.installConsoleCapture();
|
|
229
|
+
console.log('\x1b[32m ✓ Deployed\x1b[39m');
|
|
230
|
+
console.log(' Team: demo'); // plain info
|
|
231
|
+
console.log('\x1b[33m ⚠ heads up\x1b[39m');
|
|
232
|
+
console.log(' '); // whitespace-only — must NOT be recorded
|
|
233
|
+
console.error('\x1b[31mError: nope\x1b[39m');
|
|
234
|
+
process.stderr.write('[mx-agent] skipping behavioral content\n');
|
|
235
|
+
startupReport.removeConsoleCapture();
|
|
236
|
+
const entries = startupReport._entries();
|
|
237
|
+
const levels = entries.map((e) => e.level);
|
|
238
|
+
expect(levels).toEqual(['success', 'info', 'warning', 'error', 'warning']);
|
|
239
|
+
expect(entries[0].message).toBe(' ✓ Deployed');
|
|
240
|
+
expect(entries.find((e) => e.message.includes('skipping behavioral'))).toBeTruthy();
|
|
241
|
+
// Output still went out: 4 console.log calls (incl. the whitespace one).
|
|
242
|
+
expect(logSpy).toHaveBeenCalledTimes(4);
|
|
243
|
+
expect(errSpy).toHaveBeenCalledTimes(1);
|
|
244
|
+
logSpy.mockRestore();
|
|
245
|
+
errSpy.mockRestore();
|
|
246
|
+
});
|
|
247
|
+
it('records a REAL console.error exactly ONCE (no double via stderr chain)', () => {
|
|
248
|
+
// Do NOT mock console.error — the native impl dispatches through the teed
|
|
249
|
+
// process.stderr.write, which is exactly the double-count path under test.
|
|
250
|
+
// Redirect the raw stderr fd write to a sink so the line does not clutter
|
|
251
|
+
// test output, but leave console.error / process.stderr.write real so the
|
|
252
|
+
// tee sees the genuine dispatch chain.
|
|
253
|
+
startupReport.setContext({ worktree: 'realerr' });
|
|
254
|
+
const realStderrWrite = process.stderr.write.bind(process.stderr);
|
|
255
|
+
const sink = vi.fn(() => true);
|
|
256
|
+
process.stderr.write = sink;
|
|
257
|
+
try {
|
|
258
|
+
startupReport.installConsoleCapture();
|
|
259
|
+
console.error('\x1b[31mTeam \'bogus\' not found.\x1b[39m');
|
|
260
|
+
startupReport.removeConsoleCapture();
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
process.stderr.write = realStderrWrite;
|
|
264
|
+
}
|
|
265
|
+
const entries = startupReport._entries();
|
|
266
|
+
expect(entries).toHaveLength(1);
|
|
267
|
+
expect(entries[0].level).toBe('error');
|
|
268
|
+
expect(entries[0].message).toBe("Team 'bogus' not found.");
|
|
269
|
+
});
|
|
270
|
+
it('removeConsoleCapture restores the original console.log', () => {
|
|
271
|
+
const before = console.log;
|
|
272
|
+
startupReport.installConsoleCapture();
|
|
273
|
+
expect(console.log).not.toBe(before);
|
|
274
|
+
startupReport.removeConsoleCapture();
|
|
275
|
+
expect(console.log).toBe(before);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
//# sourceMappingURL=startup-report.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-report.test.js","sourceRoot":"","sources":["../../src/__tests__/startup-report.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the central StartupReport shipper (platform v2.58 Phase 2, #4350).
|
|
3
|
+
*
|
|
4
|
+
* Covers the spool lifecycle and never-throw discipline:
|
|
5
|
+
* - spoolReport writes one file; flush POSTs and deletes on 2xx
|
|
6
|
+
* - POST failure (non-2xx / network) leaves the file spooled
|
|
7
|
+
* - no token → flush skips the POST and leaves everything spooled
|
|
8
|
+
* - 50-file cap drops the oldest beyond 50
|
|
9
|
+
* - 14-day age drop happens at spool-write time
|
|
10
|
+
* - spoolReport never throws under a read-only spool dir
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=startup-shipper.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-shipper.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/startup-shipper.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|