mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-06-02 05:31:43 +02:00
refactor: remove ./shannon query CLI command
Query functionality is redundant with the Temporal Web UI at http://localhost:8233. Removes query.ts, CLI handler, npm script, and all documentation references.
This commit is contained in:
@@ -26,7 +26,6 @@ git clone https://github.com/org/repo.git ./repos/my-repo
|
||||
|
||||
# Monitor
|
||||
./shannon logs # Real-time worker logs
|
||||
./shannon query ID=<workflow-id> # Query workflow progress
|
||||
# Temporal Web UI: http://localhost:8233
|
||||
|
||||
# Stop
|
||||
@@ -57,8 +56,6 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig
|
||||
- `src/temporal/worker.ts` — Worker entry point
|
||||
- `src/temporal/client.ts` — CLI client for starting workflows
|
||||
- `src/temporal/shared.ts` — Types, interfaces, query definitions
|
||||
- `src/temporal/query.ts` — Query tool for progress inspection
|
||||
|
||||
### Five-Phase Pipeline
|
||||
|
||||
1. **Pre-Recon** (`pre-recon`) — External scans (nmap, subfinder, whatweb) + source code analysis
|
||||
|
||||
+1
-2
@@ -7,8 +7,7 @@
|
||||
"temporal:server": "docker compose -f docker/docker-compose.temporal.yml up temporal -d",
|
||||
"temporal:server:stop": "docker compose -f docker/docker-compose.temporal.yml down",
|
||||
"temporal:worker": "node dist/temporal/worker.js",
|
||||
"temporal:start": "node dist/temporal/client.js",
|
||||
"temporal:query": "node dist/temporal/query.js"
|
||||
"temporal:start": "node dist/temporal/client.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.38",
|
||||
|
||||
@@ -38,7 +38,6 @@ Usage:
|
||||
./shannon start URL=<url> REPO=<name> Start a pentest workflow
|
||||
./shannon workspaces List all workspaces
|
||||
./shannon logs ID=<workflow-id> Tail logs for a specific workflow
|
||||
./shannon query ID=<workflow-id> Query workflow progress
|
||||
./shannon stop Stop all containers
|
||||
./shannon help Show this help message
|
||||
|
||||
@@ -60,7 +59,6 @@ Examples:
|
||||
./shannon start URL=https://example.com REPO=repo-name OUTPUT=./my-reports
|
||||
./shannon workspaces
|
||||
./shannon logs ID=example.com_shannon-1234567890
|
||||
./shannon query ID=shannon-1234567890
|
||||
./shannon stop CLEAN=true
|
||||
|
||||
Monitor workflows at http://localhost:8233
|
||||
@@ -287,24 +285,11 @@ cmd_logs() {
|
||||
echo " - Workflow hasn't started yet"
|
||||
echo " - Workflow ID is incorrect"
|
||||
echo ""
|
||||
echo "Check: ./shannon query ID=$ID for workflow details"
|
||||
echo "Check the Temporal Web UI at http://localhost:8233 for workflow details"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_query() {
|
||||
parse_args "$@"
|
||||
|
||||
if [ -z "$ID" ]; then
|
||||
echo "ERROR: ID is required"
|
||||
echo "Usage: ./shannon query ID=<workflow-id>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE exec -T worker \
|
||||
node dist/temporal/query.js "$ID"
|
||||
}
|
||||
|
||||
cmd_workspaces() {
|
||||
# Ensure containers are running (need worker to execute node)
|
||||
ensure_containers
|
||||
@@ -333,10 +318,6 @@ case "${1:-help}" in
|
||||
shift
|
||||
cmd_logs "$@"
|
||||
;;
|
||||
query)
|
||||
shift
|
||||
cmd_query "$@"
|
||||
;;
|
||||
workspaces)
|
||||
shift
|
||||
cmd_workspaces
|
||||
|
||||
@@ -326,7 +326,6 @@ async function startPipeline(): Promise<void> {
|
||||
console.log(chalk.bold('Monitor progress:'));
|
||||
console.log(chalk.white(' Web UI: ') + chalk.blue(`http://localhost:8233/namespaces/default/workflows/${workflowId}`));
|
||||
console.log(chalk.white(' Logs: ') + chalk.gray(`./shannon logs ID=${workflowId}`));
|
||||
console.log(chalk.white(' Query: ') + chalk.gray(`./shannon query ID=${workflowId}`));
|
||||
console.log();
|
||||
console.log(chalk.bold('Output:'));
|
||||
console.log(chalk.white(' Reports: ') + chalk.cyan(outputDir));
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* Temporal query tool for inspecting Shannon workflow progress.
|
||||
*
|
||||
* Queries a running or completed workflow and displays its state.
|
||||
*
|
||||
* Usage:
|
||||
* npm run temporal:query -- <workflowId>
|
||||
* # or
|
||||
* node dist/temporal/query.js <workflowId>
|
||||
*
|
||||
* Environment:
|
||||
* TEMPORAL_ADDRESS - Temporal server address (default: localhost:7233)
|
||||
*/
|
||||
|
||||
import { Connection, Client } from '@temporalio/client';
|
||||
import dotenv from 'dotenv';
|
||||
import chalk from 'chalk';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
// Query name must match the one defined in workflows.ts
|
||||
const PROGRESS_QUERY = 'getProgress';
|
||||
|
||||
// Types duplicated from shared.ts to avoid importing workflow APIs
|
||||
interface AgentMetrics {
|
||||
durationMs: number;
|
||||
inputTokens: number | null;
|
||||
outputTokens: number | null;
|
||||
costUsd: number | null;
|
||||
numTurns: number | null;
|
||||
model?: string | undefined;
|
||||
}
|
||||
|
||||
interface PipelineProgress {
|
||||
status: 'running' | 'completed' | 'failed';
|
||||
currentPhase: string | null;
|
||||
currentAgent: string | null;
|
||||
completedAgents: string[];
|
||||
failedAgent: string | null;
|
||||
error: string | null;
|
||||
startTime: number;
|
||||
agentMetrics: Record<string, AgentMetrics>;
|
||||
workflowId: string;
|
||||
elapsedMs: number;
|
||||
}
|
||||
|
||||
function showUsage(): void {
|
||||
console.log(chalk.cyan.bold('\nShannon Temporal Query Tool'));
|
||||
console.log(chalk.gray('Query progress of a running workflow\n'));
|
||||
console.log(chalk.yellow('Usage:'));
|
||||
console.log(' node dist/temporal/query.js <workflowId>\n');
|
||||
console.log(chalk.yellow('Examples:'));
|
||||
console.log(' node dist/temporal/query.js shannon-1704672000000\n');
|
||||
}
|
||||
|
||||
function getStatusColor(status: string): string {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return chalk.yellow(status);
|
||||
case 'completed':
|
||||
return chalk.green(status);
|
||||
case 'failed':
|
||||
return chalk.red(status);
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
const seconds = Math.floor(ms / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${minutes % 60}m`;
|
||||
} else if (minutes > 0) {
|
||||
return `${minutes}m ${seconds % 60}s`;
|
||||
}
|
||||
return `${seconds}s`;
|
||||
}
|
||||
|
||||
async function queryWorkflow(): Promise<void> {
|
||||
const workflowId = process.argv[2];
|
||||
|
||||
if (!workflowId || workflowId === '--help' || workflowId === '-h') {
|
||||
showUsage();
|
||||
process.exit(workflowId ? 0 : 1);
|
||||
}
|
||||
|
||||
const address = process.env.TEMPORAL_ADDRESS || 'localhost:7233';
|
||||
|
||||
const connection = await Connection.connect({ address });
|
||||
const client = new Client({ connection });
|
||||
|
||||
try {
|
||||
const handle = client.workflow.getHandle(workflowId);
|
||||
const progress = await handle.query<PipelineProgress>(PROGRESS_QUERY);
|
||||
|
||||
console.log(chalk.cyan.bold('\nWorkflow Progress'));
|
||||
console.log(chalk.gray('\u2500'.repeat(40)));
|
||||
console.log(`${chalk.white('Workflow ID:')} ${progress.workflowId}`);
|
||||
console.log(`${chalk.white('Status:')} ${getStatusColor(progress.status)}`);
|
||||
console.log(
|
||||
`${chalk.white('Current Phase:')} ${progress.currentPhase || 'none'}`
|
||||
);
|
||||
console.log(
|
||||
`${chalk.white('Current Agent:')} ${progress.currentAgent || 'none'}`
|
||||
);
|
||||
console.log(`${chalk.white('Elapsed:')} ${formatDuration(progress.elapsedMs)}`);
|
||||
console.log(
|
||||
`${chalk.white('Completed:')} ${progress.completedAgents.length}/13 agents`
|
||||
);
|
||||
|
||||
if (progress.completedAgents.length > 0) {
|
||||
console.log(chalk.gray('\nCompleted agents:'));
|
||||
for (const agent of progress.completedAgents) {
|
||||
const metrics = progress.agentMetrics[agent];
|
||||
const duration = metrics ? formatDuration(metrics.durationMs) : 'unknown';
|
||||
const cost = metrics?.costUsd ? `$${metrics.costUsd.toFixed(4)}` : '';
|
||||
const model = metrics?.model ? ` [${metrics.model}]` : '';
|
||||
console.log(
|
||||
chalk.green(` - ${agent}`) +
|
||||
chalk.blue(model) +
|
||||
chalk.gray(` (${duration}${cost ? ', ' + cost : ''})`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (progress.error) {
|
||||
console.log(chalk.red(`\nError: ${progress.error}`));
|
||||
console.log(chalk.red(`Failed agent: ${progress.failedAgent}`));
|
||||
}
|
||||
|
||||
console.log();
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (err.message?.includes('not found')) {
|
||||
console.log(chalk.red(`Workflow not found: ${workflowId}`));
|
||||
} else {
|
||||
console.error(chalk.red('Query failed:'), err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
queryWorkflow().catch((err) => {
|
||||
console.error(chalk.red('Query error:'), err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user