diff --git a/backend/app/services/openclaw/runtime_usage.py b/backend/app/services/openclaw/runtime_usage.py index 6ed6bd4..a9ed204 100644 --- a/backend/app/services/openclaw/runtime_usage.py +++ b/backend/app/services/openclaw/runtime_usage.py @@ -510,14 +510,32 @@ async def get_runtime_usage( """ now = utcnow() - cost_raw, status_raw = await asyncio.gather( + cost_raw, status_raw, sessions_raw = await asyncio.gather( _safe_call("usage.cost", config), _safe_call("usage.status", config), + _safe_call("sessions.list", config), ) + + # Extract sessions from sessions.list response (primary source) + # Fallback to usage.cost if sessions.list fails + if isinstance(sessions_raw, dict): + raw_sessions = sessions_raw.get("sessions") or [] + elif isinstance(sessions_raw, list): + raw_sessions = sessions_raw + else: + raw_sessions = [] + + # Filter to dicts and merge with usage.cost data if available + sessions: list[dict[str, Any]] = [] + if raw_sessions: + sessions = [s for s in raw_sessions if isinstance(s, dict)] + else: + # Fallback: parse from usage.cost response + sessions = _parse_sessions(cost_raw) + # Merge both payloads — some gateways return everything in one response merged_status = {**cost_raw, **status_raw} - sessions = _parse_sessions(cost_raw) per_model = aggregate_per_model(sessions, account_key=account_key) window = _build_window(merged_status, now) current = _build_current(per_model, merged_status) diff --git a/backend/migrations/versions/a1b2c3d4e5f5_add_board_webhooks_table.py b/backend/migrations/versions/a1b2c3d4e5f5_add_board_webhooks_table.py new file mode 100644 index 0000000..89e224e --- /dev/null +++ b/backend/migrations/versions/a1b2c3d4e5f5_add_board_webhooks_table.py @@ -0,0 +1,70 @@ +"""Add board_webhooks table. + +Revision ID: a1b2c3d4e5f5 +Revises: 658dca8f4a11 +Create Date: 2026-03-03 00:00:00.000000 + +""" + +from __future__ import annotations + +import sqlalchemy as sa +import sqlmodel +from alembic import op + +# revision identifiers, used by Alembic. +revision = "a1b2c3d4e5f5" +down_revision = "4c1f5e2a7b9d" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + """Create board_webhooks table.""" + op.create_table( + "board_webhooks", + sa.Column("id", sa.Uuid(), nullable=False), + sa.Column("board_id", sa.Uuid(), nullable=False), + sa.Column("agent_id", sa.Uuid(), nullable=True), + sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("enabled", sa.Boolean(), nullable=False), + sa.Column("secret", sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column("signature_header", sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=False), + sa.Column("updated_at", sa.DateTime(), nullable=False), + sa.ForeignKeyConstraint( + ["board_id"], + ["boards.id"], + ), + sa.ForeignKeyConstraint( + ["agent_id"], + ["agents.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index( + op.f("ix_board_webhooks_board_id"), + "board_webhooks", + ["board_id"], + unique=False, + ) + op.create_index( + op.f("ix_board_webhooks_agent_id"), + "board_webhooks", + ["agent_id"], + unique=False, + ) + op.create_index( + op.f("ix_board_webhooks_enabled"), + "board_webhooks", + ["enabled"], + unique=False, + ) + + +def downgrade() -> None: + """Drop board_webhooks table.""" + op.drop_index(op.f("ix_board_webhooks_enabled"), table_name="board_webhooks") + op.drop_index(op.f("ix_board_webhooks_agent_id"), table_name="board_webhooks") + op.drop_index(op.f("ix_board_webhooks_board_id"), table_name="board_webhooks") + op.drop_table("board_webhooks") diff --git a/backend/migrations/versions/a1b2c3d4e5f6_add_webhook_secret.py b/backend/migrations/versions/a1b2c3d4e5f6_add_webhook_secret.py index 813b737..6122c48 100644 --- a/backend/migrations/versions/a1b2c3d4e5f6_add_webhook_secret.py +++ b/backend/migrations/versions/a1b2c3d4e5f6_add_webhook_secret.py @@ -13,7 +13,7 @@ from alembic import op # revision identifiers, used by Alembic. revision = "a1b2c3d4e5f6" -down_revision = "f1b2c3d4e5a6" +down_revision = "a1b2c3d4e5f5" branch_labels = None depends_on = None diff --git a/backend/migrations/versions/a2f6c9d4b7e8_add_previous_in_progress_at_to_tasks.py b/backend/migrations/versions/a2f6c9d4b7e8_add_previous_in_progress_at_to_tasks.py index 0eb25df..429455d 100644 --- a/backend/migrations/versions/a2f6c9d4b7e8_add_previous_in_progress_at_to_tasks.py +++ b/backend/migrations/versions/a2f6c9d4b7e8_add_previous_in_progress_at_to_tasks.py @@ -13,7 +13,7 @@ from alembic import op # revision identifiers, used by Alembic. revision = "a2f6c9d4b7e8" -down_revision = "4c1f5e2a7b9d" +down_revision = "d1e2f3a4b5c6" branch_labels = None depends_on = None diff --git a/backend/migrations/versions/b7a1d9c3e4f5_add_agent_id_to_board_webhooks.py b/backend/migrations/versions/b7a1d9c3e4f5_add_agent_id_to_board_webhooks.py index 4134512..02041f6 100644 --- a/backend/migrations/versions/b7a1d9c3e4f5_add_agent_id_to_board_webhooks.py +++ b/backend/migrations/versions/b7a1d9c3e4f5_add_agent_id_to_board_webhooks.py @@ -13,7 +13,7 @@ from alembic import op # revision identifiers, used by Alembic. revision = "b7a1d9c3e4f5" -down_revision = "a2f6c9d4b7e8" +down_revision = "a9b1c2d3e4f7" branch_labels = None depends_on = None diff --git a/backend/migrations/versions/c5d1a2b3e4f6_add_disable_device_pairing_to_gateways.py b/backend/migrations/versions/c5d1a2b3e4f6_add_disable_device_pairing_to_gateways.py index b0ce097..5e28324 100644 --- a/backend/migrations/versions/c5d1a2b3e4f6_add_disable_device_pairing_to_gateways.py +++ b/backend/migrations/versions/c5d1a2b3e4f6_add_disable_device_pairing_to_gateways.py @@ -13,7 +13,7 @@ from alembic import op # revision identifiers, used by Alembic. revision = "c5d1a2b3e4f6" -down_revision = "b7a1d9c3e4f5" +down_revision = "c1d2e3f4a5b6" branch_labels = None depends_on = None diff --git a/backend/migrations/versions/d1e2f3a4b5c6_add_provider_credentials.py b/backend/migrations/versions/d1e2f3a4b5c6_add_provider_credentials.py index 2e989a5..c9697d6 100644 --- a/backend/migrations/versions/d1e2f3a4b5c6_add_provider_credentials.py +++ b/backend/migrations/versions/d1e2f3a4b5c6_add_provider_credentials.py @@ -12,7 +12,7 @@ import sqlalchemy as sa from alembic import op revision = "d1e2f3a4b5c6" -down_revision = "c1d2e3f4a5b6" +down_revision = "f1b2c3d4e5a6" branch_labels = None depends_on = None diff --git a/backend/migrations/versions/f5a2b3c4d5e6_add_forgejo_models.py b/backend/migrations/versions/f5a2b3c4d5e6_add_forgejo_models.py index 7df035b..78fed75 100644 --- a/backend/migrations/versions/f5a2b3c4d5e6_add_forgejo_models.py +++ b/backend/migrations/versions/f5a2b3c4d5e6_add_forgejo_models.py @@ -13,7 +13,7 @@ from alembic import op # revision identifiers, used by Alembic. revision = "f5a2b3c4d5e6" -down_revision = "a9b1c2d3e4f7" +down_revision = "b7a1d9c3e4f5" branch_labels = None depends_on = None diff --git a/scripts/docker-test.sh b/scripts/docker-test.sh index 9aec0a4..51e6de6 100755 --- a/scripts/docker-test.sh +++ b/scripts/docker-test.sh @@ -1,19 +1,15 @@ #!/usr/bin/env bash # docker-test.sh — Rebuild and redeploy Pipeline locally from scratch. -# Clears all Docker cache before building to ensure a clean slate. +# Cleans only Pipeline Docker resources before building. +# IMPORTANT: Do not add global Docker cleanup here (for example `docker system prune`). +# This script must only stop/remove resources for the Pipeline compose project. set -euo pipefail cd "$(git rev-parse --show-toplevel)" -echo "=== Stopping and removing existing containers, images, volumes ===" +echo "=== Stopping and removing Pipeline containers, images, volumes ===" docker compose down --rmi all --volumes --remove-orphans 2>/dev/null || true -echo "=== Pruning all dangling Docker resources ===" -docker system prune -a --volumes -f - -echo "=== Removing Pipeline build cache ===" -docker builder prune -f 2>/dev/null || true - echo "=== Building and starting all containers ===" docker compose up --build -d @@ -25,5 +21,5 @@ docker compose ps echo "" echo "=== Deploy complete ===" -echo "Frontend: http://localhost:3030" -echo "Backend: http://localhost:8001" \ No newline at end of file +echo 'Frontend: http://localhost:3030' +echo 'Backend: http://localhost:8001'