mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-02-12 17:22:50 +00:00
refactor: simplify session ID handling and improve Taskfile options
- Include hostname in workflow ID for better audit log organization - Extract sanitizeHostname utility to audit/utils.ts for reuse - Remove unused generateSessionLogPath and buildLogFilePath functions - Simplify Taskfile with CONFIG/OUTPUT/CLEAN named parameters
This commit is contained in:
27
Taskfile.yml
27
Taskfile.yml
@@ -24,11 +24,18 @@ tasks:
|
||||
echo " task stop Stop all containers"
|
||||
echo " task help Show this help message"
|
||||
echo ""
|
||||
echo "Options for 'start':"
|
||||
echo " CONFIG=<path> Configuration file (YAML)"
|
||||
echo " OUTPUT=<path> Output directory for reports"
|
||||
echo ""
|
||||
echo "Options for 'stop':"
|
||||
echo " CLEAN=true Remove all data including volumes"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " task start URL=https://example.com REPO=/path/to/repo"
|
||||
echo " task start URL=https://example.com REPO=/path/to/repo -- --pipeline-testing"
|
||||
echo " task start URL=https://example.com REPO=/path/to/repo -- --config ./config.yaml"
|
||||
echo " task start URL=https://example.com REPO=/path/to/repo CONFIG=./config.yaml"
|
||||
echo " task query ID=shannon-1234567890"
|
||||
echo " task stop CLEAN=true"
|
||||
echo ""
|
||||
echo "Monitor workflows at http://localhost:8233"
|
||||
|
||||
@@ -52,8 +59,12 @@ tasks:
|
||||
sleep 2
|
||||
done
|
||||
- |
|
||||
ARGS=""
|
||||
{{if .CONFIG}}ARGS="$ARGS --config {{.CONFIG}}"{{end}}
|
||||
{{if .OUTPUT}}ARGS="$ARGS --output {{.OUTPUT}}"{{end}}
|
||||
{{if eq .PIPELINE_TESTING "true"}}ARGS="$ARGS --pipeline-testing"{{end}}
|
||||
docker compose -f {{.COMPOSE_FILE}} exec -T worker \
|
||||
node dist/temporal/client.js "{{.URL}}" "/target-repo" {{.CLI_ARGS}}
|
||||
node dist/temporal/client.js "{{.URL}}" "/target-repo" $ARGS {{.CLI_ARGS}}
|
||||
|
||||
logs:
|
||||
desc: View real-time worker logs
|
||||
@@ -76,8 +87,8 @@ tasks:
|
||||
silent: true
|
||||
cmds:
|
||||
- |
|
||||
if [ "{{.CLI_ARGS}}" = "--clean" ]; then
|
||||
docker compose -f {{.COMPOSE_FILE}} down -v
|
||||
else
|
||||
docker compose -f {{.COMPOSE_FILE}} down
|
||||
fi
|
||||
{{if eq .CLEAN "true"}}
|
||||
docker compose -f {{.COMPOSE_FILE}} down -v
|
||||
{{else}}
|
||||
docker compose -f {{.COMPOSE_FILE}} down
|
||||
{{end}}
|
||||
|
||||
@@ -15,7 +15,6 @@ import { timingResults, Timer } from '../utils/metrics.js';
|
||||
import { formatTimestamp } from '../utils/formatting.js';
|
||||
import { createGitCheckpoint, commitGitSuccess, rollbackGitWorkspace, getGitCommitHash } from '../utils/git-manager.js';
|
||||
import { AGENT_VALIDATORS, MCP_AGENT_MAPPING } from '../constants.js';
|
||||
import { generateSessionLogPath } from '../session-manager.js';
|
||||
import { AuditSession } from '../audit/index.js';
|
||||
import { createShannonHelperServer } from '../../mcp-server/dist/index.js';
|
||||
import type { SessionMetadata } from '../audit/utils.js';
|
||||
@@ -39,7 +38,6 @@ export interface ClaudePromptResult {
|
||||
cost: number;
|
||||
partialCost?: number;
|
||||
apiErrorDetected?: boolean;
|
||||
logFile?: string;
|
||||
error?: string;
|
||||
errorType?: string;
|
||||
prompt?: string;
|
||||
@@ -215,10 +213,7 @@ export async function runClaudePrompt(
|
||||
);
|
||||
const auditLogger = createAuditLogger(auditSession);
|
||||
|
||||
const logFilePath = buildLogFilePath(sessionMetadata, execContext.agentKey, attemptNumber);
|
||||
if (!logFilePath) {
|
||||
console.log(chalk.blue(` Running Claude Code: ${description}...`));
|
||||
}
|
||||
console.log(chalk.blue(` Running Claude Code: ${description}...`));
|
||||
|
||||
const mcpServers = buildMcpServers(sourceDir, agentName);
|
||||
const options = {
|
||||
@@ -262,7 +257,7 @@ export async function runClaudePrompt(
|
||||
|
||||
progress.finish(formatCompletionMessage(execContext, description, turnCount, duration));
|
||||
|
||||
const returnData: ClaudePromptResult = {
|
||||
return {
|
||||
result,
|
||||
success: true,
|
||||
duration,
|
||||
@@ -271,10 +266,6 @@ export async function runClaudePrompt(
|
||||
partialCost: totalCost,
|
||||
apiErrorDetected
|
||||
};
|
||||
if (logFilePath) {
|
||||
returnData.logFile = logFilePath;
|
||||
}
|
||||
return returnData;
|
||||
|
||||
} catch (error) {
|
||||
const duration = timer.stop();
|
||||
@@ -299,18 +290,6 @@ export async function runClaudePrompt(
|
||||
}
|
||||
}
|
||||
|
||||
function buildLogFilePath(
|
||||
sessionMetadata: SessionMetadata | null,
|
||||
agentKey: string,
|
||||
attemptNumber: number
|
||||
): string | null {
|
||||
if (!sessionMetadata || !sessionMetadata.webUrl || !sessionMetadata.id) {
|
||||
return null;
|
||||
}
|
||||
const timestamp = formatTimestamp().replace(/T/, '_').replace(/[:.]/g, '-').slice(0, 19);
|
||||
const logDir = generateSessionLogPath(sessionMetadata.webUrl, sessionMetadata.id);
|
||||
return path.join(logDir, `${timestamp}_${agentKey}_attempt-${attemptNumber}.log`);
|
||||
}
|
||||
|
||||
interface MessageLoopResult {
|
||||
turnCount: number;
|
||||
|
||||
@@ -31,12 +31,18 @@ export interface SessionMetadata {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate standardized session identifier: {hostname}_{sessionId}
|
||||
* Extract and sanitize hostname from URL for use in identifiers
|
||||
*/
|
||||
export function sanitizeHostname(url: string): string {
|
||||
return new URL(url).hostname.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate standardized session identifier from workflow ID
|
||||
* Workflow IDs already contain hostname, so we use them directly
|
||||
*/
|
||||
export function generateSessionIdentifier(sessionMetadata: SessionMetadata): string {
|
||||
const { id, webUrl } = sessionMetadata;
|
||||
const hostname = new URL(webUrl).hostname.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||
return `${hostname}_${id}`;
|
||||
return sessionMetadata.id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -126,10 +126,4 @@ export const AGENT_PHASE_MAP: Readonly<Record<AgentName, PhaseName>> = Object.fr
|
||||
'report': 'reporting',
|
||||
});
|
||||
|
||||
// Generate a session-based log folder path (used by claude-executor.ts)
|
||||
export const generateSessionLogPath = (webUrl: string, sessionId: string): string => {
|
||||
const hostname = new URL(webUrl).hostname.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||
const sessionFolderName = `${hostname}_${sessionId}`;
|
||||
return path.join(process.cwd(), 'agent-logs', sessionFolderName);
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import { Connection, Client } from '@temporalio/client';
|
||||
import dotenv from 'dotenv';
|
||||
import chalk from 'chalk';
|
||||
import { displaySplashScreen } from '../splash-screen.js';
|
||||
import { sanitizeHostname } from '../audit/utils.js';
|
||||
// Import types only - these don't pull in workflow runtime code
|
||||
import type { PipelineInput, PipelineState, PipelineProgress } from './shared.js';
|
||||
|
||||
@@ -126,7 +127,8 @@ async function startPipeline(): Promise<void> {
|
||||
const client = new Client({ connection });
|
||||
|
||||
try {
|
||||
const workflowId = customWorkflowId || `shannon-${Date.now()}`;
|
||||
const hostname = sanitizeHostname(webUrl);
|
||||
const workflowId = customWorkflowId || `${hostname}_shannon-${Date.now()}`;
|
||||
|
||||
const input: PipelineInput = {
|
||||
webUrl,
|
||||
|
||||
Reference in New Issue
Block a user