claude-flow-novice 2.16.0 → 2.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
- package/.claude/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
- package/.claude/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
- package/.claude/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
- package/.claude/commands/CFN_LOOP_FRONTEND.md +1 -1
- package/.claude/commands/cfn-loop-cli.md +124 -46
- package/.claude/commands/cfn-loop-frontend.md +1 -1
- package/.claude/commands/cfn-loop-task.md +2 -2
- package/.claude/commands/deprecated/cfn-loop.md +2 -2
- package/.claude/hooks/cfn-invoke-post-edit.sh +31 -5
- package/.claude/hooks/cfn-post-edit.config.json +9 -2
- package/.claude/root-claude-distribute/CFN-CLAUDE.md +1 -1
- package/.claude/skills/cfn-backlog-management/SKILL.md +1 -1
- package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +1 -1
- package/claude-assets/agents/cfn-dev-team/analysts/root-cause-analyst.md +2 -2
- package/claude-assets/agents/cfn-dev-team/architecture/base-template-generator.md +1 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +2 -2
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +2 -2
- package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +2 -2
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/documentation/pseudocode.md +1 -1
- package/claude-assets/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +1 -1
- package/claude-assets/agents/cfn-dev-team/product-owners/cto-agent.md +1 -1
- package/claude-assets/agents/cfn-dev-team/product-owners/power-user-persona.md +1 -1
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/agent-builder.md +11 -0
- package/claude-assets/agents/cfn-dev-team/utility/analyst.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/claude-code-expert.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -1
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +1 -1
- package/claude-assets/agents/custom/cfn-docker-expert.md +1 -0
- package/claude-assets/agents/custom/cfn-loops-cli-expert.md +326 -17
- package/claude-assets/agents/custom/cfn-redis-operations.md +529 -529
- package/claude-assets/agents/custom/cfn-system-expert.md +1 -1
- package/claude-assets/agents/custom/trigger-dev-expert.md +369 -0
- package/claude-assets/agents/docker-team/micro-sprint-planner.md +747 -747
- package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
- package/claude-assets/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
- package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
- package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
- package/claude-assets/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
- package/claude-assets/commands/CFN_LOOP_FRONTEND.md +1 -1
- package/claude-assets/commands/cfn-loop-cli.md +124 -46
- package/claude-assets/commands/cfn-loop-frontend.md +1 -1
- package/claude-assets/commands/cfn-loop-task.md +2 -2
- package/claude-assets/commands/deprecated/cfn-loop.md +2 -2
- package/claude-assets/hooks/GIT-HOOKS-USAGE-EXAMPLES.md +116 -0
- package/claude-assets/hooks/README-GIT-HOOKS.md +443 -0
- package/claude-assets/hooks/cfn-invoke-post-edit.sh +31 -5
- package/claude-assets/hooks/cfn-post-edit.config.json +9 -2
- package/claude-assets/hooks/install-git-hooks.sh +243 -0
- package/claude-assets/hooks/subagent-start.sh +98 -0
- package/claude-assets/hooks/subagent-stop.sh +93 -0
- package/claude-assets/hooks/validators/credential-scanner.sh +172 -0
- package/claude-assets/root-claude-distribute/CFN-CLAUDE.md +1 -1
- package/claude-assets/skills/cfn-backlog-management/SKILL.md +1 -1
- package/claude-assets/skills/cfn-dependency-ingestion/SKILL.md +41 -13
- package/claude-assets/skills/cfn-dependency-ingestion/ingest.sh +237 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/cli-mode-dependencies.txt +73 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/shared-dependencies.txt +57 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-dev-dependencies.txt +82 -0
- package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-mode-dependencies.txt +80 -0
- package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +14 -4
- package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +1 -1
- package/claude-assets/skills/cfn-provider-routing/SKILL.md +23 -0
- package/claude-assets/skills/docker-build/build.sh +1 -1
- package/dist/agent/skill-mcp-selector.js +2 -1
- package/dist/agent/skill-mcp-selector.js.map +1 -1
- package/dist/agents/agent-loader.js +165 -146
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/cli/agent-executor.js +470 -26
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +2 -2
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +7 -4
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/agent-spawner.js +51 -4
- package/dist/cli/agent-spawner.js.map +1 -1
- package/dist/cli/agent-token-manager.js +2 -1
- package/dist/cli/agent-token-manager.js.map +1 -1
- package/dist/cli/anthropic-client.js +117 -11
- package/dist/cli/anthropic-client.js.map +1 -1
- package/dist/cli/cfn-context.js +2 -1
- package/dist/cli/cfn-context.js.map +1 -1
- package/dist/cli/cfn-metrics.js +2 -1
- package/dist/cli/cfn-metrics.js.map +1 -1
- package/dist/cli/cfn-redis.js +2 -1
- package/dist/cli/cfn-redis.js.map +1 -1
- package/dist/cli/cli-agent-context.js +2 -0
- package/dist/cli/cli-agent-context.js.map +1 -1
- package/dist/cli/config-manager.js +4 -252
- package/dist/cli/config-manager.js.map +1 -1
- package/dist/cli/conversation-fork-cleanup.js +2 -1
- package/dist/cli/conversation-fork-cleanup.js.map +1 -1
- package/dist/cli/conversation-fork.js +2 -1
- package/dist/cli/conversation-fork.js.map +1 -1
- package/dist/cli/coordination/agent-messaging.js +415 -0
- package/dist/cli/coordination/agent-messaging.js.map +1 -0
- package/dist/cli/coordination/wait-for-threshold.js +232 -0
- package/dist/cli/coordination/wait-for-threshold.js.map +1 -0
- package/dist/cli/iteration-history.js +2 -1
- package/dist/cli/iteration-history.js.map +1 -1
- package/dist/cli/process-lifecycle.js +5 -1
- package/dist/cli/process-lifecycle.js.map +1 -1
- package/dist/cli/spawn-agent-cli.js +41 -6
- package/dist/cli/spawn-agent-cli.js.map +1 -1
- package/dist/coordination/redis-waiting-mode.js +4 -0
- package/dist/coordination/redis-waiting-mode.js.map +1 -1
- package/dist/lib/artifact-registry.js +4 -0
- package/dist/lib/artifact-registry.js.map +1 -1
- package/dist/lib/connection-pool.js +390 -0
- package/dist/lib/connection-pool.js.map +1 -0
- package/dist/lib/environment-contract.js +258 -0
- package/dist/lib/environment-contract.js.map +1 -0
- package/dist/lib/query-optimizer.js +388 -0
- package/dist/lib/query-optimizer.js.map +1 -0
- package/dist/lib/result-cache.js +285 -0
- package/dist/lib/result-cache.js.map +1 -0
- package/dist/mcp/auth-middleware.js +2 -1
- package/dist/mcp/auth-middleware.js.map +1 -1
- package/dist/mcp/playwright-mcp-server-auth.js +2 -1
- package/dist/mcp/playwright-mcp-server-auth.js.map +1 -1
- package/package.json +3 -1
- package/scripts/build-agent-image.sh +1 -1
- package/scripts/cost-allocation-tracker.sh +632 -0
- package/scripts/docker-rebuild-all-agents.sh +2 -2
- package/scripts/reorganize-tests.sh +280 -0
- package/scripts/trigger-dev-setup.sh +12 -0
- package/tests/README.md +45 -0
- package/.claude/commands/cost-savings-status.md +0 -34
- package/.claude/commands/metrics-summary.md +0 -58
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +0 -768
- package/claude-assets/agents/custom/test-mcp-access.md +0 -24
- package/claude-assets/commands/cost-savings-status.md +0 -34
- package/claude-assets/commands/metrics-summary.md +0 -58
- package/tests/test-memory-leak-task-mode.sh +0 -435
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# cost-allocation-tracker.sh - Container cost tracking and allocation
|
|
4
|
+
#
|
|
5
|
+
# Purpose: Track costs across Docker containers by team, project, and agent type
|
|
6
|
+
# Usage: ./cost-allocation-tracker.sh [command] [options]
|
|
7
|
+
#
|
|
8
|
+
# Commands:
|
|
9
|
+
# daily-report <date> Generate daily cost report
|
|
10
|
+
# by-team <team> Show costs for specific team
|
|
11
|
+
# by-project <project> Show costs for specific project
|
|
12
|
+
# by-agent <agent-type> Show costs by agent type
|
|
13
|
+
# anomalies Find high-cost containers
|
|
14
|
+
# forecast <days> Project costs for next N days
|
|
15
|
+
# export-csv <output-file> Export to CSV for billing
|
|
16
|
+
# quota-check Verify team quotas
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# Source validation framework for safe arithmetic
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
source "$SCRIPT_DIR/lib/validation.sh"
|
|
23
|
+
|
|
24
|
+
# Configuration
|
|
25
|
+
COST_CPU_PER_HOUR=0.05 # $0.05 per CPU core per hour
|
|
26
|
+
COST_MEMORY_PER_GB_HOUR=0.10 # $0.10 per GB per hour
|
|
27
|
+
COST_DISK_PER_GB=0.01 # $0.01 per GB disk
|
|
28
|
+
COST_API_TOKEN_MULTIPLIER=0.50 # $0.50 per 1M tokens
|
|
29
|
+
|
|
30
|
+
# Provider costs (per 1M tokens)
|
|
31
|
+
COST_ZAI=0.50
|
|
32
|
+
COST_KIMI=2.00
|
|
33
|
+
COST_OPENROUTER=1.00
|
|
34
|
+
COST_ANTHROPIC=15.00
|
|
35
|
+
|
|
36
|
+
# Color output
|
|
37
|
+
RED='\033[0;31m'
|
|
38
|
+
GREEN='\033[0;32m'
|
|
39
|
+
YELLOW='\033[1;33m'
|
|
40
|
+
BLUE='\033[0;34m'
|
|
41
|
+
NC='\033[0m' # No Color
|
|
42
|
+
|
|
43
|
+
# Logging functions
|
|
44
|
+
log_info() {
|
|
45
|
+
echo -e "${BLUE}[INFO]${NC} $*"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
log_success() {
|
|
49
|
+
echo -e "${GREEN}[SUCCESS]${NC} $*"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
log_warn() {
|
|
53
|
+
echo -e "${YELLOW}[WARN]${NC} $*"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
log_error() {
|
|
57
|
+
echo -e "${RED}[ERROR]${NC} $*"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Calculate container cost
|
|
61
|
+
calculate_container_cost() {
|
|
62
|
+
local container_id=$1
|
|
63
|
+
local cpu_percent=$2
|
|
64
|
+
local memory_mb=$3
|
|
65
|
+
local runtime_seconds=${4:-3600} # Default to 1 hour
|
|
66
|
+
local provider=${5:-zai}
|
|
67
|
+
|
|
68
|
+
# Validate inputs
|
|
69
|
+
if ! validate_numeric "$cpu_percent" 0 ; then
|
|
70
|
+
log_error "Invalid CPU percent: $cpu_percent"
|
|
71
|
+
echo "0"
|
|
72
|
+
return 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if ! validate_numeric "$memory_mb" 0 ; then
|
|
76
|
+
log_error "Invalid memory MB: $memory_mb"
|
|
77
|
+
echo "0"
|
|
78
|
+
return 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
if ! validate_numeric "$runtime_seconds" 1 ; then
|
|
82
|
+
log_error "Invalid runtime seconds: $runtime_seconds"
|
|
83
|
+
echo "0"
|
|
84
|
+
return 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Assume CPU_PERCENT is for 1 hour execution
|
|
88
|
+
# Convert to actual CPU-hours (safe division)
|
|
89
|
+
local cpu_hours
|
|
90
|
+
cpu_hours=$(safe_divide "$cpu_percent" "100" 4) || return 1
|
|
91
|
+
cpu_hours=$(safe_arithmetic "$cpu_hours * ($runtime_seconds / 3600)") || return 1
|
|
92
|
+
|
|
93
|
+
local memory_gb
|
|
94
|
+
memory_gb=$(safe_divide "$memory_mb" "1024" 4) || return 1
|
|
95
|
+
|
|
96
|
+
local memory_hours
|
|
97
|
+
memory_hours=$(safe_arithmetic "$memory_gb * ($runtime_seconds / 3600)") || return 1
|
|
98
|
+
|
|
99
|
+
# Calculate infrastructure costs
|
|
100
|
+
local cpu_cost
|
|
101
|
+
cpu_cost=$(safe_arithmetic "$cpu_hours * $COST_CPU_PER_HOUR") || return 1
|
|
102
|
+
|
|
103
|
+
local memory_cost
|
|
104
|
+
memory_cost=$(safe_arithmetic "$memory_hours * $COST_MEMORY_PER_GB_HOUR") || return 1
|
|
105
|
+
|
|
106
|
+
local total_cost
|
|
107
|
+
total_cost=$(safe_arithmetic "$cpu_cost + $memory_cost") || return 1
|
|
108
|
+
|
|
109
|
+
echo "$total_cost"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Get API token cost for provider
|
|
113
|
+
get_api_cost() {
|
|
114
|
+
local provider=$1
|
|
115
|
+
local tokens=${2:-0}
|
|
116
|
+
|
|
117
|
+
# Validate token count
|
|
118
|
+
if ! validate_numeric "$tokens" 0 ; then
|
|
119
|
+
log_error "Invalid token count: $tokens"
|
|
120
|
+
echo "0"
|
|
121
|
+
return 1
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if [ "$tokens" -eq 0 ]; then
|
|
125
|
+
echo "0"
|
|
126
|
+
return 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
local cost=0
|
|
130
|
+
case "$provider" in
|
|
131
|
+
zai)
|
|
132
|
+
cost=$(safe_divide "$COST_ZAI" "1000000" 6) || return 1
|
|
133
|
+
cost=$(safe_arithmetic "$tokens * $cost") || return 1
|
|
134
|
+
;;
|
|
135
|
+
kimi)
|
|
136
|
+
cost=$(safe_divide "$COST_KIMI" "1000000" 6) || return 1
|
|
137
|
+
cost=$(safe_arithmetic "$tokens * $cost") || return 1
|
|
138
|
+
;;
|
|
139
|
+
openrouter)
|
|
140
|
+
cost=$(safe_divide "$COST_OPENROUTER" "1000000" 6) || return 1
|
|
141
|
+
cost=$(safe_arithmetic "$tokens * $cost") || return 1
|
|
142
|
+
;;
|
|
143
|
+
anthropic)
|
|
144
|
+
cost=$(safe_divide "$COST_ANTHROPIC" "1000000" 6) || return 1
|
|
145
|
+
cost=$(safe_arithmetic "$tokens * $cost") || return 1
|
|
146
|
+
;;
|
|
147
|
+
*)
|
|
148
|
+
cost=0
|
|
149
|
+
;;
|
|
150
|
+
esac
|
|
151
|
+
|
|
152
|
+
echo "$cost"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Extract label from container (sanitized for security)
|
|
156
|
+
# Returns: Sanitized label value or empty string if invalid/missing
|
|
157
|
+
get_container_label() {
|
|
158
|
+
local container_id=$1
|
|
159
|
+
local label=$2
|
|
160
|
+
|
|
161
|
+
# Get raw label value from Docker
|
|
162
|
+
local raw_label
|
|
163
|
+
raw_label=$(docker inspect "$container_id" --format="{{index .Config.Labels \"$label\" }}" 2>/dev/null || echo "")
|
|
164
|
+
|
|
165
|
+
# Return empty if no label found
|
|
166
|
+
if [ -z "$raw_label" ]; then
|
|
167
|
+
echo ""
|
|
168
|
+
return 0
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Sanitize label to prevent injection attacks (CVSS 7.5 mitigation)
|
|
172
|
+
local sanitized_label
|
|
173
|
+
if sanitized_label=$(sanitize_label "$raw_label" 2>/dev/null); then
|
|
174
|
+
echo "$sanitized_label"
|
|
175
|
+
else
|
|
176
|
+
# Invalid label - log warning and return empty
|
|
177
|
+
log_warn "Invalid label value for '$label' in container $container_id (rejected for security)"
|
|
178
|
+
echo ""
|
|
179
|
+
fi
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Get running container stats
|
|
183
|
+
get_running_container_stats() {
|
|
184
|
+
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" 2>/dev/null | tail -n +2 || true
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# Get exited container stats (from docker history)
|
|
188
|
+
get_exited_container_stats() {
|
|
189
|
+
docker ps -a --filter "status=exited" --format "table {{.ID}}\t{{.Label \"team\"}}\t{{.Label \"project\"}}" 2>/dev/null | tail -n +2 || true
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Daily cost report
|
|
193
|
+
daily_report() {
|
|
194
|
+
local date=${1:-$(date +%Y-%m-%d)}
|
|
195
|
+
|
|
196
|
+
log_info "Generating daily cost report for: $date"
|
|
197
|
+
|
|
198
|
+
echo ""
|
|
199
|
+
echo "=========================================="
|
|
200
|
+
echo "Daily Cost Report: $date"
|
|
201
|
+
echo "=========================================="
|
|
202
|
+
echo ""
|
|
203
|
+
|
|
204
|
+
# Get all containers (running and exited)
|
|
205
|
+
local total_cost=0
|
|
206
|
+
local total_containers=0
|
|
207
|
+
|
|
208
|
+
# Process running containers
|
|
209
|
+
echo "Running Containers:"
|
|
210
|
+
echo "---"
|
|
211
|
+
|
|
212
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
213
|
+
# Clean up CPU and memory values
|
|
214
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
215
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
216
|
+
|
|
217
|
+
local team=$(get_container_label "$container_id" "team")
|
|
218
|
+
local project=$(get_container_label "$container_id" "project")
|
|
219
|
+
local agent_type=$(get_container_label "$container_id" "agent-type")
|
|
220
|
+
local provider=$(get_container_label "$container_id" "provider" || echo "zai")
|
|
221
|
+
|
|
222
|
+
if [ -z "$team" ]; then
|
|
223
|
+
continue
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
local cost=$(calculate_container_cost "$container_id" "$cpu" "$mem" 3600 "$provider")
|
|
227
|
+
|
|
228
|
+
printf "%-12s %-30s %-20s CPU:%-6s MEM:%-8s Cost: \$%.4f\n" \
|
|
229
|
+
"$team" "$project" "$agent_type" "$cpu%" "$mem" "$cost"
|
|
230
|
+
|
|
231
|
+
total_cost=$(echo "scale=6; $total_cost + $cost" | bc)
|
|
232
|
+
total_containers=$((total_containers + 1))
|
|
233
|
+
done
|
|
234
|
+
|
|
235
|
+
echo ""
|
|
236
|
+
echo "Summary:"
|
|
237
|
+
echo "--------"
|
|
238
|
+
echo "Total containers: $total_containers"
|
|
239
|
+
echo "Total cost: \$$(printf '%.2f' "$total_cost")"
|
|
240
|
+
echo ""
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
# Costs by team
|
|
244
|
+
by_team() {
|
|
245
|
+
local team=${1:-all}
|
|
246
|
+
|
|
247
|
+
log_info "Generating cost report by team"
|
|
248
|
+
echo ""
|
|
249
|
+
|
|
250
|
+
declare -A team_costs
|
|
251
|
+
declare -A team_counts
|
|
252
|
+
|
|
253
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
254
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
255
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
256
|
+
|
|
257
|
+
local container_team=$(get_container_label "$container_id" "team")
|
|
258
|
+
local provider=$(get_container_label "$container_id" "provider" || echo "zai")
|
|
259
|
+
|
|
260
|
+
if [ -z "$container_team" ]; then
|
|
261
|
+
continue
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
if [ "$team" != "all" ] && [ "$container_team" != "$team" ]; then
|
|
265
|
+
continue
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
local cost=$(calculate_container_cost "$container_id" "$cpu" "$mem" 3600 "$provider")
|
|
269
|
+
|
|
270
|
+
team_costs["$container_team"]=$(echo "scale=6; ${team_costs[$container_team]:-0} + $cost" | bc)
|
|
271
|
+
team_counts["$container_team"]=$((${team_counts[$container_team]:-0} + 1))
|
|
272
|
+
done
|
|
273
|
+
|
|
274
|
+
echo "Team Cost Summary:"
|
|
275
|
+
echo "==================="
|
|
276
|
+
for t in "${!team_costs[@]}"; do
|
|
277
|
+
printf "%-20s: Count: %-3d Cost: \$%.2f\n" \
|
|
278
|
+
"$t" "${team_counts[$t]}" "${team_costs[$t]}"
|
|
279
|
+
done | sort -k4 -rn
|
|
280
|
+
|
|
281
|
+
echo ""
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
# Costs by project
|
|
285
|
+
by_project() {
|
|
286
|
+
local project=${1}
|
|
287
|
+
|
|
288
|
+
if [ -z "$project" ]; then
|
|
289
|
+
log_error "Project name required"
|
|
290
|
+
exit 1
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
log_info "Generating cost report for project: $project"
|
|
294
|
+
echo ""
|
|
295
|
+
|
|
296
|
+
local total_cost=0
|
|
297
|
+
local count=0
|
|
298
|
+
|
|
299
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
300
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
301
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
302
|
+
|
|
303
|
+
local container_project=$(get_container_label "$container_id" "project")
|
|
304
|
+
local team=$(get_container_label "$container_id" "team")
|
|
305
|
+
local agent_type=$(get_container_label "$container_id" "agent-type")
|
|
306
|
+
local provider=$(get_container_label "$container_id" "provider" || echo "zai")
|
|
307
|
+
|
|
308
|
+
if [ "$container_project" != "$project" ]; then
|
|
309
|
+
continue
|
|
310
|
+
fi
|
|
311
|
+
|
|
312
|
+
local cost=$(calculate_container_cost "$container_id" "$cpu" "$mem" 3600 "$provider")
|
|
313
|
+
|
|
314
|
+
printf "%-15s %-20s %-12s CPU:%-6s Cost: \$%.4f\n" \
|
|
315
|
+
"$team" "$agent_type" "$container_id" "$cpu%" "$cost"
|
|
316
|
+
|
|
317
|
+
total_cost=$(echo "scale=6; $total_cost + $cost" | bc)
|
|
318
|
+
count=$((count + 1))
|
|
319
|
+
done
|
|
320
|
+
|
|
321
|
+
echo ""
|
|
322
|
+
echo "Project Summary: $project"
|
|
323
|
+
echo "Containers: $count"
|
|
324
|
+
echo "Total cost: \$$(printf '%.2f' "$total_cost")"
|
|
325
|
+
echo ""
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# Costs by agent type
|
|
329
|
+
by_agent() {
|
|
330
|
+
local agent_type=${1}
|
|
331
|
+
|
|
332
|
+
if [ -z "$agent_type" ]; then
|
|
333
|
+
log_error "Agent type required"
|
|
334
|
+
exit 1
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
log_info "Generating cost report for agent: $agent_type"
|
|
338
|
+
echo ""
|
|
339
|
+
|
|
340
|
+
local total_cost=0
|
|
341
|
+
local count=0
|
|
342
|
+
|
|
343
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
344
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
345
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
346
|
+
|
|
347
|
+
local container_agent=$(get_container_label "$container_id" "agent-type")
|
|
348
|
+
local team=$(get_container_label "$container_id" "team")
|
|
349
|
+
local project=$(get_container_label "$container_id" "project")
|
|
350
|
+
local provider=$(get_container_label "$container_id" "provider" || echo "zai")
|
|
351
|
+
|
|
352
|
+
if [ "$container_agent" != "$agent_type" ]; then
|
|
353
|
+
continue
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
local cost=$(calculate_container_cost "$container_id" "$cpu" "$mem" 3600 "$provider")
|
|
357
|
+
|
|
358
|
+
printf "%-15s %-30s CPU:%-6s Cost: \$%.4f\n" \
|
|
359
|
+
"$team" "$project" "$cpu%" "$cost"
|
|
360
|
+
|
|
361
|
+
total_cost=$(echo "scale=6; $total_cost + $cost" | bc)
|
|
362
|
+
count=$((count + 1))
|
|
363
|
+
done
|
|
364
|
+
|
|
365
|
+
echo ""
|
|
366
|
+
echo "Agent Type Summary: $agent_type"
|
|
367
|
+
echo "Containers: $count"
|
|
368
|
+
echo "Total cost: \$$(printf '%.2f' "$total_cost")"
|
|
369
|
+
echo ""
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
# Find cost anomalies
|
|
373
|
+
anomalies() {
|
|
374
|
+
log_info "Finding high-cost containers"
|
|
375
|
+
echo ""
|
|
376
|
+
|
|
377
|
+
echo "Containers with cost > \$1.00:"
|
|
378
|
+
echo "=============================="
|
|
379
|
+
|
|
380
|
+
local found=0
|
|
381
|
+
|
|
382
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
383
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
384
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
385
|
+
|
|
386
|
+
local team=$(get_container_label "$container_id" "team")
|
|
387
|
+
local project=$(get_container_label "$container_id" "project")
|
|
388
|
+
local provider=$(get_container_label "$container_id" "provider" || echo "zai")
|
|
389
|
+
|
|
390
|
+
if [ -z "$team" ]; then
|
|
391
|
+
continue
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
local cost=$(calculate_container_cost "$container_id" "$cpu" "$mem" 3600 "$provider")
|
|
395
|
+
|
|
396
|
+
# Convert cost to number for comparison
|
|
397
|
+
if (( $(echo "$cost > 1.00" | bc -l) )); then
|
|
398
|
+
printf "\$%.2f %-15s %-30s (CPU: %s%%, MEM: %s MB)\n" \
|
|
399
|
+
"$cost" "$team" "$project" "$cpu" "$mem"
|
|
400
|
+
found=$((found + 1))
|
|
401
|
+
fi
|
|
402
|
+
done | sort -rn -k1
|
|
403
|
+
|
|
404
|
+
echo ""
|
|
405
|
+
if [ $found -eq 0 ]; then
|
|
406
|
+
log_success "No anomalies found (all costs < \$1.00)"
|
|
407
|
+
else
|
|
408
|
+
log_warn "Found $found containers with high costs"
|
|
409
|
+
fi
|
|
410
|
+
echo ""
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
# Cost forecast
|
|
414
|
+
forecast() {
|
|
415
|
+
local days=${1:-7}
|
|
416
|
+
|
|
417
|
+
log_info "Forecasting costs for next $days days"
|
|
418
|
+
echo ""
|
|
419
|
+
|
|
420
|
+
# Calculate average daily cost
|
|
421
|
+
local total_cost=0
|
|
422
|
+
local count=0
|
|
423
|
+
|
|
424
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
425
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
426
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
427
|
+
|
|
428
|
+
local provider=$(get_container_label "$container_id" "provider" || echo "zai")
|
|
429
|
+
local cost=$(calculate_container_cost "$container_id" "$cpu" "$mem" 3600 "$provider")
|
|
430
|
+
|
|
431
|
+
total_cost=$(echo "scale=6; $total_cost + $cost" | bc)
|
|
432
|
+
count=$((count + 1))
|
|
433
|
+
done
|
|
434
|
+
|
|
435
|
+
if [ $count -eq 0 ]; then
|
|
436
|
+
log_warn "No running containers to forecast"
|
|
437
|
+
return
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
local avg_daily_cost=$total_cost
|
|
441
|
+
local forecast_cost=$(echo "scale=2; $avg_daily_cost * $days" | bc)
|
|
442
|
+
|
|
443
|
+
echo "Cost Forecast:"
|
|
444
|
+
echo "=============="
|
|
445
|
+
echo "Current running containers: $count"
|
|
446
|
+
echo "Average daily cost: \$$(printf '%.2f' "$avg_daily_cost")"
|
|
447
|
+
echo "Forecasted ${days}-day cost: \$$(printf '%.2f' "$forecast_cost")"
|
|
448
|
+
echo ""
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
# Export to CSV
|
|
452
|
+
export_csv() {
|
|
453
|
+
local output_file=${1:-costs-$(date +%Y-%m-%d).csv}
|
|
454
|
+
|
|
455
|
+
log_info "Exporting costs to CSV: $output_file"
|
|
456
|
+
|
|
457
|
+
# CSV header
|
|
458
|
+
cat > "$output_file" <<EOF
|
|
459
|
+
Container ID,Team,Cost Center,Project,Agent Type,CPU %,Memory MB,Provider,Cost,Timestamp
|
|
460
|
+
EOF
|
|
461
|
+
|
|
462
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
463
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
464
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
465
|
+
|
|
466
|
+
local team=$(get_container_label "$container_id" "team" || echo "unknown")
|
|
467
|
+
local cost_center=$(get_container_label "$container_id" "cost-center" || echo "unknown")
|
|
468
|
+
local project=$(get_container_label "$container_id" "project" || echo "unknown")
|
|
469
|
+
local agent_type=$(get_container_label "$container_id" "agent-type" || echo "unknown")
|
|
470
|
+
local provider=$(get_container_label "$container_id" "provider" || echo "zai")
|
|
471
|
+
local cost=$(calculate_container_cost "$container_id" "$cpu" "$mem" 3600 "$provider")
|
|
472
|
+
|
|
473
|
+
printf "%s,%s,%s,%s,%s,%.1f,%.0f,%s,%.6f,%s\n" \
|
|
474
|
+
"$container_id" "$team" "$cost_center" "$project" "$agent_type" \
|
|
475
|
+
"$cpu" "$mem" "$provider" "$cost" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$output_file"
|
|
476
|
+
done
|
|
477
|
+
|
|
478
|
+
log_success "Costs exported to: $output_file"
|
|
479
|
+
log_info "Total rows: $(tail -n +2 "$output_file" | wc -l)"
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
# Check quotas
|
|
483
|
+
quota_check() {
|
|
484
|
+
log_info "Checking team quotas"
|
|
485
|
+
echo ""
|
|
486
|
+
|
|
487
|
+
# Define team quotas
|
|
488
|
+
declare -A team_daily_budgets=(
|
|
489
|
+
[engineering]=500
|
|
490
|
+
[marketing]=200
|
|
491
|
+
[data]=1000
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
declare -A team_max_cpu=(
|
|
495
|
+
[engineering]=4
|
|
496
|
+
[marketing]=2
|
|
497
|
+
[data]=8
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
declare -A team_max_memory=(
|
|
501
|
+
[engineering]=16
|
|
502
|
+
[marketing]=8
|
|
503
|
+
[data]=32
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
echo "Quota Status:"
|
|
507
|
+
echo "============="
|
|
508
|
+
|
|
509
|
+
local overall_ok=true
|
|
510
|
+
|
|
511
|
+
get_running_container_stats | while IFS=$'\t' read -r container_id cpu mem; do
|
|
512
|
+
cpu=$(echo "$cpu" | sed 's/%$//')
|
|
513
|
+
mem=$(echo "$mem" | sed 's/MiB$//' | sed 's/GiB$//')
|
|
514
|
+
|
|
515
|
+
local team=$(get_container_label "$container_id" "team")
|
|
516
|
+
|
|
517
|
+
if [ -z "$team" ]; then
|
|
518
|
+
continue
|
|
519
|
+
fi
|
|
520
|
+
|
|
521
|
+
# Check per-container limits (assume quotas are per-container max)
|
|
522
|
+
local max_cpu=${team_max_cpu[$team]:-4}
|
|
523
|
+
local max_mem=${team_max_memory[$team]:-16}
|
|
524
|
+
|
|
525
|
+
if (( $(echo "$cpu > $max_cpu" | bc -l) )); then
|
|
526
|
+
log_warn "Team $team exceeded CPU quota: $cpu% > $max_cpu%"
|
|
527
|
+
overall_ok=false
|
|
528
|
+
fi
|
|
529
|
+
|
|
530
|
+
if (( $(echo "${mem%.*} > $max_mem" | bc -l) )); then
|
|
531
|
+
log_warn "Team $team exceeded memory quota: ${mem}GB > ${max_mem}GB"
|
|
532
|
+
overall_ok=false
|
|
533
|
+
fi
|
|
534
|
+
done
|
|
535
|
+
|
|
536
|
+
if [ "$overall_ok" = true ]; then
|
|
537
|
+
log_success "All teams within quotas"
|
|
538
|
+
else
|
|
539
|
+
log_warn "Some teams exceeding quotas - review above"
|
|
540
|
+
fi
|
|
541
|
+
echo ""
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
# Main command dispatcher
|
|
545
|
+
main() {
|
|
546
|
+
local command=${1:-help}
|
|
547
|
+
|
|
548
|
+
case "$command" in
|
|
549
|
+
daily-report)
|
|
550
|
+
daily_report "${2:-}"
|
|
551
|
+
;;
|
|
552
|
+
by-team)
|
|
553
|
+
by_team "${2:-all}"
|
|
554
|
+
;;
|
|
555
|
+
by-project)
|
|
556
|
+
by_project "${2:-}"
|
|
557
|
+
;;
|
|
558
|
+
by-agent)
|
|
559
|
+
by_agent "${2:-}"
|
|
560
|
+
;;
|
|
561
|
+
anomalies)
|
|
562
|
+
anomalies
|
|
563
|
+
;;
|
|
564
|
+
forecast)
|
|
565
|
+
forecast "${2:-7}"
|
|
566
|
+
;;
|
|
567
|
+
export-csv)
|
|
568
|
+
export_csv "${2:-}"
|
|
569
|
+
;;
|
|
570
|
+
quota-check)
|
|
571
|
+
quota_check
|
|
572
|
+
;;
|
|
573
|
+
help|--help|-h)
|
|
574
|
+
cat <<EOF
|
|
575
|
+
Cost Allocation and Tracking Tool
|
|
576
|
+
|
|
577
|
+
Usage: $(basename "$0") [command] [options]
|
|
578
|
+
|
|
579
|
+
Commands:
|
|
580
|
+
daily-report [date] Generate daily cost report
|
|
581
|
+
date: YYYY-MM-DD (default: today)
|
|
582
|
+
|
|
583
|
+
by-team [team] Show costs by team
|
|
584
|
+
team: team name or 'all' (default: all)
|
|
585
|
+
|
|
586
|
+
by-project <project> Show costs for specific project
|
|
587
|
+
project: project name (required)
|
|
588
|
+
|
|
589
|
+
by-agent <agent-type> Show costs by agent type
|
|
590
|
+
agent-type: agent type (required)
|
|
591
|
+
|
|
592
|
+
anomalies Find high-cost containers (> \$1.00/hour)
|
|
593
|
+
|
|
594
|
+
forecast [days] Project future costs
|
|
595
|
+
days: number of days to forecast (default: 7)
|
|
596
|
+
|
|
597
|
+
export-csv [file] Export costs to CSV file
|
|
598
|
+
file: output file (default: costs-YYYY-MM-DD.csv)
|
|
599
|
+
|
|
600
|
+
quota-check Verify team resource quotas
|
|
601
|
+
|
|
602
|
+
help Show this help message
|
|
603
|
+
|
|
604
|
+
Pricing Configuration:
|
|
605
|
+
CPU: \$$COST_CPU_PER_HOUR per core per hour
|
|
606
|
+
Memory: \$$COST_MEMORY_PER_GB_HOUR per GB per hour
|
|
607
|
+
API (Z.ai): \$$COST_ZAI per 1M tokens
|
|
608
|
+
|
|
609
|
+
Examples:
|
|
610
|
+
# Daily report for today
|
|
611
|
+
$(basename "$0") daily-report
|
|
612
|
+
|
|
613
|
+
# Show costs for engineering team
|
|
614
|
+
$(basename "$0") by-team engineering
|
|
615
|
+
|
|
616
|
+
# Find expensive containers
|
|
617
|
+
$(basename "$0") anomalies
|
|
618
|
+
|
|
619
|
+
# Export all costs to CSV
|
|
620
|
+
$(basename "$0") export-csv costs.csv
|
|
621
|
+
|
|
622
|
+
EOF
|
|
623
|
+
;;
|
|
624
|
+
*)
|
|
625
|
+
log_error "Unknown command: $command"
|
|
626
|
+
echo "Run '$(basename "$0") help' for usage"
|
|
627
|
+
exit 1
|
|
628
|
+
;;
|
|
629
|
+
esac
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
main "$@"
|
|
@@ -28,11 +28,11 @@ BUILD_START=$(date +%s)
|
|
|
28
28
|
|
|
29
29
|
# 1. Base agent image (used by all variants)
|
|
30
30
|
echo "📦 [1/4] Building claude-flow-novice-agent:latest"
|
|
31
|
-
echo " Dockerfile: Dockerfile
|
|
31
|
+
echo " Dockerfile: docker/agent/Dockerfile"
|
|
32
32
|
docker build \
|
|
33
33
|
--progress=plain \
|
|
34
34
|
--cache-from claude-flow-novice-agent:latest \
|
|
35
|
-
-f Dockerfile
|
|
35
|
+
-f docker/agent/Dockerfile \
|
|
36
36
|
-t claude-flow-novice-agent:latest \
|
|
37
37
|
. || {
|
|
38
38
|
echo "❌ Failed to build claude-flow-novice-agent:latest"
|