testronaut 1.10.2 → 1.11.0
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/bin/cli.js +19 -3
- package/bin/terminalUi.js +51 -0
- package/package.json +1 -1
- package/tests/binTests/terminalUi.test.js +34 -0
- package/tests/toolsTests/generateHtmlReport.test.js +3 -0
- package/tests/toolsTests/turnLoopUtils.test.js +17 -0
- package/tools/generateHtmlReport.js +39 -21
- package/tools/turnLoopUtils.js +7 -11
package/bin/cli.js
CHANGED
|
@@ -57,6 +57,7 @@ import { discoverMissionFiles } from '../core/missionDiscovery.js';
|
|
|
57
57
|
import { loadMissionModule } from '../core/missionLoader.js';
|
|
58
58
|
import { loadConfig } from '../core/config.js';
|
|
59
59
|
import { matchesTagFilter, normalizeTagMatch, normalizeTags } from '../core/tags.js';
|
|
60
|
+
import { createTerminalUi } from './terminalUi.js';
|
|
60
61
|
|
|
61
62
|
// Keep PW browsers inside the project to avoid global cache skew
|
|
62
63
|
process.env.PLAYWRIGHT_BROWSERS_PATH = process.env.PLAYWRIGHT_BROWSERS_PATH || '0';
|
|
@@ -78,6 +79,7 @@ const jsonOutput = args.includes('--json');
|
|
|
78
79
|
const quietOutput = args.includes('--quiet') || jsonOutput;
|
|
79
80
|
args = args.filter(arg => arg !== '--json' && arg !== '--quiet');
|
|
80
81
|
const writeOutput = value => process.stdout.write(`${typeof value === 'string' ? value : JSON.stringify(value, null, 2)}\n`);
|
|
82
|
+
const terminalUi = createTerminalUi({ quiet: quietOutput });
|
|
81
83
|
if (quietOutput) {
|
|
82
84
|
console.log = () => {};
|
|
83
85
|
console.warn = () => {};
|
|
@@ -879,14 +881,14 @@ if (args.length === 1 && args[0] === 'list') {
|
|
|
879
881
|
const listedMissions = [];
|
|
880
882
|
const listTags = cliTags ?? normalizeTags(tagConfig?.tags, { allowUntagged: true });
|
|
881
883
|
const listTagMatch = normalizeTagMatch(cliTagMatch ?? tagConfig?.tagMatch);
|
|
882
|
-
if (!jsonOutput)
|
|
884
|
+
if (!jsonOutput) terminalUi.phase('preflight', `Missions in ${path.relative(process.cwd(), missionsRoot) || '.'}`);
|
|
883
885
|
for (const file of discoveredMissions) {
|
|
884
886
|
try {
|
|
885
887
|
const mission = await loadMissionModule(path.resolve(missionsRoot, file));
|
|
886
888
|
const tags = normalizeTags(mission.tags);
|
|
887
889
|
const selected = matchesTagFilter(tags, listTags, listTagMatch);
|
|
888
890
|
listedMissions.push({ file, tags, selected });
|
|
889
|
-
if (!jsonOutput)
|
|
891
|
+
if (!jsonOutput) terminalUi.mission(selected, file, tags);
|
|
890
892
|
} catch (error) {
|
|
891
893
|
console.log(` ${file} [could not load: ${error.message}]`);
|
|
892
894
|
process.exitCode = 1;
|
|
@@ -943,6 +945,17 @@ if (dryRun) {
|
|
|
943
945
|
return;
|
|
944
946
|
}
|
|
945
947
|
|
|
948
|
+
if (!jsonOutput) {
|
|
949
|
+
const previewModel = resolveProviderModel({ cwd: process.cwd() });
|
|
950
|
+
terminalUi.banner({
|
|
951
|
+
version: CLI_VERSION,
|
|
952
|
+
provider: previewModel.provider,
|
|
953
|
+
model: previewModel.model,
|
|
954
|
+
session: sessionMode,
|
|
955
|
+
});
|
|
956
|
+
terminalUi.phase('preflight', `${explicitFiles ? args.length : discoveredMissions.length} mission file(s) queued`);
|
|
957
|
+
}
|
|
958
|
+
|
|
946
959
|
const runFile = async (filePath) => {
|
|
947
960
|
const modulePath = resolveMissionPath(filePath, { cwd: process.cwd(), missionsRoot });
|
|
948
961
|
try {
|
|
@@ -960,6 +973,7 @@ const runFile = async (filePath) => {
|
|
|
960
973
|
}
|
|
961
974
|
|
|
962
975
|
if (typeof missionsModule.executeMission === 'function') {
|
|
976
|
+
if (!jsonOutput) terminalUi.phase('launch', path.relative(process.cwd(), modulePath) || filePath);
|
|
963
977
|
const result = await missionsModule.executeMission();
|
|
964
978
|
if (result == null) {
|
|
965
979
|
console.error(`❌ Mission did not return a result: ${filePath}`);
|
|
@@ -1053,8 +1067,10 @@ report.tags = normalizeTags(flatMissions.flatMap(m =>
|
|
|
1053
1067
|
|
|
1054
1068
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
1055
1069
|
fs.writeFileSync(path.join(outputDir, `${runId}.json`), JSON.stringify(report, null, 2));
|
|
1056
|
-
|
|
1070
|
+
const htmlReportPath = path.join(outputDir, `${runId}.html`);
|
|
1071
|
+
generateHtmlReport(report, htmlReportPath);
|
|
1057
1072
|
if (jsonOutput) writeOutput(report);
|
|
1073
|
+
else terminalUi.summary({ ...report.summary, reportPath: path.relative(process.cwd(), htmlReportPath) });
|
|
1058
1074
|
|
|
1059
1075
|
try {
|
|
1060
1076
|
if (!process.env.TN_KEEP_TMP && fs.existsSync(TMP_DIR)) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const ANSI = {
|
|
2
|
+
reset: '\u001b[0m',
|
|
3
|
+
bold: '\u001b[1m',
|
|
4
|
+
dim: '\u001b[2m',
|
|
5
|
+
orange: '\u001b[38;5;208m',
|
|
6
|
+
cyan: '\u001b[38;5;81m',
|
|
7
|
+
teal: '\u001b[38;5;43m',
|
|
8
|
+
red: '\u001b[38;5;203m',
|
|
9
|
+
cream: '\u001b[38;5;230m',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function shouldUseColor(stream = process.stdout, env = process.env) {
|
|
13
|
+
return Boolean(stream?.isTTY) && !('NO_COLOR' in env) && env.TERM !== 'dumb';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createTerminalUi({
|
|
17
|
+
stream = process.stdout,
|
|
18
|
+
color = shouldUseColor(stream),
|
|
19
|
+
quiet = false,
|
|
20
|
+
} = {}) {
|
|
21
|
+
const paint = (codes, value) => color ? `${codes}${value}${ANSI.reset}` : String(value);
|
|
22
|
+
const write = value => {
|
|
23
|
+
if (!quiet) stream.write(`${value}\n`);
|
|
24
|
+
};
|
|
25
|
+
const label = value => paint(`${ANSI.bold}${ANSI.orange}`, value.padEnd(10));
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
banner({ version, provider, model, session }) {
|
|
29
|
+
write(paint(`${ANSI.bold}${ANSI.cream}`, '🧑🚀 TESTRONAUT / MISSION RUNNER'));
|
|
30
|
+
write(paint(ANSI.dim, ` v${version} • ${provider}/${model} • ${session}`));
|
|
31
|
+
write(paint(ANSI.dim, ' ─────────────────────────────────────────'));
|
|
32
|
+
},
|
|
33
|
+
phase(name, message) {
|
|
34
|
+
write(`${label(name.toUpperCase())}${paint(ANSI.cyan, message)}`);
|
|
35
|
+
},
|
|
36
|
+
mission(selected, file, tags = []) {
|
|
37
|
+
const marker = selected ? paint(ANSI.teal, '●') : paint(ANSI.dim, '○');
|
|
38
|
+
const tagText = tags.length ? paint(ANSI.dim, ` ${tags.map(tag => `#${tag}`).join(' ')}`) : '';
|
|
39
|
+
write(` ${marker} ${file}${tagText}`);
|
|
40
|
+
},
|
|
41
|
+
summary({ totalMissions = 0, passed = 0, failed = 0, reportPath }) {
|
|
42
|
+
const status = failed === 0
|
|
43
|
+
? paint(`${ANSI.bold}${ANSI.teal}`, 'MISSION COMPLETE')
|
|
44
|
+
: paint(`${ANSI.bold}${ANSI.red}`, 'MISSION NEEDS REVIEW');
|
|
45
|
+
write('');
|
|
46
|
+
write(`${label('COMPLETE')}${status}`);
|
|
47
|
+
write(` ${paint(ANSI.teal, `${passed} passed`)} ${paint(failed ? ANSI.red : ANSI.dim, `${failed} failed`)} ${paint(ANSI.dim, `${totalMissions} total`)}`);
|
|
48
|
+
if (reportPath) write(paint(ANSI.dim, ` Report: ${reportPath}`));
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createTerminalUi, shouldUseColor } from '../../bin/terminalUi.js';
|
|
3
|
+
|
|
4
|
+
function capture() {
|
|
5
|
+
let output = '';
|
|
6
|
+
return {
|
|
7
|
+
stream: { isTTY: false, write(value) { output += value; } },
|
|
8
|
+
read: () => output,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('terminal UI', () => {
|
|
13
|
+
it('renders a readable color-free mission lifecycle', () => {
|
|
14
|
+
const target = capture();
|
|
15
|
+
const ui = createTerminalUi({ stream: target.stream, color: false });
|
|
16
|
+
|
|
17
|
+
ui.banner({ version: '1.2.3', provider: 'openai', model: 'gpt-test', session: 'isolated' });
|
|
18
|
+
ui.phase('launch', 'login.mission.js');
|
|
19
|
+
ui.mission(true, 'login.mission.js', ['smoke']);
|
|
20
|
+
ui.summary({ totalMissions: 1, passed: 1, failed: 0, reportPath: 'reports/run.html' });
|
|
21
|
+
|
|
22
|
+
expect(target.read()).toContain('🧑🚀 TESTRONAUT / MISSION RUNNER');
|
|
23
|
+
expect(target.read()).toContain('LAUNCH login.mission.js');
|
|
24
|
+
expect(target.read()).toContain('#smoke');
|
|
25
|
+
expect(target.read()).toContain('MISSION COMPLETE');
|
|
26
|
+
expect(target.read()).not.toContain('\u001b[');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('honors terminal capability and NO_COLOR', () => {
|
|
30
|
+
expect(shouldUseColor({ isTTY: true }, {})).toBe(true);
|
|
31
|
+
expect(shouldUseColor({ isTTY: true }, { NO_COLOR: '' })).toBe(false);
|
|
32
|
+
expect(shouldUseColor({ isTTY: false }, {})).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -55,6 +55,9 @@ describe('generateHtmlReport', () => {
|
|
|
55
55
|
const html = fs.readFileSync(outPath, 'utf8');
|
|
56
56
|
|
|
57
57
|
expect(html).toContain('Testronaut Report');
|
|
58
|
+
expect(html).toContain('Mission Control · Flight Report');
|
|
59
|
+
expect(html).toContain('--orange:#ff8a43');
|
|
60
|
+
expect(html).toContain('--cyan:#79d7df');
|
|
58
61
|
expect(html).toContain('Mission A');
|
|
59
62
|
expect(html).toContain('Turn 1 (re-attempt 1/5)');
|
|
60
63
|
expect(html).toContain('⚠️ Turn Issues');
|
|
@@ -37,4 +37,21 @@ describe('tools/turnLoopUtils', () => {
|
|
|
37
37
|
logSpy.mockRestore();
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
|
+
|
|
41
|
+
it('uses the mission transmission frame without changing result data', () => {
|
|
42
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
43
|
+
try {
|
|
44
|
+
expect(finalResponseHandler({ content: 'SUCCESS: Docking complete' })).toEqual({
|
|
45
|
+
finalMessage: 'SUCCESS: Docking complete',
|
|
46
|
+
success: true,
|
|
47
|
+
});
|
|
48
|
+
const printed = logSpy.mock.calls.flat().join('\n');
|
|
49
|
+
expect(printed).toContain('🧑🚀 MISSION TRANSMISSION / SUCCESS');
|
|
50
|
+
expect(printed).toContain('│ SUCCESS: Docking complete');
|
|
51
|
+
expect(printed).toContain('END TRANSMISSION');
|
|
52
|
+
expect(printed).not.toContain('FINAL AGENT RESPONSE');
|
|
53
|
+
} finally {
|
|
54
|
+
logSpy.mockRestore();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
40
57
|
});
|
|
@@ -196,55 +196,71 @@ export function generateHtmlReport(report, outputPath) {
|
|
|
196
196
|
<title>Testronaut Report – ${esc(runId)}</title>
|
|
197
197
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
198
198
|
<style>
|
|
199
|
-
/* =====
|
|
199
|
+
/* ===== Testronaut launch-control tokens ===== */
|
|
200
200
|
:root{
|
|
201
|
-
--bg-top:#
|
|
202
|
-
--bg-mid:#
|
|
203
|
-
--bg-btm:#
|
|
204
|
-
|
|
205
|
-
--
|
|
206
|
-
--
|
|
201
|
+
--bg-top:#050b18;
|
|
202
|
+
--bg-mid:#081426;
|
|
203
|
+
--bg-btm:#030712;
|
|
204
|
+
--panel:rgba(8,24,45,.78);
|
|
205
|
+
--panel-strong:rgba(7,19,36,.94);
|
|
206
|
+
--orange:#ff8a43;
|
|
207
|
+
--amber:#ffc15b;
|
|
208
|
+
--cyan:#79d7df;
|
|
209
|
+
|
|
210
|
+
--hairline:rgba(121,215,223,.20);
|
|
211
|
+
--hairline-strong:rgba(121,215,223,.34);
|
|
207
212
|
|
|
208
213
|
--text:rgba(255,255,255,.92);
|
|
209
214
|
--text-muted:rgba(255,255,255,.65);
|
|
210
215
|
|
|
211
|
-
--ok:#
|
|
212
|
-
--bad:#
|
|
216
|
+
--ok:#39d9aa;
|
|
217
|
+
--bad:#ff6678;
|
|
213
218
|
|
|
214
219
|
--chip-ok-bg:rgba(34,197,94,.14);
|
|
215
220
|
--chip-bad-bg:rgba(239,68,68,.16);
|
|
216
|
-
--chip-border:rgba(
|
|
221
|
+
--chip-border:rgba(121,215,223,.30);
|
|
217
222
|
--chip-ok-border:rgba(34,197,94,.40);
|
|
218
223
|
--chip-bad-border:rgba(239,68,68,.45);
|
|
219
224
|
}
|
|
220
225
|
|
|
221
226
|
/* ===== Base ===== */
|
|
222
|
-
html,body{height:100%;}
|
|
227
|
+
html,body{min-height:100%;}
|
|
223
228
|
body{
|
|
224
229
|
margin:0; padding:24px;
|
|
225
230
|
font-family: ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Helvetica,Arial;
|
|
226
|
-
background:
|
|
231
|
+
background:
|
|
232
|
+
radial-gradient(circle at 84% 8%,rgba(255,138,67,.16),transparent 25rem),
|
|
233
|
+
radial-gradient(circle at 10% 32%,rgba(121,215,223,.10),transparent 28rem),
|
|
234
|
+
linear-gradient(180deg,var(--bg-top) 0%,var(--bg-mid) 60%,var(--bg-btm) 100%);
|
|
235
|
+
background-attachment:fixed;
|
|
227
236
|
color: var(--text);
|
|
228
237
|
-webkit-font-smoothing: antialiased;
|
|
229
238
|
-moz-osx-font-smoothing: grayscale;
|
|
230
239
|
}
|
|
240
|
+
body::before{
|
|
241
|
+
content:"";position:fixed;inset:0;pointer-events:none;opacity:.22;
|
|
242
|
+
background-image:linear-gradient(rgba(121,215,223,.08) 1px,transparent 1px),linear-gradient(90deg,rgba(121,215,223,.08) 1px,transparent 1px);
|
|
243
|
+
background-size:42px 42px;mask-image:linear-gradient(to bottom,black,transparent 82%);
|
|
244
|
+
}
|
|
231
245
|
|
|
232
246
|
/* ===== Utilities ===== */
|
|
233
247
|
.gradient-text{
|
|
234
|
-
background: linear-gradient(90deg,#
|
|
248
|
+
background: linear-gradient(90deg,#fff7e7,var(--amber),var(--orange));
|
|
235
249
|
-webkit-background-clip:text; background-clip:text; color:transparent;
|
|
236
250
|
}
|
|
237
251
|
.glass{
|
|
238
|
-
background:
|
|
252
|
+
background: var(--panel);
|
|
239
253
|
border: 1px solid var(--hairline);
|
|
240
254
|
border-radius:16px;
|
|
241
255
|
backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
|
|
242
|
-
box-shadow: 0
|
|
256
|
+
box-shadow: 0 18px 45px rgba(0,0,0,.28), inset 0 1px rgba(255,255,255,.035);
|
|
243
257
|
}
|
|
244
258
|
|
|
245
259
|
/* ===== Header ===== */
|
|
246
|
-
.header{ max-width:960px; margin:0 auto 16px; padding:
|
|
247
|
-
|
|
260
|
+
.header{ max-width:960px; margin:0 auto 16px; padding:22px 24px; position:relative; overflow:hidden; }
|
|
261
|
+
.header::after{content:"";position:absolute;right:0;top:0;width:92px;height:4px;background:linear-gradient(90deg,var(--orange),var(--amber));}
|
|
262
|
+
.eyebrow{margin-bottom:7px;color:var(--cyan);font-size:11px;font-weight:800;letter-spacing:.18em;text-transform:uppercase;}
|
|
263
|
+
h1{ margin:0 0 12px; font-size:27px; font-weight:850; color:#fff; letter-spacing:-.02em; }
|
|
248
264
|
.run-meta{ color:var(--text-muted); display:grid; gap:4px; }
|
|
249
265
|
.provider-meta{ display:inline-flex; align-items:center; gap:7px; }
|
|
250
266
|
.provider-logo{ width:20px; height:20px; color:var(--text); flex:none; }
|
|
@@ -258,6 +274,7 @@ export function generateHtmlReport(report, outputPath) {
|
|
|
258
274
|
padding:6px 12px; border-radius:999px; font-size:12px; color:var(--text);
|
|
259
275
|
letter-spacing:.2px;
|
|
260
276
|
}
|
|
277
|
+
.pill:first-child{border-color:rgba(255,138,67,.5);box-shadow:inset 3px 0 var(--orange);}
|
|
261
278
|
.pill.ok{ background: var(--chip-ok-bg); color: var(--ok); border-color: var(--chip-ok-border); font-weight:700; }
|
|
262
279
|
.pill.bad{ background: var(--chip-bad-bg); color: var(--bad); border-color: var(--chip-bad-border); font-weight:700; }
|
|
263
280
|
.tag-filter{max-width:928px;margin:0 auto 20px;padding:18px 16px;background:rgba(2,6,23,.30);}
|
|
@@ -267,7 +284,7 @@ export function generateHtmlReport(report, outputPath) {
|
|
|
267
284
|
.match-count{margin-left:auto;color:var(--text-muted);font-size:12px;}
|
|
268
285
|
.tag-buttons,.mission-tags{display:flex;gap:6px;flex-wrap:wrap;align-items:center;}
|
|
269
286
|
.tag-button,.tag-small{border:1px solid var(--chip-border);background:rgba(255,255,255,.06);color:var(--text);border-radius:999px;padding:5px 10px;font-size:11px;font-weight:700;}
|
|
270
|
-
.tag-button{cursor:pointer;transition:transform .15s ease,background .15s ease}.tag-button:hover{transform:translateY(-1px);background:rgba(
|
|
287
|
+
.tag-button{cursor:pointer;transition:transform .15s ease,background .15s ease}.tag-button:hover{transform:translateY(-1px);background:rgba(121,215,223,.12)}.tag-button.active{box-shadow:0 0 0 2px rgba(255,138,67,.48);}
|
|
271
288
|
.filter-controls{display:flex;gap:16px;align-items:center;margin-top:14px;font-size:12px;color:var(--text-muted);flex-wrap:wrap;}
|
|
272
289
|
.filter-controls select{color:var(--text);background:#111a31;border:1px solid var(--chip-border);border-radius:8px;padding:6px 24px 6px 8px;}
|
|
273
290
|
.show-control{cursor:help;display:flex;align-items:center;gap:5px;}
|
|
@@ -277,12 +294,12 @@ export function generateHtmlReport(report, outputPath) {
|
|
|
277
294
|
@media(max-width:640px){.match-count{width:100%;margin-left:0}.tag-filter{padding:14px 12px}.meta{display:none}}
|
|
278
295
|
|
|
279
296
|
/* ===== Disclosure blocks ===== */
|
|
280
|
-
details{ background:
|
|
297
|
+
details{ background: var(--panel); border:1px solid var(--hairline); border-radius:16px; margin:10px 0; overflow:hidden; }
|
|
281
298
|
summary{
|
|
282
299
|
cursor:pointer; padding:14px 16px; display:flex; align-items:center; gap:12px;
|
|
283
300
|
color:#fff; background: rgba(255,255,255,.04);
|
|
284
301
|
}
|
|
285
|
-
.mission-group > summary{ font-weight:800; font-size:15px; letter-spacing:.2px; }
|
|
302
|
+
.mission-group > summary{ font-weight:800; font-size:15px; letter-spacing:.2px; border-left:3px solid var(--orange); }
|
|
286
303
|
.mission-group > summary:hover{ background: rgba(255,255,255,.08); }
|
|
287
304
|
|
|
288
305
|
.group-body{ padding:12px; display:grid; gap:10px; }
|
|
@@ -336,7 +353,7 @@ export function generateHtmlReport(report, outputPath) {
|
|
|
336
353
|
.tokens{ color: var(--text-muted); font-size:12px; margin-left:auto; font-variant-numeric: tabular-nums; }
|
|
337
354
|
|
|
338
355
|
.events{
|
|
339
|
-
background:
|
|
356
|
+
background: var(--panel-strong);
|
|
340
357
|
color: rgba(255,255,255,.92);
|
|
341
358
|
padding:12px; border-radius:12px; margin:12px 0 0;
|
|
342
359
|
font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New";
|
|
@@ -357,6 +374,7 @@ export function generateHtmlReport(report, outputPath) {
|
|
|
357
374
|
</head>
|
|
358
375
|
<body>
|
|
359
376
|
<div class="header glass">
|
|
377
|
+
<div class="eyebrow">Mission Control · Flight Report</div>
|
|
360
378
|
<h1>🧑🚀 <span class="gradient-text">Testronaut Report</span></h1>
|
|
361
379
|
<div class="run-meta">
|
|
362
380
|
<div class="provider-meta"><span class="provider-logo" title="${esc(provider.name)}" aria-label="${esc(provider.name)}">${provider.logo}</span><strong>${esc(llm.model ?? '—')}</strong></div>
|
package/tools/turnLoopUtils.js
CHANGED
|
@@ -5,17 +5,13 @@ export const finalResponseHandler = (msg) => {
|
|
|
5
5
|
const safeContent = redactPasswordInText(content);
|
|
6
6
|
const final = content.trim().replace(/^[#*_`\s]+/, '');
|
|
7
7
|
const verdict = final.match(/^(?:(?:result|final)\s*:\s*)?(success|failure)\b/i)?.[1]?.toLowerCase();
|
|
8
|
-
if (verdict === 'success') {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
console.log('\n┏━ FINAL AGENT RESPONSE ━━━━━━━━━━━━━━━━━━━');
|
|
16
|
-
console.log(safeContent);
|
|
17
|
-
console.log('┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
18
|
-
return { finalMessage: safeContent, success: false};
|
|
8
|
+
if (verdict === 'success' || verdict === 'failure') {
|
|
9
|
+
const success = verdict === 'success';
|
|
10
|
+
const signal = success ? 'SUCCESS' : 'FAILURE';
|
|
11
|
+
console.log(`\n╭─ 🧑🚀 MISSION TRANSMISSION / ${signal} ─────────────────`);
|
|
12
|
+
safeContent.split('\n').forEach(line => console.log(`│ ${line}`));
|
|
13
|
+
console.log('╰─ END TRANSMISSION ─────────────────────────────');
|
|
14
|
+
return { finalMessage: safeContent, success };
|
|
19
15
|
}
|
|
20
16
|
return null;
|
|
21
17
|
}
|