feat(monitoring): dispatch monitoring tasks to desktop via AMQP outbox

Replace SSE /desktop/events with priority AMQP dispatch for monitoring
runs, and add phase1/phase2 infrastructure so desktop workers can lease,
resume, report, skip, and cancel monitoring tasks over the existing
dispatch WebSocket.

- Add monitoring collect outbox worker and phase2 desktop task fields
- Add /desktop/monitoring/tasks/{lease,resume,result,skip,cancel} routes
- Introduce execution-devtools and network-observer on desktop runtime
- Refactor runtime-controller, LoginView, and doubao adapter for the new flow
- Add channelName column to tracking views

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 00:24:21 +08:00
parent 749b6b99cd
commit 4142c53fa6
57 changed files with 7897 additions and 985 deletions
@@ -0,0 +1,17 @@
DROP INDEX IF EXISTS idx_collect_requests_active_scope;
DROP TABLE IF EXISTS monitoring_collect_requests;
DROP INDEX IF EXISTS idx_collect_tasks_target_client_ready;
DROP INDEX IF EXISTS idx_collect_tasks_dispatch_ready;
ALTER TABLE monitoring_collect_tasks
DROP COLUMN IF EXISTS execution_owner,
DROP COLUMN IF EXISTS ingest_shard_key,
DROP COLUMN IF EXISTS dispatch_attempts,
DROP COLUMN IF EXISTS last_dispatched_at,
DROP COLUMN IF EXISTS superseded_by_request_id,
DROP COLUMN IF EXISTS interrupt_generation,
DROP COLUMN IF EXISTS dispatch_after,
DROP COLUMN IF EXISTS target_client_id,
DROP COLUMN IF EXISTS dispatch_lane,
DROP COLUMN IF EXISTS dispatch_priority;
@@ -0,0 +1,87 @@
ALTER TABLE monitoring_collect_tasks
ADD COLUMN IF NOT EXISTS dispatch_priority INT NOT NULL DEFAULT 100,
ADD COLUMN IF NOT EXISTS dispatch_lane VARCHAR(20) NOT NULL DEFAULT 'normal',
ADD COLUMN IF NOT EXISTS target_client_id UUID,
ADD COLUMN IF NOT EXISTS dispatch_after TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS interrupt_generation INT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS superseded_by_request_id UUID,
ADD COLUMN IF NOT EXISTS last_dispatched_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS dispatch_attempts INT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS ingest_shard_key VARCHAR(120),
ADD COLUMN IF NOT EXISTS execution_owner VARCHAR(20) NOT NULL DEFAULT 'legacy';
UPDATE monitoring_collect_tasks
SET dispatch_after = COALESCE(dispatch_after, planned_at),
ingest_shard_key = COALESCE(
NULLIF(ingest_shard_key, ''),
tenant_id::text || ':' || brand_id::text || ':' || business_date::text
),
dispatch_priority = CASE
WHEN trigger_source = 'manual' THEN 5000
ELSE dispatch_priority
END,
dispatch_lane = CASE
WHEN trigger_source = 'manual' THEN 'high'
ELSE dispatch_lane
END,
updated_at = NOW();
CREATE INDEX IF NOT EXISTS idx_collect_tasks_dispatch_ready
ON monitoring_collect_tasks (
collector_type,
status,
business_date,
dispatch_lane,
dispatch_priority DESC,
dispatch_after,
planned_at
);
CREATE INDEX IF NOT EXISTS idx_collect_tasks_target_client_ready
ON monitoring_collect_tasks (
target_client_id,
collector_type,
status,
business_date,
dispatch_lane,
dispatch_priority DESC,
planned_at
);
CREATE TABLE IF NOT EXISTS monitoring_collect_requests (
id BIGSERIAL PRIMARY KEY,
request_id UUID NOT NULL,
tenant_id BIGINT NOT NULL,
workspace_id BIGINT NOT NULL,
brand_id BIGINT NOT NULL,
keyword_id BIGINT,
requested_by_user_id BIGINT NOT NULL,
target_client_id UUID,
scope_hash VARCHAR(64) NOT NULL,
request_scope JSONB NOT NULL DEFAULT '{}'::jsonb,
status VARCHAR(30) NOT NULL DEFAULT 'accepted',
requested_task_count INT NOT NULL DEFAULT 0,
dispatched_task_count INT NOT NULL DEFAULT 0,
promoted_task_count INT NOT NULL DEFAULT 0,
aborted_queued_count INT NOT NULL DEFAULT 0,
interrupt_requested_count INT NOT NULL DEFAULT 0,
interrupt_generation INT NOT NULL DEFAULT 0,
message TEXT,
ttl_expires_at TIMESTAMPTZ NOT NULL,
first_dispatched_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
superseded_by_request_id UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (request_id)
);
CREATE INDEX IF NOT EXISTS idx_collect_requests_active_scope
ON monitoring_collect_requests (
tenant_id,
workspace_id,
brand_id,
requested_by_user_id,
scope_hash,
ttl_expires_at DESC
);
@@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_collect_dispatch_outbox_request;
DROP INDEX IF EXISTS idx_collect_dispatch_outbox_pending;
DROP TABLE IF EXISTS monitoring_collect_dispatch_outbox;
@@ -0,0 +1,26 @@
CREATE TABLE IF NOT EXISTS monitoring_collect_dispatch_outbox (
id BIGSERIAL PRIMARY KEY,
request_id UUID NOT NULL,
workspace_id BIGINT NOT NULL,
target_client_id UUID NOT NULL,
event_kind VARCHAR(40) NOT NULL,
task_id BIGINT,
lane VARCHAR(20),
priority INT NOT NULL DEFAULT 0,
interrupt_generation INT NOT NULL DEFAULT 0,
reason VARCHAR(120),
request_dispatched_count INT NOT NULL DEFAULT 0,
available_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
published_at TIMESTAMPTZ,
attempt_count INT NOT NULL DEFAULT 0,
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_collect_dispatch_outbox_pending
ON monitoring_collect_dispatch_outbox (available_at, id)
WHERE published_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_collect_dispatch_outbox_request
ON monitoring_collect_dispatch_outbox (request_id, id);