mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-09-22 17:50:50 +02:00
feat(cli): sunset wordmark and section hierarchy in terminal output
Add a section-chrome module (chrome.ts) and a log decorator (log-render.ts) that give the CLI a coherent visual hierarchy beneath the SHANNON splash, all drawn from the existing sunset ramp: - splash.ts now sources the ramp from chrome.ts (one definition) - start's launch info gets a solid-yellow rule under each section label, with aligned Label: values greyed - streaming scan logs (logs, and start --follow) get one per-phase gutter bar that walks the ramp, dimmed timestamps, red on [ERROR], and the end-of-run summary framed in a corner panel Named chrome.ts to avoid the existing ui.ts (spinner/step helpers). Presentation only: the worker and workflow.log are untouched, the log on disk stays plain text (tail/grep and the failure marker keep working), and non-TTY / NO_COLOR output is byte-identical to before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6108de3cfc
commit
d017d74519
@@ -11,8 +11,10 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { setTimeout as sleep } from 'node:timers/promises';
|
||||
import { watch } from 'chokidar';
|
||||
import { field } from '../chrome.js';
|
||||
import { fail } from '../errors.js';
|
||||
import { getWorkspacesDir } from '../home.js';
|
||||
import { LogRenderer } from '../log-render.js';
|
||||
import { resolveRunFile } from '../paths.js';
|
||||
import { resolveWorkflowId } from '../session.js';
|
||||
import { waitForWorkflowClose } from '../temporal-client.js';
|
||||
@@ -90,6 +92,9 @@ export function tailUntilComplete(logFile: string, opts: TailOptions = {}): Prom
|
||||
let position = 0;
|
||||
let done = false;
|
||||
let sawFailure = false;
|
||||
// Decorates the streamed log for the terminal; a pass-through when colour is off, so
|
||||
// piped/redirected output stays byte-identical and the failure check still sees raw text.
|
||||
const renderer = new LogRenderer();
|
||||
const controller = new AbortController();
|
||||
let watcher: ReturnType<typeof watch> | undefined;
|
||||
|
||||
@@ -99,7 +104,7 @@ export function tailUntilComplete(logFile: string, opts: TailOptions = {}): Prom
|
||||
const { size } = fs.statSync(logFile);
|
||||
if (size <= position) return;
|
||||
const data = readRange(logFile, position, size);
|
||||
process.stdout.write(data);
|
||||
process.stdout.write(renderer.write(data));
|
||||
position = size;
|
||||
if (!sawFailure && FAILURE_MARKER.test(data)) {
|
||||
sawFailure = true;
|
||||
@@ -112,6 +117,7 @@ export function tailUntilComplete(logFile: string, opts: TailOptions = {}): Prom
|
||||
function finish(): void {
|
||||
if (done) return;
|
||||
done = true;
|
||||
process.stdout.write(renderer.end());
|
||||
controller.abort();
|
||||
if (watcher) {
|
||||
watcher.close().finally(() => resolve({ sawFailure }));
|
||||
@@ -165,7 +171,7 @@ export function tailUntilComplete(logFile: string, opts: TailOptions = {}): Prom
|
||||
export function logs(workspaceId: string): void {
|
||||
const logFile = resolveLogFile(workspaceId);
|
||||
const workflowId = resolveWorkflowId(workspaceId);
|
||||
console.error(stdoutIsTerminal() ? `Tailing scan log: ${logFile}` : 'Tailing scan log');
|
||||
console.error(stdoutIsTerminal() ? field(`Tailing scan log: ${logFile}`) : 'Tailing scan log');
|
||||
|
||||
let unreachable = false;
|
||||
tailUntilComplete(logFile, {
|
||||
|
||||
@@ -10,6 +10,7 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { setTimeout as sleep } from 'node:timers/promises';
|
||||
import * as p from '@clack/prompts';
|
||||
import { field, rule } from '../chrome.js';
|
||||
import { ensureDocker, ensureImage, ensureInfra, randomSuffix, spawnWorker } from '../docker.js';
|
||||
import { buildEnvFlags, loadEnv, resolveHostPiAuthPath, shouldUsePiAuth, validateCredentials } from '../env.js';
|
||||
import { fail } from '../errors.js';
|
||||
@@ -298,9 +299,9 @@ async function followScan(workspace: string, workspacesDir: string): Promise<nev
|
||||
|
||||
function printPreservedContainerHint(containerName: string): void {
|
||||
console.log('');
|
||||
console.log(` Worker container preserved: ${containerName}`);
|
||||
console.log(` Inspect logs: docker logs ${containerName}`);
|
||||
console.log(` Remove: docker rm ${containerName}`);
|
||||
console.log(field(` Worker container preserved: ${containerName}`));
|
||||
console.log(field(` Inspect logs: docker logs ${containerName}`));
|
||||
console.log(field(` Remove: docker rm ${containerName}`));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
@@ -312,19 +313,19 @@ function printInfo(args: StartArgs, workspace: string, repoPath: string, workspa
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log(` Target: ${args.url}`);
|
||||
console.log(` Repository: ${interactive ? repoPath : path.basename(repoPath)}`);
|
||||
console.log(` Workspace: ${workspace}`);
|
||||
console.log(field(` Target: ${args.url}`));
|
||||
console.log(field(` Repository: ${interactive ? repoPath : path.basename(repoPath)}`));
|
||||
console.log(field(` Workspace: ${workspace}`));
|
||||
if (args.config) {
|
||||
console.log(` Config: ${interactive ? path.resolve(args.config) : path.basename(args.config)}`);
|
||||
console.log(field(` Config: ${interactive ? path.resolve(args.config) : path.basename(args.config)}`));
|
||||
}
|
||||
if (args.pipelineTesting) {
|
||||
console.log(' Mode: Pipeline Testing');
|
||||
console.log(field(' Mode: Pipeline Testing'));
|
||||
}
|
||||
|
||||
const spec = resolveModelSpec();
|
||||
if (typeof spec !== 'string') {
|
||||
console.log(` Model: ${spec.providerId}:${spec.modelId}`);
|
||||
console.log(field(` Model: ${spec.providerId}:${spec.modelId}`));
|
||||
}
|
||||
|
||||
if (!interactive) {
|
||||
@@ -338,13 +339,13 @@ function printInfo(args: StartArgs, workspace: string, repoPath: string, workspa
|
||||
if (!args.follow) {
|
||||
const prefix = commandPrefix();
|
||||
console.log('');
|
||||
console.log(' Watch scan progress:');
|
||||
console.log(` Live logs: ${prefix} logs ${workspace}`);
|
||||
console.log(` Progress: ${prefix} status ${workspace}`);
|
||||
console.log(rule('Watch scan progress:'));
|
||||
console.log(field(` Live logs: ${prefix} logs ${workspace}`));
|
||||
console.log(field(` Progress: ${prefix} status ${workspace}`));
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log(' Report (when the scan finishes):');
|
||||
console.log(rule('Report (when the scan finishes):'));
|
||||
console.log(` ${reportPath}`);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user