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
|
# Monitor
|
||||||
./shannon logs # Real-time worker logs
|
./shannon logs # Real-time worker logs
|
||||||
./shannon query ID=<workflow-id> # Query workflow progress
|
|
||||||
# Temporal Web UI: http://localhost:8233
|
# Temporal Web UI: http://localhost:8233
|
||||||
|
|
||||||
# Stop
|
# Stop
|
||||||
@@ -57,8 +56,6 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig
|
|||||||
- `src/temporal/worker.ts` — Worker entry point
|
- `src/temporal/worker.ts` — Worker entry point
|
||||||
- `src/temporal/client.ts` — CLI client for starting workflows
|
- `src/temporal/client.ts` — CLI client for starting workflows
|
||||||
- `src/temporal/shared.ts` — Types, interfaces, query definitions
|
- `src/temporal/shared.ts` — Types, interfaces, query definitions
|
||||||
- `src/temporal/query.ts` — Query tool for progress inspection
|
|
||||||
|
|
||||||
### Five-Phase Pipeline
|
### Five-Phase Pipeline
|
||||||
|
|
||||||
1. **Pre-Recon** (`pre-recon`) — External scans (nmap, subfinder, whatweb) + source code analysis
|
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": "docker compose -f docker/docker-compose.temporal.yml up temporal -d",
|
||||||
"temporal:server:stop": "docker compose -f docker/docker-compose.temporal.yml down",
|
"temporal:server:stop": "docker compose -f docker/docker-compose.temporal.yml down",
|
||||||
"temporal:worker": "node dist/temporal/worker.js",
|
"temporal:worker": "node dist/temporal/worker.js",
|
||||||
"temporal:start": "node dist/temporal/client.js",
|
"temporal:start": "node dist/temporal/client.js"
|
||||||
"temporal:query": "node dist/temporal/query.js"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/claude-agent-sdk": "^0.2.38",
|
"@anthropic-ai/claude-agent-sdk": "^0.2.38",
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ Usage:
|
|||||||
./shannon start URL=<url> REPO=<name> Start a pentest workflow
|
./shannon start URL=<url> REPO=<name> Start a pentest workflow
|
||||||
./shannon workspaces List all workspaces
|
./shannon workspaces List all workspaces
|
||||||
./shannon logs ID=<workflow-id> Tail logs for a specific workflow
|
./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 stop Stop all containers
|
||||||
./shannon help Show this help message
|
./shannon help Show this help message
|
||||||
|
|
||||||
@@ -60,7 +59,6 @@ Examples:
|
|||||||
./shannon start URL=https://example.com REPO=repo-name OUTPUT=./my-reports
|
./shannon start URL=https://example.com REPO=repo-name OUTPUT=./my-reports
|
||||||
./shannon workspaces
|
./shannon workspaces
|
||||||
./shannon logs ID=example.com_shannon-1234567890
|
./shannon logs ID=example.com_shannon-1234567890
|
||||||
./shannon query ID=shannon-1234567890
|
|
||||||
./shannon stop CLEAN=true
|
./shannon stop CLEAN=true
|
||||||
|
|
||||||
Monitor workflows at http://localhost:8233
|
Monitor workflows at http://localhost:8233
|
||||||
@@ -287,24 +285,11 @@ cmd_logs() {
|
|||||||
echo " - Workflow hasn't started yet"
|
echo " - Workflow hasn't started yet"
|
||||||
echo " - Workflow ID is incorrect"
|
echo " - Workflow ID is incorrect"
|
||||||
echo ""
|
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
|
exit 1
|
||||||
fi
|
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() {
|
cmd_workspaces() {
|
||||||
# Ensure containers are running (need worker to execute node)
|
# Ensure containers are running (need worker to execute node)
|
||||||
ensure_containers
|
ensure_containers
|
||||||
@@ -333,10 +318,6 @@ case "${1:-help}" in
|
|||||||
shift
|
shift
|
||||||
cmd_logs "$@"
|
cmd_logs "$@"
|
||||||
;;
|
;;
|
||||||
query)
|
|
||||||
shift
|
|
||||||
cmd_query "$@"
|
|
||||||
;;
|
|
||||||
workspaces)
|
workspaces)
|
||||||
shift
|
shift
|
||||||
cmd_workspaces
|
cmd_workspaces
|
||||||
|
|||||||
@@ -326,7 +326,6 @@ async function startPipeline(): Promise<void> {
|
|||||||
console.log(chalk.bold('Monitor progress:'));
|
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(' 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(' Logs: ') + chalk.gray(`./shannon logs ID=${workflowId}`));
|
||||||
console.log(chalk.white(' Query: ') + chalk.gray(`./shannon query ID=${workflowId}`));
|
|
||||||
console.log();
|
console.log();
|
||||||
console.log(chalk.bold('Output:'));
|
console.log(chalk.bold('Output:'));
|
||||||
console.log(chalk.white(' Reports: ') + chalk.cyan(outputDir));
|
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