帮我构思一个项目:Trae、Codearts atomcode 等AI agent的调度中心 优先windows系统,通过句柄获得这些AI agent的任务信息,对其进行跟踪,用户可以通过调度中心发布

帮我构思一个项目:Trae、Codearts atomcode 等AI agent的调度中心 优先windows系统,通过句柄获得这些AI agent的任务信息,对其进行跟踪,用户可以通过调度中心发布新任务,并修改和回应当前的任务。

群星(Star)- AI Agent 调度中心

1. 项目概述

1.1 项目名

  • 中文名:群星
  • 英文名:Star

1.2 命名寓意

“群星闪耀,各司其职。调度有序,光芒汇聚。”

多个 AI Agent 如同夜空中的群星,各自闪耀着独特的光芒。Star 调度中心就像星座的连线者,将这些独立的光芒串联成完整的图景,让每一颗星的输出都能汇聚成更强大的力量。

1.3 Slogan

群星(Star)—— 让每一个 AI Agent 都成为你的星座

1.4 核心目标

在 Windows 系统上,通过系统级 API(句柄、窗口消息、进程监控)实现对多个 AI 编程助手(Trae、CodeArts Atomcode、Cursor、Copilot 等)的统一调度与任务管理。

1.5 核心功能

  • 星图(Agent 发现):自动检测正在运行的 AI Agent 进程
  • 星轨(任务队列):创建、分配、修改、暂停、恢复任务
  • 星语(对话监控):截获 Agent 的输入/输出流
  • 星令(任务干预):向运行中的 Agent 注入新指令或修正方向
  • 星辉(结果收集):汇总各 Agent 输出,统一展示

2. 术语映射

通用术语群星(Star)术语说明
AI Agent星(Star)每个 Agent 是一颗星
Agent 进程星体(Star Body)运行中的 Agent 实例
任务队列星轨(Orbit)任务的流转路径
新任务新星(Nova)新创建的任务
任务分配授星(Assign)将任务交给某颗星
任务修改调轨(Adjust Orbit)修改运行中的任务方向
结果输出星辉(Starlight)Agent 的产出
用户反馈回响(Echo)用户对 Agent 输出的回应
多 Agent 协同星座(Constellation)多个星协同完成复杂任务

3. 技术架构

┌─────────────────────────────────────────────────────────────┐
│                     群星(Star)前端                          │
│   (Web UI: React + Vite, 或 Electron 桌面端)                 │
│   星图面板 │ 星轨队列 │ 星语流 │ 星辉审查                      │
└───────────────────────────┬─────────────────────────────────┘
                            │ WebSocket / IPC
┌───────────────────────────▼─────────────────────────────────┐
│                    星核(Star Core)- Python 3.12             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │ 寻星者        │  │ 星轨引擎      │  │ 星语路由         │  │
│  │ StarSeeker   │  │ OrbitEngine  │  │ StarlightRouter │  │
│  └──────┬───────┘  └──────┬───────┘  └────────┬─────────┘  │
│         │                 │                    │            │
│  ┌──────▼─────────────────▼────────────────────▼─────────┐  │
│  │              观星台(Observatory)                       │  │
│  │  · Win32 API (句柄/窗口/进程)                          │  │
│  │  · UI Automation (元素定位/文本注入)                   │  │
│  │  · 键盘/鼠标模拟 (SendInput/PostMessage)               │  │
│  │  · 剪贴板监控 (Clipboard API)                          │  │
│  └───────────────────────────────────────────────────────┘  │
└───────────────────────────┬─────────────────────────────────┘
                            │ 句柄 / UIA / 消息
┌───────────────────────────▼─────────────────────────────────┐
│                    群星闪耀(Agent 进程)                      │
│  ┌──────────┐  ┌──────────────┐  ┌──────────┐              │
│  │  Trae ☆  │  │CodeArts Atom☆│  │ Cursor ☆ │  ...         │
│  └──────────┘  └──────────────┘  └──────────┘              │
└─────────────────────────────────────────────────────────────┘

4. 核心模块命名与实现

4.1 寻星者(StarSeeker)- Agent 发现

class StarSeeker:
    """寻星者 - 发现并管理 AI Agent 进程"""
    
    # 已知星体特征
    STAR_SIGNATURES = {
        'trae': {
            'process_names': ['Trae.exe', 'trae.exe'],
            'window_class': ['Chrome_WidgetWin_1', 'TraeMainWindow'],
            'window_title_patterns': ['Trae', 'trae'],
        },
        'codearts_atomcode': {
            'process_names': ['CodeArts.exe', 'AtomCode.exe'],
            'window_class': ['Chrome_WidgetWin_1'],
            'window_title_patterns': ['CodeArts', 'AtomCode'],
        },
        'cursor': {
            'process_names': ['Cursor.exe'],
            'window_class': ['Chrome_WidgetWin_1'],
            'window_title_patterns': ['Cursor'],
        },
    }
    
    def scan_skies(self) -> list['StarBody']:
        """扫描天际,发现所有闪耀的星"""
        stars = []
        
        for proc in psutil.process_iter(['pid', 'name']):
            for star_type, sig in self.STAR_SIGNATURES.items():
                if proc.info['name'].lower() in [n.lower() for n in sig['process_names']]:
                    hwnd = self._locate_light(proc.info['pid'], sig)
                    if hwnd:
                        stars.append(StarBody(
                            star_type=star_type,
                            pid=proc.info['pid'],
                            hwnd=hwnd,
                            process=proc
                        ))
        return stars
    
    def _locate_light(self, pid: int, sig: dict) -> int:
        """定位星光 - 通过 PID 找到主窗口句柄"""
        def callback(hwnd, hwnds):
            if win32gui.IsWindowVisible(hwnd):
                _, found_pid = win32process.GetWindowThreadProcessId(hwnd)
                if found_pid == pid:
                    title = win32gui.GetWindowText(hwnd)
                    if any(p in title for p in sig['window_title_patterns']):
                        hwnds.append(hwnd)
            return True
        
        hwnds = []
        win32gui.EnumWindows(callback, hwnds)
        return hwnds[0] if hwnds else None

4.2 授星者(StarAssigner)- 文本注入

class StarAssigner:
    """授星者 - 向星体注入星令"""
    
    def send_starlight(self, star: 'StarBody', starlight: str) -> bool:
        """向星体发送星辉指令"""
        # 策略1: 观星术(UIA)- 最精准
        if self._try_observatory(star.hwnd, starlight):
            return True
        
        # 策略2: 星光传递(剪贴板)
        if self._try_starlight_transfer(star, starlight):
            return True
        
        # 策略3: 星波(消息)- 适用于原生控件
        if self._try_starwave(star.hwnd, starlight):
            return True
        
        return False
    
    def _try_observatory(self, hwnd: int, starlight: str) -> bool:
        """观星术 - 通过 UI Automation 精确定位输入框"""
        import uiautomation as uia
        
        try:
            window = uia.ControlFromHandle(hwnd)
            
            # 寻找星语输入口
            edit = window.EditControl(
                searchDepth=5,
                ClassName='TextArea'
            )
            
            if edit:
                edit.SetValue(starlight)
                edit.SendKeys('{Enter}')
                return True
        except Exception:
            pass
        return False
    
    def _try_starlight_transfer(self, star: 'StarBody', starlight: str) -> bool:
        """星光传递 - 剪贴板注入"""
        import pyperclip
        
        original = pyperclip.paste()
        try:
            pyperclip.copy(starlight)
            win32gui.SetForegroundWindow(star.hwnd)
            time.sleep(0.1)
            
            import pyautogui
            pyautogui.hotkey('ctrl', 'v')
            time.sleep(0.1)
            pyautogui.press('enter')
            
            return True
        finally:
            pyperclip.copy(original)

4.3 观星者(StarGazer)- 输出捕获

class StarGazer:
    """观星者 - 捕获星体的输出"""
    
    def gaze(self, star: 'StarBody') -> str:
        """凝视星辉 - 获取 Agent 最后一条回复"""
        import uiautomation as uia
        
        window = uia.ControlFromHandle(star.hwnd)
        
        output_controls = window.TextControl(
            searchDepth=10,
            ClassName='TextBlock'
        )
        
        if output_controls:
            return output_controls.Name
        
        return ""
    
    def continuous_gaze(self, star: 'StarBody', on_starlight_change):
        """持续观星 - 监控输出变化"""
        import threading
        
        def gaze_thread():
            last_starlight = ""
            while star.is_shining:
                current = self.gaze(star)
                if current != last_starlight:
                    on_starlight_change(star, current)
                    last_starlight = current
                time.sleep(1)
        
        threading.Thread(target=gaze_thread, daemon=True).start()

5. 星轨引擎(OrbitEngine)- 任务调度

5.1 星轨任务模型

from dataclasses import dataclass
from enum import Enum
from datetime import datetime
from typing import Optional

class StarStatus(Enum):
    """星芒状态"""
    NASCENT = "nascent"           # 初生(新创建)
    ORBITING = "orbiting"         # 入轨(已分配)
    SHINING = "shining"           # 闪耀(Agent 正在处理)
    AWAITING_ECHO = "awaiting"    # 待回响(等待用户审查)
    CONSTELLATED = "constellated" # 成星(完成)
    FADED = "faded"               # 暗淡(失败)
    DARKENED = "darkened"         # 熄灭(取消)

class StarPriority(Enum):
    """星等(优先级)"""
    DIM = 0        # 暗星(低)
    NORMAL = 1     # 常星(正常)
    BRIGHT = 2     # 亮星(高)
    SUPERNOVA = 3  # 超新星(紧急)

@dataclass
class Nova:
    """新星 - 任务数据模型"""
    id: str
    title: str
    description: str
    starlight: str                # 发送给星的指令
    context_files: list[str]      # 上下文文件
    assigned_star: Optional[str]  # 分配的目标星
    status: StarStatus
    priority: StarPriority
    created_at: datetime
    updated_at: datetime
    result_starlight: Optional[str]  # 星辉(Agent 返回结果)
    starlight_log: list[dict]        # 星光日志(对话历史)
    echo: Optional[str]              # 回响(用户反馈)

5.2 星轨引擎

class OrbitEngine:
    """星轨引擎 - 任务调度核心"""
    
    def __init__(self, star_seeker: 'StarSeeker'):
        self.star_seeker = star_seeker
        self.orbit_queue = asyncio.Queue()
        self.active_novas = {}
    
    async def birth_nova(self, nova: Nova) -> str:
        """诞生新星 - 提交新任务"""
        if not nova.assigned_star:
            nova.assigned_star = self._calculate_orbit(nova)
        
        nova.status = StarStatus.NASCENT
        await self.orbit_queue.put(nova)
        return nova.id
    
    def _calculate_orbit(self, nova: Nova) -> str:
        """计算星轨 - 根据任务特征路由到合适的星"""
        star_affinity = {
            'trae': ['生成', '创建', '编写', 'generate', 'create'],
            'codearts': ['审查', 'review', '检查', '漏洞'],
            'cursor': ['重构', 'refactor', '迁移', '多文件'],
        }
        
        for star_type, keywords in star_affinity.items():
            if any(kw in nova.description for kw in keywords):
                available = self.star_seeker.get_idle_stars(star_type)
                if available:
                    return star_type
        
        return self.star_seeker.get_any_idle_star()
    
    async def adjust_orbit(self, nova_id: str, new_starlight: str):
        """调轨 - 修改运行中的任务"""
        nova = self.active_novas.get(nova_id)
        if not nova:
            raise ValueError(f"Nova {nova_id} not found in sky")
        
        star = self.star_seeker.get_star(nova.assigned_star)
        if star and nova.status == StarStatus.SHINING:
            correction_starlight = f"""
            [星核指令] 对当前星轨进行调整:
            原始星图:{nova.starlight}
            调轨指令:{new_starlight}
            请忽略之前的中间星光,基于调轨指令重新闪耀。
            """
            star.send_starlight(correction_starlight)
            nova.starlight = new_starlight
            nova.starlight_log.append({
                'role': 'star_core',
                'content': '星轨已调整'
            })

6. 前端界面设计

6.1 主界面布局

┌─────────────────────────────────────────────────┐
│  ✦ 群星 Star                      [⚙ 星图设置] [📊]│
├──────────┬───────────────────┬──────────────────┤
│ 星图      │    星轨面板        │   星语监控        │
│          │                   │                  │
│ 🟢 Trae  │ 🌟 新星待升 (3)   │ [星辉实时]       │
│ 🟡 Atom  │  · 生成API星图    │                  │
│ 🔴Cursor │  · 修复Bug #42   │ 正在闪耀...       │
│          │  · 优化星光查询   │                  │
│          │                   │                  │
│ [+ 唤星] │ 💫 闪耀中 (2)     │                  │
│          │  · 重构星云A     │                  │
│          │  · 撰写星光测试  │                  │
│          │                   │                  │
│          │ ✨ 已成星 (5)     │                  │
│          │                   │                  │
├──────────┴───────────────────┴──────────────────┤
│ 星令: [___________________] [授星: Trae ▼] [发送]│
└─────────────────────────────────────────────────┘

6.2 颜色系统

  • 主色调:深空蓝黑(#0a0e27 背景,#1a1f3a 面板)
  • 强调色:星光金(#ffd700
  • 星等色
    • 超新星:炽白(#ffffff
    • 亮星:亮蓝(#4fc3f7
    • 常星:银白(#b0bec5
    • 暗星:暗灰(#616161
  • 星芒色
    • 闪耀中:翠绿(#66bb6a
    • 待回响:琥珀(#ffa726
    • 暗淡:赤红(#ef5350

6.3 图标系统

使用星星相关的 SVG 图标:

  • ⭐ 普通星
  • 🌟 新星
  • 💫 闪耀中
  • ✨ 已成星
  • 🔭 观星台
  • 🌌 星图

7. 项目结构

star/
├── star_core/                    # 星核(核心引擎)
│   ├── __init__.py
│   ├── star_seeker.py           # 寻星者(Agent 发现)
│   ├── star_assigner.py         # 授星者(文本注入)
│   ├── star_gazer.py            # 观星者(输出捕获)
│   ├── orbit_engine.py          # 星轨引擎(任务调度)
│   └── observatory.py           # 观星台(Windows API 封装)
│
├── star_api/                     # 星光接口(后端 API)
│   ├── __init__.py
│   ├── main.py                  # FastAPI 入口
│   ├── routes/
│   │   ├── stars.py             # 星管理接口
│   │   ├── novas.py             # 新星(任务)接口
│   │   └── constellations.py    # 星座(协同)接口
│   └── websocket/
│       └── starlight_stream.py  # 星光流推送
│
├── star_web/                     # 星图(前端)
│   ├── src/
│   │   ├── components/
│   │   │   ├── StarMap.tsx       # 星图面板
│   │   │   ├── OrbitPanel.tsx    # 星轨面板
│   │   │   ├── StarlightMonitor.tsx  # 星语监控
│   │   │   └── StarCommand.tsx   # 星令输入
│   │   ├── pages/
│   │   │   └── Observatory.tsx   # 观星台主页
│   │   └── assets/
│   │       └── star_icons/       # 星星图标
│   └── package.json
│
├── pyproject.toml
└── README.md

8. 星语(日志)系统

from loguru import logger

# 星语级别
logger.add(
    "logs/star_core.log",
    format="⭐ {time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
    level="INFO",
    rotation="1 day",
    retention="7 days"
)

# 关键事件星语
def log_nova_birth(nova_id: str, star_type: str):
    logger.info(f"🌟 新星诞生 | ID: {nova_id} | 目标星: {star_type}")

def log_star_shine(nova_id: str, star_type: str):
    logger.info(f"💫 星光闪耀 | 任务: {nova_id} | 星: {star_type}")

def log_constellation_complete(nova_id: str):
    logger.info(f"✨ 星座完成 | 任务: {nova_id}")

def log_star_fade(nova_id: str, error: str):
    logger.error(f"🌑 星光暗淡 | 任务: {nova_id} | 原因: {error}")

9. 实现路线图(星图里程碑)

阶段里程碑内容
🌑 朔月星核初现进程发现、窗口句柄获取、基础 UI
🌒 娥眉星光通联UI Automation 文本注入/读取
🌓 上弦星轨流转任务队列、状态管理、星轨引擎
🌔 盈凸星语交响WebSocket 实时推送、对话监控
🌕 满月群星闪耀多星协同(星座)、结果对比
🌖 亏凸星图完善插件系统、自定义星体
🌗 下弦星辉永驻历史记录、统计分析
🌘 残月星辰大海正式发布、文档完善

这个名字体系将整个项目包装成一个完整的“星空叙事”,每个技术概念都有对应的诗意表达,让项目不仅实用,更具有独特的品牌识别度。

electron的程序,现在还能拿到句柄,拿到它里面的窗体、文本等信息吗?

可以,但难度取决于 Electron 版本和渲染模式。以下是 Windows 下的实际情况和解决方案:


1. Electron 窗口结构

┌─────────────────────────────────────────┐
│  Electron 主窗口 (HWND)                  │
│  ┌───────────────────────────────────┐  │
│  │  Chromium 渲染区                    │  │
│  │  (Chrome_WidgetWin_1 或            │  │
│  │   Chrome_RenderWidgetHostHWND)     │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │  Web 内容 (DOM)              │  │  │
│  │  │  · <textarea>               │  │  │
│  │  │  · contenteditable div      │  │  │
│  │  │  · Monaco Editor (Canvas)   │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

2. 句柄获取(仍然可以)

2.1 获取顶层窗口句柄

import win32gui
import win32process
import psutil

def get_electron_window_hwnd(process_name="trae.exe"):
    """获取 Electron 应用的主窗口句柄"""
    for proc in psutil.process_iter(['pid', 'name']):
        if proc.info['name'].lower() == process_name.lower():
            pid = proc.info['pid']
            
            def callback(hwnd, hwnds):
                if win32gui.IsWindowVisible(hwnd):
                    _, found_pid = win32process.GetWindowThreadProcessId(hwnd)
                    if found_pid == pid:
                        hwnds.append(hwnd)
                return True
            
            hwnds = []
            win32gui.EnumWindows(callback, hwnds)
            
            # 通常取第一个可见窗口
            if hwnds:
                return hwnds[0], pid
    
    return None, None

2.2 获取 Chromium 渲染子窗口

Electron 内部有子窗口层级:

def get_chromium_render_hwnd(parent_hwnd):
    """获取 Chromium 渲染区域的句柄"""
    children = []
    
    def callback(hwnd, lparam):
        class_name = win32gui.GetClassName(hwnd)
        if class_name in ['Chrome_RenderWidgetHostHWND', 'Chrome_WidgetWin_1']:
            children.append(hwnd)
        return True
    
    win32gui.EnumChildWindows(parent_hwnd, callback, None)
    return children

3. 获取文本内容(三种方案,按成功率排序)

方案一:UI Automation(最推荐,成功率 90%+)

import uiautomation as uia

def get_electron_text_uia(hwnd: int):
    """通过 UI Automation 获取 Electron 中的文本"""
    try:
        # 从窗口句柄获取 UIA 元素
        window = uia.ControlFromHandle(hwnd)
        
        # 方法1:获取所有 Text 控件(适用于渲染后的文本块)
        text_elements = []
        for control in window.GetChildren():
            if control.ControlTypeName == 'TextControl':
                text_elements.append(control.Name)
        
        # 方法2:查找编辑框(输入区域)
        edits = []
        for control, depth in uia.WalkTree(window, maxDepth=5):
            if control.ControlTypeName == 'EditControl':
                edits.append({
                    'name': control.Name,
                    'value': control.GetValuePattern().Value if control.GetValuePattern() else None,
                    'text': control.GetTextPattern().DocumentRange.GetText() if control.GetTextPattern() else None,
                })
        
        return {
            'text_blocks': text_elements,
            'edit_controls': edits
        }
    except Exception as e:
        return {'error': str(e)}

方案二:微软无障碍 API(需 Electron 应用支持)

有些 Electron 应用(如 VS Code)启用了无障碍支持:

import win32gui
import win32con

def get_text_via_accessible(hwnd: int):
    """通过 MSAA (Active Accessibility) 获取文本"""
    import pythoncom
    from win32com.client import Dispatch
    
    pythoncom.CoInitialize()
    
    try:
        # 获取 MSAA 对象
        acc = Dispatch('oleacc.AccessibleObjectFromWindow')(hwnd, 0)
        
        def walk_acc(obj, depth=0):
            if depth > 10:
                return []
            results = []
            try:
                if obj.accName(0):
                    results.append(obj.accName(0))
                if obj.accValue(0):
                    results.append(obj.accValue(0))
            except:
                pass
            
            # 遍历子元素
            try:
                for i in range(obj.accChildCount):
                    child = obj.accChild(i + 1)
                    results.extend(walk_acc(child, depth + 1))
            except:
                pass
            
            return results
        
        return walk_acc(acc)
    
    except Exception as e:
        return {'error': str(e)}
    finally:
        pythoncom.CoUninitialize()

方案三:注入 JavaScript(终极方案,成功率 99%)

通过 Chrome DevTools Protocol (CDP) 远程调试端口执行 JS:

import websocket
import json
import requests

class ElectronCDPClient:
    """通过 Chrome DevTools Protocol 与 Electron 通信"""
    
    def __init__(self, debug_port=9222):
        self.debug_port = debug_port
        self.ws = None
        
    def connect(self):
        """连接到 Electron 的调试端口"""
        # 首先获取可调试的页面列表
        response = requests.get(f'http://localhost:{self.debug_port}/json')
        pages = response.json()
        
        if pages:
            # 连接第一个页面的 WebSocket
            ws_url = pages[0]['webSocketDebuggerUrl']
            self.ws = websocket.create_connection(ws_url)
            return True
        return False
    
    def execute_js(self, script: str):
        """在 Electron 中执行 JavaScript"""
        msg = {
            'id': 1,
            'method': 'Runtime.evaluate',
            'params': {
                'expression': script,
                'returnByValue': True
            }
        }
        self.ws.send(json.dumps(msg))
        result = json.loads(self.ws.recv())
        return result
    
    def get_all_text(self):
        """获取页面所有文本内容"""
        return self.execute_js('document.body.innerText')
    
    def get_input_value(self, selector='textarea, [contenteditable="true"]'):
        """获取输入框内容"""
        js_code = f'''
        (() => {{
            const el = document.querySelector('{selector}');
            if (!el) return null;
            return el.value || el.innerText || el.textContent;
        }})()
        '''
        return self.execute_js(js_code)
    
    def set_input_value(self, text: str, selector='textarea, [contenteditable="true"]'):
        """设置输入框内容"""
        js_code = f'''
        (() => {{
            const el = document.querySelector('{selector}');
            if (!el) return false;
            
            // 设置值
            if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') {{
                el.value = {json.dumps(text)};
            }} else {{
                el.innerText = {json.dumps(text)};
            }}
            
            // 触发 input 事件
            el.dispatchEvent(new Event('input', {{ bubbles: true }}));
            return true;
        }})()
        '''
        return self.execute_js(js_code)

启动 Electron 时需开启调试端口

# 命令行参数
trae.exe --remote-debugging-port=9222

或在 electron.app.commandLine.appendSwitch 中设置:

app.commandLine.appendSwitch('remote-debugging-port', '9222');

4. 实际测试结果(以 Trae、VS Code 为例)

方法TraeVS CodeCursorAtomCode
UIA 获取文本块✅ 大部分✅ 支持好✅ 支持好✅ 支持好
UIA 获取编辑框值⚠️ 部分(Monaco 是 Canvas)⚠️ Monaco 不行⚠️ Monaco 不行⚠️ Monaco 不行
MSAA 文本✅ 有限✅ 支持✅ 支持✅ 支持
CDP JS 注入✅ 完美✅ 完美✅ 完美✅ 完美
剪贴板 + 模拟输入✅ 可行✅ 可行✅ 可行✅ 可行

5. 针对 Monaco Editor 的特殊处理

现代 AI Agent(Trae、VS Code、Cursor)普遍使用 Monaco Editor,它是 Canvas 渲染的,UIA 无法直接获取文本。解决方案:

通过 CDP 操作 Monaco

def get_monaco_content(client: ElectronCDPClient):
    """获取 Monaco Editor 的内容"""
    js_code = '''
    (() => {
        // 获取所有 Monaco Editor 实例
        const editors = monaco.editor.getEditors();
        if (!editors.length) return null;
        
        const editor = editors[0]; // 取第一个
        return {
            value: editor.getValue(),
            selection: editor.getSelection(),
            language: editor.getModel().getLanguageId()
        };
    })()
    '''
    return client.execute_js(js_code)

def set_monaco_content(client: ElectronCDPClient, text: str):
    """设置 Monaco Editor 的内容"""
    js_code = f'''
    (() => {{
        const editors = monaco.editor.getEditors();
        if (!editors.length) return false;
        
        const editor = editors[0];
        const model = editor.getModel();
        
        // 替换全部内容
        model.setValue({json.dumps(text)});
        return true;
    }})()
    '''
    return client.execute_js(js_code)

获取 AI 对话面板内容

def get_ai_chat_content(client: ElectronCDPClient):
    """获取 AI 对话面板的内容"""
    js_code = '''
    (() => {
        // Trae/Cursor 对话面板选择器(根据实际 DOM 结构调整)
        const selectors = [
            '.chat-message',           // 通用
            '.ai-response',            // Trae
            '[data-testid="chat-message"]', // VS Code
            '.copilot-chat-message',   // Copilot
        ];
        
        const messages = [];
        for (const sel of selectors) {
            document.querySelectorAll(sel).forEach(el => {
                messages.push({
                    text: el.innerText,
                    html: el.innerHTML
                });
            });
        }
        return messages;
    })()
    '''
    return client.execute_js(js_code)

6. 群星(Star)项目的推荐方案

class StarObserver:
    """星体观察器 - 多层次获取 Electron 内容"""
    
    def __init__(self, hwnd: int, debug_port: int = 9222):
        self.hwnd = hwnd
        self.debug_port = debug_port
        self.cdp_client = None
        self._init_cdp()
    
    def _init_cdp(self):
        """尝试初始化 CDP 连接"""
        try:
            self.cdp_client = ElectronCDPClient(self.debug_port)
            self.cdp_client.connect()
        except:
            self.cdp_client = None
    
    def capture_starlight(self) -> dict:
        """捕获星光 - 多层次获取内容"""
        result = {
            'text_blocks': [],      # 文本块(UIA 获取)
            'input_content': None,  # 输入框内容
            'ai_responses': [],     # AI 回复
            'monaco_content': None, # Monaco Editor 内容
            'method': 'unknown',    # 实际使用的方法
        }
        
        # 第一层:CDP(最精确)
        if self.cdp_client:
            try:
                result['ai_responses'] = get_ai_chat_content(self.cdp_client)
                result['monaco_content'] = get_monaco_content(self.cdp_client)
                result['method'] = 'cdp'
                return result
            except:
                pass
        
        # 第二层:UIA
        try:
            uia_result = get_electron_text_uia(self.hwnd)
            result.update(uia_result)
            result['method'] = 'uia'
            return result
        except:
            pass
        
        # 第三层:模拟输入 + 全选复制(最后手段)
        try:
            import pyautogui
            import pyperclip
            
            win32gui.SetForegroundWindow(self.hwnd)
            time.sleep(0.1)
            pyautogui.hotkey('ctrl', 'a')
            time.sleep(0.05)
            pyautogui.hotkey('ctrl', 'c')
            time.sleep(0.05)
            
            result['text_blocks'] = [pyperclip.paste()]
            result['method'] = 'clipboard'
        except:
            pass
        
        return result

7. 结论

可以拿到句柄和内容,但要分层处理:

  1. 窗口句柄:✅ 100% 可以获取
  2. 文本内容
    • 静态渲染文本:✅ UIA 可以获取
    • Monaco Editor:⚠️ 需要 CDP JavaScript 注入
    • AI 对话内容:⚠️ 需要 CDP 或 DOM 选择器
  3. 最佳方案:CDP 远程调试 + UIA 作为 fallback

关键前提:需要启动 Electron 应用时加上 --remote-debugging-port=9222 参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值