Files
NetMesh/domain/codingCliOutputDetect.test.ts
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

179 lines
6.6 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
createCodingCliOutputScanner,
inferCodingCliProviderFromOutput,
stripTerminalControlSequences,
} from './codingCliOutputDetect';
test('inferCodingCliProviderFromOutput detects Codex startup banner', () => {
assert.equal(
inferCodingCliProviderFromOutput('>_ OpenAI Codex (v0.141.0)\r\nmodel: gpt-5.5'),
'codex',
);
assert.equal(
inferCodingCliProviderFromOutput('OpenAI Codex (v0.141.0)'),
'codex',
);
});
test('inferCodingCliProviderFromOutput detects other CLI banners', () => {
assert.equal(inferCodingCliProviderFromOutput('Welcome to Claude Code'), 'claude');
assert.equal(inferCodingCliProviderFromOutput('GitHub Copilot CLI'), 'copilot');
assert.equal(inferCodingCliProviderFromOutput('Factory Droid ready'), 'droid');
assert.equal(
inferCodingCliProviderFromOutput(
'█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█\n'
+ '█ █ █ █ █▀▀▀ █ █ █ █ █ █ █ █▀▀▀\n'
+ '▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀',
),
'opencode',
);
});
test('inferCodingCliProviderFromOutput ignores coding CLI installer and package output', () => {
assert.equal(
inferCodingCliProviderFromOutput('Setting up Claude Code...\n✅ Installation complete!'),
undefined,
);
assert.equal(
inferCodingCliProviderFromOutput(
'npm update codex\nchanged 3 packages in 2s\nopencode@1.2.3\n├── opencode@1.2.3',
),
undefined,
);
assert.equal(
inferCodingCliProviderFromOutput(
'\x1b[90mOpenCode includes free models, to start:\x1b[0m\n'
+ 'cd <project> # Open directory\n'
+ 'opencode # Run command\n'
+ 'For more information visit https://opencode.ai/docs',
),
undefined,
);
assert.equal(
inferCodingCliProviderFromOutput('updated opencode-ai@1.2.3'),
undefined,
);
});
test('createCodingCliOutputScanner ignores split installer output', () => {
const scanner = createCodingCliOutputScanner();
assert.equal(scanner.feed('\x1b[90mSetting up Claude '), undefined);
assert.equal(scanner.feed('Code...\x1b[0m\n✅ Installation complete!'), undefined);
assert.equal(
scanner.feed(
'\n\x1b[90m█▀▀█ █▀▀█ █▀▀█ █▀▀▄ \x1b[0m█▀▀▀ █▀▀█ █▀▀█ █▀▀█\n'
+ '\x1b[90m█░░█ █░░█ █▀▀▀ █░░█ \x1b[0m█░░░ █░░█ █░░█ █▀▀▀\n',
),
undefined,
);
assert.equal(
scanner.feed(
'\x1b[90m▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ \x1b[0m▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀\n'
+ '\x1b[90mOpenCode includes free models, ',
),
undefined,
);
assert.equal(scanner.feed('to start:\nopencode # Run command'), undefined);
});
test('createCodingCliOutputScanner detects the ANSI-colored OpenCode TUI logo across chunks', () => {
const scanner = createCodingCliOutputScanner();
assert.equal(
scanner.feed('\x1b[36m█▀▀█ █▀▀█ █▀▀█ █▀▀▄\x1b[0m █▀▀▀ █▀▀█ █▀▀█ █▀▀█\n'),
undefined,
);
assert.equal(
scanner.feed('\x1b[36m█ █ █ █ █▀▀▀ █ █\x1b[0m █ █ █ █ █ █▀▀▀\n'),
'opencode',
);
assert.equal(
scanner.feed('\x1b[36m▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀\x1b[0m ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀'),
'opencode',
);
});
test('createCodingCliOutputScanner preserves ANSI sequences split across chunks', () => {
const scanner = createCodingCliOutputScanner();
assert.equal(
scanner.feed('\x1b[36m█▀▀█ █▀▀█ █▀▀█ █▀▀▄\x1b[0m █▀▀▀ █▀▀█ █▀▀█ █▀▀█\n'),
undefined,
);
assert.equal(
scanner.feed('\x1b[36m█ █ \x1b['),
undefined,
);
assert.equal(
scanner.feed('0m█ █ █▀▀▀ █ █\x1b[0m █ █ █ █ █ █▀▀▀'),
'opencode',
);
});
test('createCodingCliOutputScanner hides OSC payloads split before BEL or ST terminators', () => {
const belScanner = createCodingCliOutputScanner();
assert.equal(belScanner.feed('\x1b]0;Welcome to Claude Code'), undefined);
assert.equal(belScanner.feed('\x07ordinary output'), undefined);
const stScanner = createCodingCliOutputScanner();
assert.equal(stScanner.feed('\x1b]0;GitHub Copilot CLI'), undefined);
assert.equal(stScanner.feed('\x1b\\ordinary output'), undefined);
});
test('createCodingCliOutputScanner preserves visible output between ST-terminated OSC sequences', () => {
const scanner = createCodingCliOutputScanner();
assert.equal(
scanner.feed('\x1b]0;first title\x1b\\Welcome to Claude '),
undefined,
);
assert.equal(
scanner.feed('Code\x1b]0;second title\x1b\\'),
'claude',
);
});
test('createCodingCliOutputScanner hides provider text in OSC payloads longer than the scan buffer', () => {
const belScanner = createCodingCliOutputScanner();
assert.equal(
belScanner.feed(`\x1b]0;${'x'.repeat(9000)} Welcome to Claude Code`),
undefined,
);
assert.equal(belScanner.feed('\x07ordinary output'), undefined);
const stScanner = createCodingCliOutputScanner();
assert.equal(
stScanner.feed(`\x1b]0;${'x'.repeat(9000)} GitHub Copilot CLI`),
undefined,
);
assert.equal(stScanner.feed('\x1b\\ordinary output'), undefined);
});
test('createCodingCliOutputScanner strips split ESC intermediate sequences inside banners', () => {
const scanner = createCodingCliOutputScanner();
assert.equal(scanner.feed('Welcome to Claude \x1b('), undefined);
assert.equal(scanner.feed('BCode'), 'claude');
});
test('createCodingCliOutputScanner hides split DCS, SOS, PM, and APC payloads', () => {
for (const introducer of ['P', 'X', '^', '_']) {
const scanner = createCodingCliOutputScanner();
assert.equal(
scanner.feed(`\x1b${introducer}Welcome to Claude`),
undefined,
);
assert.equal(scanner.feed(' Code\x1b\\ordinary output'), undefined);
}
});
test('createCodingCliOutputScanner finds providers across chunked output', () => {
const scanner = createCodingCliOutputScanner();
assert.equal(scanner.feed('>_ Open'), undefined);
assert.equal(scanner.feed('AI Codex (v0.141.0)'), 'codex');
assert.equal(scanner.feed('more output'), 'codex');
});
test('stripTerminalControlSequences removes ANSI color codes', () => {
const stripped = stripTerminalControlSequences('\x1b[1mOpenAI Codex\x1b[0m');
assert.equal(stripped, 'OpenAI Codex');
});