refactor: remove ~275 lines of dead code and enable stricter tsconfig
- Delete unused src/cli/ui.ts, remove zod dependency, drop 4 dead functions (logError, handleToolError, getRetryDelay, displayTimingSummary) - Remove 8 unused types/interfaces and 3 duplicate formatting utils from audit/utils.ts - Narrow export surface: make 7 message-handler functions private, remove unused audit re-exports, unexport AgentDefinition and path constants - Remove unused runClaudePrompt params (sessionMetadata, attemptNumber) and update caller - Enable tsconfig noUnusedLocals, noUnusedParameters, noImplicitReturns, noImplicitOverride, noFallthroughCasesInSwitch
This commit is contained in:
Generated
-1
@@ -21,7 +21,6 @@
|
|||||||
"figlet": "^1.9.3",
|
"figlet": "^1.9.3",
|
||||||
"gradient-string": "^3.0.0",
|
"gradient-string": "^3.0.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"zod": "^4.3.6",
|
|
||||||
"zx": "^8.0.0"
|
"zx": "^8.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
"figlet": "^1.9.3",
|
"figlet": "^1.9.3",
|
||||||
"gradient-string": "^3.0.0",
|
"gradient-string": "^3.0.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"zod": "^4.3.6",
|
|
||||||
"zx": "^8.0.0"
|
"zx": "^8.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { formatTimestamp } from '../utils/formatting.js';
|
|||||||
import { AGENT_VALIDATORS, MCP_AGENT_MAPPING } from '../constants.js';
|
import { AGENT_VALIDATORS, MCP_AGENT_MAPPING } from '../constants.js';
|
||||||
import { AuditSession } from '../audit/index.js';
|
import { AuditSession } from '../audit/index.js';
|
||||||
import { createShannonHelperServer } from '../../mcp-server/dist/index.js';
|
import { createShannonHelperServer } from '../../mcp-server/dist/index.js';
|
||||||
import type { SessionMetadata } from '../audit/utils.js';
|
|
||||||
import { getPromptNameForAgent } from '../types/agents.js';
|
import { getPromptNameForAgent } from '../types/agents.js';
|
||||||
import type { AgentName } from '../types/index.js';
|
import type { AgentName } from '../types/index.js';
|
||||||
|
|
||||||
@@ -200,9 +199,7 @@ export async function runClaudePrompt(
|
|||||||
description: string = 'Claude analysis',
|
description: string = 'Claude analysis',
|
||||||
agentName: string | null = null,
|
agentName: string | null = null,
|
||||||
colorFn: ChalkInstance = chalk.cyan,
|
colorFn: ChalkInstance = chalk.cyan,
|
||||||
sessionMetadata: SessionMetadata | null = null,
|
auditSession: AuditSession | null = null
|
||||||
auditSession: AuditSession | null = null,
|
|
||||||
attemptNumber: number = 1
|
|
||||||
): Promise<ClaudePromptResult> {
|
): Promise<ClaudePromptResult> {
|
||||||
const timer = new Timer(`agent-${description.toLowerCase().replace(/\s+/g, '-')}`);
|
const timer = new Timer(`agent-${description.toLowerCase().replace(/\s+/g, '-')}`);
|
||||||
const fullPrompt = context ? `${context}\n\n${prompt}` : prompt;
|
const fullPrompt = context ? `${context}\n\n${prompt}` : prompt;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import type {
|
|||||||
import type { ChalkInstance } from 'chalk';
|
import type { ChalkInstance } from 'chalk';
|
||||||
|
|
||||||
// Handles both array and string content formats from SDK
|
// Handles both array and string content formats from SDK
|
||||||
export function extractMessageContent(message: AssistantMessage): string {
|
function extractMessageContent(message: AssistantMessage): string {
|
||||||
const messageContent = message.message;
|
const messageContent = message.message;
|
||||||
|
|
||||||
if (Array.isArray(messageContent.content)) {
|
if (Array.isArray(messageContent.content)) {
|
||||||
@@ -51,7 +51,7 @@ export function extractMessageContent(message: AssistantMessage): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extracts only text content (no tool_use JSON) to avoid false positives in error detection
|
// Extracts only text content (no tool_use JSON) to avoid false positives in error detection
|
||||||
export function extractTextOnlyContent(message: AssistantMessage): string {
|
function extractTextOnlyContent(message: AssistantMessage): string {
|
||||||
const messageContent = message.message;
|
const messageContent = message.message;
|
||||||
|
|
||||||
if (Array.isArray(messageContent.content)) {
|
if (Array.isArray(messageContent.content)) {
|
||||||
@@ -64,7 +64,7 @@ export function extractTextOnlyContent(message: AssistantMessage): string {
|
|||||||
return String(messageContent.content);
|
return String(messageContent.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function detectApiError(content: string): ApiErrorDetection {
|
function detectApiError(content: string): ApiErrorDetection {
|
||||||
if (!content || typeof content !== 'string') {
|
if (!content || typeof content !== 'string') {
|
||||||
return { detected: false };
|
return { detected: false };
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ function handleStructuredError(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleAssistantMessage(
|
function handleAssistantMessage(
|
||||||
message: AssistantMessage,
|
message: AssistantMessage,
|
||||||
turnCount: number
|
turnCount: number
|
||||||
): AssistantResult {
|
): AssistantResult {
|
||||||
@@ -219,7 +219,7 @@ export function handleAssistantMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Final message of a query with cost/duration info
|
// Final message of a query with cost/duration info
|
||||||
export function handleResultMessage(message: ResultMessage): ResultData {
|
function handleResultMessage(message: ResultMessage): ResultData {
|
||||||
const result: ResultData = {
|
const result: ResultData = {
|
||||||
result: message.result || null,
|
result: message.result || null,
|
||||||
cost: message.total_cost_usd || 0,
|
cost: message.total_cost_usd || 0,
|
||||||
@@ -243,7 +243,7 @@ export function handleResultMessage(message: ResultMessage): ResultData {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleToolUseMessage(message: ToolUseMessage): ToolUseData {
|
function handleToolUseMessage(message: ToolUseMessage): ToolUseData {
|
||||||
return {
|
return {
|
||||||
toolName: message.name,
|
toolName: message.name,
|
||||||
parameters: message.input || {},
|
parameters: message.input || {},
|
||||||
@@ -252,7 +252,7 @@ export function handleToolUseMessage(message: ToolUseMessage): ToolUseData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Truncates long results for display (500 char limit), preserves full content for logging
|
// Truncates long results for display (500 char limit), preserves full content for logging
|
||||||
export function handleToolResultMessage(message: ToolResultMessage): ToolResultData {
|
function handleToolResultMessage(message: ToolResultMessage): ToolResultData {
|
||||||
const content = message.content;
|
const content = message.content;
|
||||||
const contentStr =
|
const contentStr =
|
||||||
typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
||||||
|
|||||||
@@ -13,22 +13,6 @@ export interface ExecutionContext {
|
|||||||
agentKey: string;
|
agentKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProcessingState {
|
|
||||||
turnCount: number;
|
|
||||||
result: string | null;
|
|
||||||
apiErrorDetected: boolean;
|
|
||||||
totalCost: number;
|
|
||||||
partialCost: number;
|
|
||||||
lastHeartbeat: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProcessingResult {
|
|
||||||
result: string | null;
|
|
||||||
turnCount: number;
|
|
||||||
apiErrorDetected: boolean;
|
|
||||||
totalCost: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssistantResult {
|
export interface AssistantResult {
|
||||||
content: string;
|
content: string;
|
||||||
cleanedContent: string;
|
cleanedContent: string;
|
||||||
@@ -110,15 +94,6 @@ export interface ApiErrorDetection {
|
|||||||
shouldThrow?: Error;
|
shouldThrow?: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message types from SDK stream
|
|
||||||
type SdkMessage =
|
|
||||||
| AssistantMessage
|
|
||||||
| ResultMessage
|
|
||||||
| ToolUseMessage
|
|
||||||
| ToolResultMessage
|
|
||||||
| SystemInitMessage
|
|
||||||
| UserMessage;
|
|
||||||
|
|
||||||
export interface SystemInitMessage {
|
export interface SystemInitMessage {
|
||||||
type: 'system';
|
type: 'system';
|
||||||
subtype: 'init';
|
subtype: 'init';
|
||||||
@@ -131,16 +106,3 @@ export interface UserMessage {
|
|||||||
type: 'user';
|
type: 'user';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch result types for message processing
|
|
||||||
type MessageDispatchResult =
|
|
||||||
| { action: 'continue' }
|
|
||||||
| { action: 'break'; result: string | null; cost: number }
|
|
||||||
| { action: 'throw'; error: Error };
|
|
||||||
|
|
||||||
interface MessageDispatchContext {
|
|
||||||
turnCount: number;
|
|
||||||
execContext: ExecutionContext;
|
|
||||||
description: string;
|
|
||||||
colorFn: (text: string) => string;
|
|
||||||
useCleanOutput: boolean;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,7 +17,3 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export { AuditSession } from './audit-session.js';
|
export { AuditSession } from './audit-session.js';
|
||||||
export { AgentLogger } from './logger.js';
|
|
||||||
export { WorkflowLogger } from './workflow-logger.js';
|
|
||||||
export { MetricsTracker } from './metrics-tracker.js';
|
|
||||||
export * as AuditUtils from './utils.js';
|
|
||||||
|
|||||||
+2
-35
@@ -19,8 +19,8 @@ const __filename = fileURLToPath(import.meta.url);
|
|||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
// Get Shannon repository root
|
// Get Shannon repository root
|
||||||
export const SHANNON_ROOT = path.resolve(__dirname, '..', '..');
|
const SHANNON_ROOT = path.resolve(__dirname, '..', '..');
|
||||||
export const AUDIT_LOGS_DIR = path.join(SHANNON_ROOT, 'audit-logs');
|
const AUDIT_LOGS_DIR = path.join(SHANNON_ROOT, 'audit-logs');
|
||||||
|
|
||||||
export interface SessionMetadata {
|
export interface SessionMetadata {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -132,39 +132,6 @@ export async function atomicWrite(filePath: string, data: object | string): Prom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format duration in milliseconds to human-readable string
|
|
||||||
*/
|
|
||||||
export function formatDuration(ms: number): string {
|
|
||||||
if (ms < 1000) {
|
|
||||||
return `${ms}ms`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const seconds = ms / 1000;
|
|
||||||
if (seconds < 60) {
|
|
||||||
return `${seconds.toFixed(1)}s`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const minutes = Math.floor(seconds / 60);
|
|
||||||
const remainingSeconds = Math.floor(seconds % 60);
|
|
||||||
return `${minutes}m ${remainingSeconds}s`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format timestamp to ISO 8601 string
|
|
||||||
*/
|
|
||||||
export function formatTimestamp(timestamp: number = Date.now()): string {
|
|
||||||
return new Date(timestamp).toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate percentage
|
|
||||||
*/
|
|
||||||
export function calculatePercentage(part: number, total: number): number {
|
|
||||||
if (total === 0) return 0;
|
|
||||||
return (part / total) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read and parse JSON file
|
* Read and parse JSON file
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
// Copyright (C) 2025 Keygraph, Inc.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License version 3
|
|
||||||
// as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import { displaySplashScreen } from '../splash-screen.js';
|
|
||||||
|
|
||||||
// Helper function: Display help information
|
|
||||||
function showHelp(): void {
|
|
||||||
console.log(chalk.cyan.bold('AI Penetration Testing Agent'));
|
|
||||||
console.log(chalk.gray('Automated security assessment tool\n'));
|
|
||||||
|
|
||||||
console.log(chalk.yellow.bold('USAGE:'));
|
|
||||||
console.log(' shannon <WEB_URL> <REPO_PATH> [--config config.yaml] [--output /path/to/reports]\n');
|
|
||||||
|
|
||||||
console.log(chalk.yellow.bold('OPTIONS:'));
|
|
||||||
console.log(
|
|
||||||
' --config <file> YAML configuration file for authentication and testing parameters'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
' --output <path> Custom output directory for session folder (default: ./audit-logs/)'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
' --pipeline-testing Use minimal prompts for fast pipeline testing (creates minimal deliverables)'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
' --disable-loader Disable the animated progress loader (useful when logs interfere with spinner)'
|
|
||||||
);
|
|
||||||
console.log(' --help Show this help message\n');
|
|
||||||
|
|
||||||
console.log(chalk.yellow.bold('EXAMPLES:'));
|
|
||||||
console.log(' shannon "https://example.com" "/path/to/local/repo"');
|
|
||||||
console.log(' shannon "https://example.com" "/path/to/local/repo" --config auth.yaml');
|
|
||||||
console.log(' shannon "https://example.com" "/path/to/local/repo" --output /path/to/reports');
|
|
||||||
console.log(' shannon "https://example.com" "/path/to/local/repo" --pipeline-testing\n');
|
|
||||||
|
|
||||||
console.log(chalk.yellow.bold('REQUIREMENTS:'));
|
|
||||||
console.log(' • WEB_URL must start with http:// or https://');
|
|
||||||
console.log(' • REPO_PATH must be an accessible local directory');
|
|
||||||
console.log(' • Only test systems you own or have permission to test\n');
|
|
||||||
|
|
||||||
console.log(chalk.yellow.bold('ENVIRONMENT VARIABLES:'));
|
|
||||||
console.log(' PENTEST_MAX_RETRIES Number of retries for AI agents (default: 3)');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export the splash screen function for use in main
|
|
||||||
export { displaySplashScreen };
|
|
||||||
@@ -13,7 +13,6 @@ import { PentestError } from './error-handling.js';
|
|||||||
import type {
|
import type {
|
||||||
Config,
|
Config,
|
||||||
Rule,
|
Rule,
|
||||||
Rules,
|
|
||||||
Authentication,
|
Authentication,
|
||||||
DistributedConfig,
|
DistributedConfig,
|
||||||
} from './types/config.js';
|
} from './types/config.js';
|
||||||
|
|||||||
+2
-96
@@ -4,25 +4,15 @@
|
|||||||
// it under the terms of the GNU Affero General Public License version 3
|
// it under the terms of the GNU Affero General Public License version 3
|
||||||
// as published by the Free Software Foundation.
|
// as published by the Free Software Foundation.
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import { fs, path } from 'zx';
|
|
||||||
import type {
|
import type {
|
||||||
PentestErrorType,
|
PentestErrorType,
|
||||||
PentestErrorContext,
|
PentestErrorContext,
|
||||||
LogEntry,
|
|
||||||
ToolErrorResult,
|
|
||||||
PromptErrorResult,
|
PromptErrorResult,
|
||||||
} from './types/errors.js';
|
} from './types/errors.js';
|
||||||
|
|
||||||
// Temporal error classification for ApplicationFailure wrapping
|
|
||||||
export interface TemporalErrorClassification {
|
|
||||||
type: string;
|
|
||||||
retryable: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom error class for pentest operations
|
// Custom error class for pentest operations
|
||||||
export class PentestError extends Error {
|
export class PentestError extends Error {
|
||||||
name = 'PentestError' as const;
|
override name = 'PentestError' as const;
|
||||||
type: PentestErrorType;
|
type: PentestErrorType;
|
||||||
retryable: boolean;
|
retryable: boolean;
|
||||||
context: PentestErrorContext;
|
context: PentestErrorContext;
|
||||||
@@ -42,76 +32,7 @@ export class PentestError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logError(
|
|
||||||
error: Error & { type?: PentestErrorType; retryable?: boolean; context?: PentestErrorContext },
|
|
||||||
contextMsg: string,
|
|
||||||
sourceDir: string | null = null
|
|
||||||
): Promise<LogEntry> {
|
|
||||||
const timestamp = new Date().toISOString();
|
|
||||||
const logEntry: LogEntry = {
|
|
||||||
timestamp,
|
|
||||||
context: contextMsg,
|
|
||||||
error: {
|
|
||||||
name: error.name || error.constructor.name,
|
|
||||||
message: error.message,
|
|
||||||
type: error.type || 'unknown',
|
|
||||||
retryable: error.retryable || false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// Only add stack if it exists
|
|
||||||
if (error.stack) {
|
|
||||||
logEntry.error.stack = error.stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Console logging with color
|
|
||||||
const prefix = error.retryable ? '⚠️' : '❌';
|
|
||||||
const color = error.retryable ? chalk.yellow : chalk.red;
|
|
||||||
console.log(color(`${prefix} ${contextMsg}:`));
|
|
||||||
console.log(color(` ${error.message}`));
|
|
||||||
|
|
||||||
if (error.context && Object.keys(error.context).length > 0) {
|
|
||||||
console.log(chalk.gray(` Context: ${JSON.stringify(error.context)}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// File logging (if source directory available)
|
|
||||||
if (sourceDir) {
|
|
||||||
try {
|
|
||||||
const logPath = path.join(sourceDir, 'error.log');
|
|
||||||
await fs.appendFile(logPath, JSON.stringify(logEntry) + '\n');
|
|
||||||
} catch (logErr) {
|
|
||||||
const errMsg = logErr instanceof Error ? logErr.message : String(logErr);
|
|
||||||
console.log(chalk.gray(` (Failed to write error log: ${errMsg})`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return logEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle tool execution errors
|
// Handle tool execution errors
|
||||||
export function handleToolError(
|
|
||||||
toolName: string,
|
|
||||||
error: Error & { code?: string }
|
|
||||||
): ToolErrorResult {
|
|
||||||
const isRetryable =
|
|
||||||
error.code === 'ECONNRESET' ||
|
|
||||||
error.code === 'ETIMEDOUT' ||
|
|
||||||
error.code === 'ENOTFOUND';
|
|
||||||
|
|
||||||
return {
|
|
||||||
tool: toolName,
|
|
||||||
output: `Error: ${error.message}`,
|
|
||||||
status: 'error',
|
|
||||||
duration: 0,
|
|
||||||
success: false,
|
|
||||||
error: new PentestError(
|
|
||||||
`${toolName} execution failed: ${error.message}`,
|
|
||||||
'tool',
|
|
||||||
isRetryable,
|
|
||||||
{ toolName, originalError: error.message, errorCode: error.code }
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle prompt loading errors
|
// Handle prompt loading errors
|
||||||
export function handlePromptError(
|
export function handlePromptError(
|
||||||
promptName: string,
|
promptName: string,
|
||||||
@@ -181,21 +102,6 @@ export function isRetryableError(error: Error): boolean {
|
|||||||
return RETRYABLE_PATTERNS.some((pattern) => message.includes(pattern));
|
return RETRYABLE_PATTERNS.some((pattern) => message.includes(pattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rate limit errors get longer base delay (30s) vs standard exponential backoff (2s)
|
|
||||||
export function getRetryDelay(error: Error, attempt: number): number {
|
|
||||||
const message = error.message.toLowerCase();
|
|
||||||
|
|
||||||
// Rate limiting gets longer delays
|
|
||||||
if (message.includes('rate limit') || message.includes('429')) {
|
|
||||||
return Math.min(30000 + attempt * 10000, 120000); // 30s, 40s, 50s, max 2min
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exponential backoff with jitter for other retryable errors
|
|
||||||
const baseDelay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
|
|
||||||
const jitter = Math.random() * 1000; // 0-1s random
|
|
||||||
return Math.min(baseDelay + jitter, 30000); // Max 30s
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classifies errors for Temporal workflow retry behavior.
|
* Classifies errors for Temporal workflow retry behavior.
|
||||||
* Returns error type and whether Temporal should retry.
|
* Returns error type and whether Temporal should retry.
|
||||||
@@ -204,7 +110,7 @@ export function getRetryDelay(error: Error, attempt: number): number {
|
|||||||
* - Retryable errors: Temporal retries with configured backoff
|
* - Retryable errors: Temporal retries with configured backoff
|
||||||
* - Non-retryable errors: Temporal fails immediately
|
* - Non-retryable errors: Temporal fails immediately
|
||||||
*/
|
*/
|
||||||
export function classifyErrorForTemporal(error: unknown): TemporalErrorClassification {
|
export function classifyErrorForTemporal(error: unknown): { type: string; retryable: boolean } {
|
||||||
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
||||||
|
|
||||||
// === BILLING ERRORS (Retryable with long backoff) ===
|
// === BILLING ERRORS (Retryable with long backoff) ===
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import type { AgentName } from './types/index.js';
|
import type { AgentName } from './types/index.js';
|
||||||
|
|
||||||
// Agent definition interface
|
// Agent definition interface
|
||||||
export interface AgentDefinition {
|
interface AgentDefinition {
|
||||||
name: AgentName;
|
name: AgentName;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
prerequisites: AgentName[];
|
prerequisites: AgentName[];
|
||||||
|
|||||||
@@ -181,9 +181,7 @@ async function runAgentActivity(
|
|||||||
agentName, // description
|
agentName, // description
|
||||||
agentName,
|
agentName,
|
||||||
chalk.cyan,
|
chalk.cyan,
|
||||||
sessionMetadata,
|
auditSession
|
||||||
auditSession,
|
|
||||||
attemptNumber
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// 6.5. Sanity check: Detect spending cap that slipped through all detection layers
|
// 6.5. Sanity check: Detect spending cap that slipped through all detection layers
|
||||||
|
|||||||
+1
-3
@@ -29,10 +29,8 @@ export interface Rules {
|
|||||||
|
|
||||||
export type LoginType = 'form' | 'sso' | 'api' | 'basic';
|
export type LoginType = 'form' | 'sso' | 'api' | 'basic';
|
||||||
|
|
||||||
export type SuccessConditionType = 'url' | 'cookie' | 'element' | 'redirect';
|
|
||||||
|
|
||||||
export interface SuccessCondition {
|
export interface SuccessCondition {
|
||||||
type: SuccessConditionType;
|
type: 'url' | 'cookie' | 'element' | 'redirect';
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,6 @@
|
|||||||
// it under the terms of the GNU Affero General Public License version 3
|
// it under the terms of the GNU Affero General Public License version 3
|
||||||
// as published by the Free Software Foundation.
|
// as published by the Free Software Foundation.
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import { formatDuration } from './formatting.js';
|
|
||||||
|
|
||||||
// Timing utilities
|
// Timing utilities
|
||||||
|
|
||||||
export class Timer {
|
export class Timer {
|
||||||
@@ -59,52 +56,3 @@ export const costResults: CostResults = {
|
|||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to display comprehensive timing summary
|
|
||||||
const displayTimingSummary = (): void => {
|
|
||||||
if (!timingResults.total) {
|
|
||||||
console.log(chalk.yellow('No timing data available'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalDuration = timingResults.total.stop();
|
|
||||||
|
|
||||||
console.log(chalk.cyan.bold('\n⏱️ TIMING SUMMARY'));
|
|
||||||
console.log(chalk.gray('─'.repeat(60)));
|
|
||||||
|
|
||||||
// Total execution time
|
|
||||||
console.log(chalk.cyan(`📊 Total Execution Time: ${formatDuration(totalDuration)}`));
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
// Agent breakdown
|
|
||||||
if (Object.keys(timingResults.agents).length > 0) {
|
|
||||||
console.log(chalk.magenta.bold('🤖 Agent Breakdown:'));
|
|
||||||
let agentTotal = 0;
|
|
||||||
for (const [agent, duration] of Object.entries(timingResults.agents)) {
|
|
||||||
const percentage = ((duration / totalDuration) * 100).toFixed(1);
|
|
||||||
const displayName = agent.replace(/-/g, ' ');
|
|
||||||
console.log(
|
|
||||||
chalk.magenta(
|
|
||||||
` ${displayName.padEnd(20)} ${formatDuration(duration).padStart(8)} (${percentage}%)`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
agentTotal += duration;
|
|
||||||
}
|
|
||||||
console.log(
|
|
||||||
chalk.gray(
|
|
||||||
` ${'Agents Total'.padEnd(20)} ${formatDuration(agentTotal).padStart(8)} (${((agentTotal / totalDuration) * 100).toFixed(1)}%)`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cost breakdown
|
|
||||||
if (Object.keys(costResults.agents).length > 0) {
|
|
||||||
console.log(chalk.green.bold('\n💰 Cost Breakdown:'));
|
|
||||||
for (const [agent, cost] of Object.entries(costResults.agents)) {
|
|
||||||
const displayName = agent.replace(/-/g, ' ');
|
|
||||||
console.log(chalk.green(` ${displayName.padEnd(20)} $${cost.toFixed(4).padStart(8)}`));
|
|
||||||
}
|
|
||||||
console.log(chalk.gray(` ${'Total Cost'.padEnd(20)} $${costResults.total.toFixed(4).padStart(8)}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(chalk.gray('─'.repeat(60)));
|
|
||||||
};
|
|
||||||
|
|||||||
+5
-5
@@ -33,11 +33,11 @@
|
|||||||
"exactOptionalPropertyTypes": true,
|
"exactOptionalPropertyTypes": true,
|
||||||
|
|
||||||
// Style Options
|
// Style Options
|
||||||
// "noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
// "noImplicitOverride": true,
|
"noImplicitOverride": true,
|
||||||
// "noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
// "noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
// "noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
// "noPropertyAccessFromIndexSignature": true,
|
// "noPropertyAccessFromIndexSignature": true,
|
||||||
|
|
||||||
// Recommended Options
|
// Recommended Options
|
||||||
|
|||||||
Reference in New Issue
Block a user