mirror of
https://github.com/PumpkinTTL/kiro-automation-toolkit.git
synced 2026-08-28 17:41:56 +08:00
初始提交:Kiro IDE 自动化安装完整项目
🚀 项目特性: - Windows Sandbox 环境支持 - 完整的自动化安装流程 - 智能等待和界面检测机制 - 多种登录方式支持 - 丰富的调试和分析工具 📁 项目结构: - sandbox/ - 项目根目录 - sandbox_files/ - 沙盒环境脚本 - start_sandbox.ps1 - 主启动脚本 - sandbox_config.wsb - Sandbox 配置 🛠️ 核心功能: - 自动化 Kiro IDE 安装 - 登录界面自动化 - 窗口和控件分析 - 详细的进度反馈 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(gh auth status:*)",
|
||||
"Bash(git init:*)",
|
||||
"Bash(gh repo delete:*)",
|
||||
"Bash(gh auth refresh:*)",
|
||||
"Read(//f/Code/**)",
|
||||
"Bash(git add:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# 排除大型安装文件
|
||||
*.exe
|
||||
python-*.exe
|
||||
kiro.exe
|
||||
sandbox_files/*.exe
|
||||
|
||||
# 排除日志文件
|
||||
*.log
|
||||
*.txt
|
||||
sandbox_files/*.log
|
||||
sandbox_files/*.txt
|
||||
sandbox_files/install_log.txt
|
||||
|
||||
# 排除临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# 排除备份文件
|
||||
*副本*
|
||||
*-不带*
|
||||
sandbox_files/*副本*
|
||||
sandbox_files/*-不带*
|
||||
|
||||
# 排除配置文件(如果包含敏感信息)
|
||||
config.json
|
||||
配置.json
|
||||
sandbox_files/config.json
|
||||
sandbox_files/配置.json
|
||||
|
||||
# 排除IDE文件
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# 排除系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 排除Windows Sandbox相关
|
||||
*.wsb
|
||||
@@ -0,0 +1,184 @@
|
||||
# Kiro IDE 自动化安装项目
|
||||
|
||||
这是一个完整的 Kiro IDE 自动化安装和配置项目,包含 Windows Sandbox 环境配置和自动化脚本。
|
||||
|
||||
## 🚀 项目概述
|
||||
|
||||
本项目提供了一套完整的解决方案,用于在 Windows Sandbox 环境中自动化安装和配置 Kiro IDE,包括:
|
||||
|
||||
- Windows Sandbox 环境配置
|
||||
- 自动化安装脚本
|
||||
- 登录自动化工具
|
||||
- 调试和分析工具
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
sandbox/
|
||||
├── README.md # 项目说明文档
|
||||
├── start_sandbox.ps1 # 主启动脚本
|
||||
├── sandbox_config.wsb # Windows Sandbox 配置文件
|
||||
├── promat.txt # 项目提示文件
|
||||
├── .claude/ # Claude Code 配置目录
|
||||
└── sandbox_files/ # 沙盒环境中的脚本文件
|
||||
├── README.md # 脚本详细说明
|
||||
├── install.ps1 # Kiro 安装脚本
|
||||
├── automate_kiro.py # 核心自动化脚本
|
||||
├── kiro_login_automation.py # 登录自动化脚本
|
||||
├── debug_kiro.py # 调试工具
|
||||
├── window_analyzer.py # 窗口分析工具
|
||||
├── quick_analyzer.py # 快速分析工具
|
||||
└── kiro_step1.py # 辅助脚本
|
||||
```
|
||||
|
||||
## 🛠️ 使用方法
|
||||
|
||||
### 方法1:完整的 Sandbox 环境(推荐)
|
||||
```powershell
|
||||
# 启动 Windows Sandbox 并自动运行安装
|
||||
powershell -ExecutionPolicy Bypass -File .\start_sandbox.ps1
|
||||
```
|
||||
|
||||
### 方法2:直接在主机运行
|
||||
```powershell
|
||||
# 进入 sandbox_files 目录
|
||||
cd sandbox_files
|
||||
|
||||
# 运行完整安装流程
|
||||
powershell -ExecutionPolicy Bypass -File .\install.ps1
|
||||
```
|
||||
|
||||
### 方法3:仅运行登录自动化
|
||||
```bash
|
||||
cd sandbox_files
|
||||
python kiro_login_automation.py
|
||||
```
|
||||
|
||||
## ⚙️ 系统要求
|
||||
|
||||
- Windows 10/11 Pro/Enterprise(支持 Windows Sandbox)
|
||||
- PowerShell 5.0+
|
||||
- Python 3.7+(会自动安装)
|
||||
- 所需 Python 包(会自动安装):
|
||||
- `pywinauto`
|
||||
- `requests`
|
||||
|
||||
## 🔧 功能特性
|
||||
|
||||
### 🎯 自动化安装
|
||||
- 自动下载 Python 和 Kiro IDE
|
||||
- 智能检测安装进度
|
||||
- 自动处理安装界面交互
|
||||
|
||||
### 🧠 智能等待机制
|
||||
- 检测界面加载状态
|
||||
- 窗口标题变化监控
|
||||
- 控件数量动态检测
|
||||
- 登录按钮可用性验证
|
||||
|
||||
### 🔐 登录自动化
|
||||
- 支持多种登录方式:
|
||||
- Google 账户登录
|
||||
- GitHub 账户登录(默认)
|
||||
- AWS Builder ID 登录
|
||||
- 组织身份登录
|
||||
- 3秒倒计时自动选择
|
||||
- 详细的按钮检测和点击
|
||||
|
||||
### 🛠️ 调试工具
|
||||
- 窗口结构分析
|
||||
- 控件信息查看
|
||||
- 界面状态监控
|
||||
- 详细的日志输出
|
||||
|
||||
## 📊 工作流程
|
||||
|
||||
1. **环境准备**:启动 Windows Sandbox 或准备主机环境
|
||||
2. **依赖安装**:自动下载并安装 Python 和依赖包
|
||||
3. **Kiro 安装**:下载并安装 Kiro IDE
|
||||
4. **界面等待**:智能检测登录界面加载完成
|
||||
5. **登录自动化**:自动检测并点击登录按钮
|
||||
6. **完成**:打开浏览器完成登录流程
|
||||
|
||||
## 🐛 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **PowerShell 执行策略错误**
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
|
||||
```
|
||||
|
||||
2. **Windows Sandbox 不可用**
|
||||
- 确保使用 Windows 10/11 Pro/Enterprise
|
||||
- 启用 Windows Sandbox 功能
|
||||
|
||||
3. **界面检测失败**
|
||||
- 等待更长时间让界面完全加载
|
||||
- 使用调试工具分析界面状态
|
||||
|
||||
4. **登录按钮未找到**
|
||||
- 手动运行 `python kiro_login_automation.py`
|
||||
- 检查界面是否完全加载
|
||||
|
||||
### 调试工具使用
|
||||
|
||||
```bash
|
||||
# 分析当前窗口结构
|
||||
python window_analyzer.py
|
||||
|
||||
# 快速检查界面状态
|
||||
python quick_analyzer.py
|
||||
|
||||
# 详细调试信息
|
||||
python debug_kiro.py
|
||||
```
|
||||
|
||||
## 📝 配置说明
|
||||
|
||||
### Windows Sandbox 配置 (sandbox_config.wsb)
|
||||
- 启用网络访问
|
||||
- 映射主机文件夹
|
||||
- 配置内存和处理器
|
||||
|
||||
### 登录选项配置
|
||||
- 默认选择:GitHub 登录
|
||||
- 自动选择时间:3秒
|
||||
- 支持手动选择 1-5 选项
|
||||
|
||||
## 🔄 更新日志
|
||||
|
||||
- **v1.0** - 初始版本,基本安装和登录功能
|
||||
- **v1.1** - 添加 Windows Sandbox 支持
|
||||
- **v1.2** - 智能等待机制,提高成功率
|
||||
- **v1.3** - 优化登录按钮检测,支持多种登录方式
|
||||
- **v1.4** - 修复等待时间问题,提高界面检测准确性
|
||||
- **v1.5** - 完整项目结构,添加调试工具
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
欢迎提交 Issue 和 Pull Request 来改进这个项目!
|
||||
|
||||
### 贡献指南
|
||||
1. Fork 本项目
|
||||
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 打开 Pull Request
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
MIT License - 详见 LICENSE 文件
|
||||
|
||||
## ⚠️ 免责声明
|
||||
|
||||
此项目仅用于学习和自动化目的。请确保:
|
||||
- 遵守相关软件的使用条款
|
||||
- 在安全的环境中测试
|
||||
- 不用于恶意目的
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
- 感谢 Anthropic 提供的 Claude Code 开发环境
|
||||
- 感谢 pywinauto 项目提供的 Windows 自动化支持
|
||||
- 感谢所有贡献者和测试用户
|
||||
@@ -0,0 +1,31 @@
|
||||
# 排除大型安装文件
|
||||
*.exe
|
||||
python-*.exe
|
||||
kiro.exe
|
||||
|
||||
# 排除日志文件
|
||||
*.log
|
||||
*.txt
|
||||
install_log.txt
|
||||
|
||||
# 排除临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# 排除备份文件
|
||||
*副本*
|
||||
*-不带*
|
||||
|
||||
# 排除配置文件(如果包含敏感信息)
|
||||
config.json
|
||||
配置.json
|
||||
|
||||
# 排除IDE文件
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# 排除系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,95 @@
|
||||
# Kiro 自动化安装和登录脚本
|
||||
|
||||
这是一个用于自动化安装和配置 Kiro IDE 的 PowerShell 和 Python 脚本集合。
|
||||
|
||||
## 🚀 功能特性
|
||||
|
||||
- **自动化安装**:自动下载并安装 Kiro IDE
|
||||
- **智能等待**:检测安装进度和界面加载状态
|
||||
- **登录自动化**:自动检测并点击登录按钮
|
||||
- **多种登录方式**:支持 Google、GitHub、AWS Builder ID 等
|
||||
- **用户友好**:提供详细的进度反馈和错误处理
|
||||
|
||||
## 📁 文件说明
|
||||
|
||||
### 主要脚本
|
||||
- `start_sandbox.ps1` - 主启动脚本,启动整个自动化流程
|
||||
- `install.ps1` - Kiro 安装脚本
|
||||
- `automate_kiro.py` - 核心自动化脚本,处理安装过程和界面检测
|
||||
- `kiro_login_automation.py` - 登录自动化脚本
|
||||
|
||||
### 辅助工具
|
||||
- `debug_kiro.py` - 调试工具,用于分析 Kiro 界面
|
||||
- `window_analyzer.py` - 窗口分析工具
|
||||
- `quick_analyzer.py` - 快速界面分析工具
|
||||
|
||||
## 🛠️ 使用方法
|
||||
|
||||
### 方法1:完整自动化流程
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\start_sandbox.ps1
|
||||
```
|
||||
|
||||
### 方法2:仅安装 Kiro
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\install.ps1
|
||||
```
|
||||
|
||||
### 方法3:仅运行登录自动化
|
||||
```bash
|
||||
python kiro_login_automation.py
|
||||
```
|
||||
|
||||
## ⚙️ 系统要求
|
||||
|
||||
- Windows 10/11
|
||||
- PowerShell 5.0+
|
||||
- Python 3.7+
|
||||
- 所需 Python 包:
|
||||
- `pywinauto`
|
||||
- `requests`
|
||||
|
||||
## 🔧 配置选项
|
||||
|
||||
脚本支持以下登录方式:
|
||||
1. Google 账户登录
|
||||
2. GitHub 账户登录(默认)
|
||||
3. AWS Builder ID 登录
|
||||
4. 组织身份登录
|
||||
|
||||
默认选择 GitHub 登录,3秒后自动执行。
|
||||
|
||||
## 📊 工作流程
|
||||
|
||||
1. **安装阶段**:下载并安装 Kiro IDE
|
||||
2. **等待阶段**:智能检测界面加载状态
|
||||
3. **登录阶段**:自动检测并点击登录按钮
|
||||
4. **完成**:打开浏览器完成登录流程
|
||||
|
||||
## 🐛 故障排除
|
||||
|
||||
如果遇到问题:
|
||||
|
||||
1. **权限问题**:使用管理员权限运行 PowerShell
|
||||
2. **执行策略**:使用 `-ExecutionPolicy Bypass` 参数
|
||||
3. **界面检测失败**:等待更长时间让界面完全加载
|
||||
4. **登录按钮未找到**:手动运行 `python kiro_login_automation.py`
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
- **v1.0** - 初始版本,基本安装和登录功能
|
||||
- **v1.1** - 添加智能等待机制,提高成功率
|
||||
- **v1.2** - 优化登录按钮检测,支持多种登录方式
|
||||
- **v1.3** - 修复等待时间问题,提高界面检测准确性
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
欢迎提交 Issue 和 Pull Request 来改进这个项目!
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
MIT License - 详见 LICENSE 文件
|
||||
|
||||
## ⚠️ 免责声明
|
||||
|
||||
此脚本仅用于学习和自动化目的。请确保遵守相关软件的使用条款。
|
||||
@@ -0,0 +1,644 @@
|
||||
import time
|
||||
import sys
|
||||
from pywinauto import Application
|
||||
from pywinauto.keyboard import send_keys
|
||||
|
||||
# 全局超时设置
|
||||
CONNECT_TIMEOUT = 30
|
||||
WINDOW_TIMEOUT = 60
|
||||
|
||||
def connect_to_install_window():
|
||||
"""连接到安装提醒窗口"""
|
||||
print("Connecting to install prompt window...")
|
||||
|
||||
# 尝试连接到"安装"窗口
|
||||
connection_methods = [
|
||||
lambda: Application(backend="uia").connect(title="安装", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title="安装", timeout=5),
|
||||
]
|
||||
|
||||
for i, method in enumerate(connection_methods, 1):
|
||||
try:
|
||||
print(f"Trying connection method {i}...")
|
||||
app = method()
|
||||
print(f"Connected to install window! PID: {app.process}")
|
||||
return app
|
||||
except Exception as e:
|
||||
print(f"Method {i} failed: {str(e)[:50]}...")
|
||||
continue
|
||||
|
||||
print("Failed to connect to install window")
|
||||
return None
|
||||
|
||||
def wait_for_control(app, criteria, timeout=WINDOW_TIMEOUT):
|
||||
"""通用控件查找(支持 class_name, control_type, name 等)"""
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
try:
|
||||
ctrl = app.top_window_().child_window(**criteria)
|
||||
if ctrl.exists() and ctrl.is_visible():
|
||||
ctrl.set_focus()
|
||||
print(f"[Success] Found control: {criteria}")
|
||||
return ctrl
|
||||
# 尝试所有顶级窗口
|
||||
for win in app.windows():
|
||||
try:
|
||||
ctrl = win.child_window(**criteria)
|
||||
if ctrl.exists() and ctrl.is_visible():
|
||||
win.set_focus()
|
||||
ctrl.set_focus()
|
||||
print(f"[Success] Found control in window: {win.window_text()}")
|
||||
return ctrl
|
||||
except:
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
time.sleep(0.8)
|
||||
print(f"[Failed] Control not found: {criteria}")
|
||||
return None
|
||||
|
||||
def click_button_by_text(text):
|
||||
"""点击文本为指定内容的按钮"""
|
||||
return wait_for_control(app, {
|
||||
"control_type": "Button",
|
||||
"title": text,
|
||||
"visible_only": True
|
||||
})
|
||||
|
||||
def click_button_by_class(class_name):
|
||||
"""点击 class_name 的按钮"""
|
||||
return wait_for_control(app, {
|
||||
"class_name": class_name,
|
||||
"control_type": "Button"
|
||||
})
|
||||
|
||||
def select_radio_by_name(name):
|
||||
"""选择单选框"""
|
||||
return wait_for_control(app, {
|
||||
"control_type": "RadioButton",
|
||||
"name": name
|
||||
})
|
||||
|
||||
# 临时调试:打印所有控件
|
||||
def dump_controls():
|
||||
for win in app.windows():
|
||||
print(f"\n--- Window: {win.window_text()} ---")
|
||||
try:
|
||||
win.print_control_identifiers()
|
||||
except:
|
||||
pass
|
||||
|
||||
def main():
|
||||
print("Starting kiro installation automation...")
|
||||
|
||||
global app
|
||||
|
||||
# === 步骤 1:处理"安装"提醒窗口 ===
|
||||
print("\n=== Step 1: Handle Install Prompt Window ===")
|
||||
app = connect_to_install_window()
|
||||
if not app:
|
||||
print("Failed to connect to install prompt window")
|
||||
return
|
||||
|
||||
# 查找并点击"确定"按钮 (ID: 1)
|
||||
print("Looking for '确定' button...")
|
||||
try:
|
||||
window = app.top_window()
|
||||
print(f"Window title: '{window.window_text()}'")
|
||||
|
||||
# 根据分析结果,确定按钮的ID是1,类名是Button
|
||||
confirm_button = window.child_window(auto_id="1", class_name="Button")
|
||||
|
||||
if confirm_button.exists() and confirm_button.is_visible():
|
||||
print("Found '确定' button, clicking...")
|
||||
try:
|
||||
# 确保窗口有焦点
|
||||
window.set_focus()
|
||||
time.sleep(0.5)
|
||||
|
||||
# 使用方法1:普通点击(已验证有效)
|
||||
confirm_button.click()
|
||||
print("Click executed, waiting for window change...")
|
||||
|
||||
# 等待更长时间并检查窗口变化
|
||||
for i in range(10): # 等待最多10秒
|
||||
time.sleep(1)
|
||||
try:
|
||||
# 检查原窗口是否还存在
|
||||
if not window.exists():
|
||||
print(f"✅ Original window closed after {i+1} seconds")
|
||||
break
|
||||
# 检查窗口标题是否改变
|
||||
current_title = window.window_text()
|
||||
if current_title != "安装":
|
||||
print(f"✅ Window title changed to: '{current_title}'")
|
||||
break
|
||||
except:
|
||||
print(f"✅ Window changed after {i+1} seconds")
|
||||
break
|
||||
|
||||
if i == 9:
|
||||
print("⚠️ Window didn't change after 10 seconds, but click was executed")
|
||||
|
||||
print("✅ Successfully clicked '确定' button!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Click failed: {e}")
|
||||
return
|
||||
|
||||
else:
|
||||
print("❌ '确定' button not found or not visible")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error handling install prompt: {e}")
|
||||
return
|
||||
|
||||
print("✅ Step 1 completed - should now be in license agreement window")
|
||||
|
||||
# === 步骤 2:处理许可协议界面 ===
|
||||
print("\n=== Step 2: Handle License Agreement Window ===")
|
||||
|
||||
# 重新连接到协议窗口
|
||||
print("Connecting to license agreement window...")
|
||||
|
||||
# 等待协议窗口出现
|
||||
print("Waiting for license window to appear...")
|
||||
time.sleep(2)
|
||||
|
||||
# 连接协议窗口(使用方法3:win32 + 正则匹配)
|
||||
try:
|
||||
print("Connecting to license window...")
|
||||
license_app = Application(backend="win32").connect(title_re=".*安装.*Kiro.*", timeout=10)
|
||||
print(f"✅ Connected to license window! PID: {license_app.process}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to connect to license window: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
license_window = license_app.top_window()
|
||||
print(f"License window title: '{license_window.window_text()}'")
|
||||
|
||||
# 步骤2.1:点击"我同意此协议"单选框(使用方法4:第一个单选框)
|
||||
print("Looking for '我同意此协议' radio button...")
|
||||
try:
|
||||
agree_radio = license_window.child_window(class_name="TNewRadioButton", found_index=0)
|
||||
if agree_radio.exists() and agree_radio.is_visible():
|
||||
radio_text = agree_radio.window_text() or "[无文本]"
|
||||
print(f"✅ Found radio button: '{radio_text}'")
|
||||
agree_radio.click()
|
||||
print("✅ Successfully clicked '我同意此协议'!")
|
||||
time.sleep(1) # 等待界面更新
|
||||
else:
|
||||
print("❌ '我同意此协议' radio button not found")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Error clicking radio button: {e}")
|
||||
return
|
||||
|
||||
# 步骤2.2:点击"下一步"按钮
|
||||
print("Looking for '下一步' button...")
|
||||
|
||||
# 等待按钮启用(选择协议后按钮会启用)
|
||||
time.sleep(1)
|
||||
|
||||
# 点击"下一步"按钮(使用方法4:第一个按钮)
|
||||
try:
|
||||
next_button = license_window.child_window(class_name="TNewButton", found_index=0)
|
||||
if next_button.exists() and next_button.is_visible() and next_button.is_enabled():
|
||||
button_text = next_button.window_text() or "[无文本]"
|
||||
print(f"✅ Found button: '{button_text}'")
|
||||
next_button.click()
|
||||
print("✅ Successfully clicked '下一步' button!")
|
||||
time.sleep(3) # 等待进入下一个安装步骤
|
||||
else:
|
||||
print("❌ '下一步' button not found or disabled")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Error clicking next button: {e}")
|
||||
return
|
||||
|
||||
print("✅ Step 2 completed - should now be in next installation step")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error in Step 2: {e}")
|
||||
return
|
||||
|
||||
print("🎉 Second step automation finished!")
|
||||
|
||||
# === 步骤 3:处理选择目标位置界面 ===
|
||||
print("\n=== Step 3: Handle Target Location Selection ===")
|
||||
|
||||
# 等待目标位置界面出现
|
||||
print("Waiting for target location window to appear...")
|
||||
time.sleep(2)
|
||||
|
||||
# 连接到安装窗口(使用成功的方法5)
|
||||
try:
|
||||
print("Connecting to target location window...")
|
||||
target_app = Application(backend="uia").connect(title_re=".*安装.*", timeout=10)
|
||||
print(f"✅ Connected to target location window! PID: {target_app.process}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to connect to target location window: {e}")
|
||||
return
|
||||
|
||||
# 获取窗口
|
||||
try:
|
||||
target_window = target_app.windows()[0] # 直接获取第一个窗口
|
||||
print(f"✅ Got window: '{target_window.window_text()}'")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to get window: {e}")
|
||||
return
|
||||
|
||||
# 查找并点击"下一步"按钮
|
||||
try:
|
||||
print("Looking for '下一步' button...")
|
||||
controls = target_window.descendants()
|
||||
|
||||
# 通过类名和文本查找按钮(最有效的方法)
|
||||
for ctrl in controls:
|
||||
try:
|
||||
if (ctrl.class_name() == "TNewButton" and "下一步" in ctrl.window_text()):
|
||||
print(f"✅ Found button: '{ctrl.window_text()}'")
|
||||
ctrl.click()
|
||||
print("✅ Successfully clicked '下一步' button!")
|
||||
time.sleep(3)
|
||||
break
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
print("❌ '下一步' button not found")
|
||||
return
|
||||
|
||||
print("✅ Step 3 completed - should now be in next installation step")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error in Step 3: {e}")
|
||||
return
|
||||
|
||||
# === 步骤 4:处理选择开始菜单文件夹界面 ===
|
||||
print("\n=== Step 4: Handle Start Menu Folder Selection ===")
|
||||
|
||||
# 等待开始菜单文件夹界面出现
|
||||
print("Waiting for start menu folder window to appear...")
|
||||
time.sleep(2)
|
||||
|
||||
# 连接到安装窗口(使用成功的方法5)
|
||||
try:
|
||||
print("Connecting to start menu folder window...")
|
||||
menu_app = Application(backend="uia").connect(title_re=".*安装.*", timeout=10)
|
||||
print(f"✅ Connected to start menu folder window! PID: {menu_app.process}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to connect to start menu folder window: {e}")
|
||||
return
|
||||
|
||||
# 获取窗口
|
||||
try:
|
||||
menu_window = menu_app.windows()[0] # 直接获取第一个窗口
|
||||
print(f"✅ Got window: '{menu_window.window_text()}'")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to get window: {e}")
|
||||
return
|
||||
|
||||
# 查找并点击"下一步"按钮
|
||||
try:
|
||||
print("Looking for '下一步' button...")
|
||||
controls = menu_window.descendants()
|
||||
|
||||
# 通过类名和文本查找按钮(使用第三步成功的方法)
|
||||
for ctrl in controls:
|
||||
try:
|
||||
if (ctrl.class_name() == "TNewButton" and "下一步" in ctrl.window_text()):
|
||||
print(f"✅ Found button: '{ctrl.window_text()}'")
|
||||
ctrl.click()
|
||||
print("✅ Successfully clicked '下一步' button!")
|
||||
time.sleep(3)
|
||||
break
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
print("❌ '下一步' button not found")
|
||||
return
|
||||
|
||||
print("✅ Step 4 completed - should now be in next installation step")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error in Step 4: {e}")
|
||||
return
|
||||
|
||||
# === 步骤 5:处理选择附加任务界面 ===
|
||||
print("\n=== Step 5: Handle Additional Tasks Selection ===")
|
||||
|
||||
# 等待附加任务界面出现
|
||||
print("Waiting for additional tasks window to appear...")
|
||||
time.sleep(2)
|
||||
|
||||
# 连接到安装窗口(使用成功的方法5)
|
||||
try:
|
||||
print("Connecting to additional tasks window...")
|
||||
tasks_app = Application(backend="uia").connect(title_re=".*安装.*", timeout=10)
|
||||
print(f"✅ Connected to additional tasks window! PID: {tasks_app.process}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to connect to additional tasks window: {e}")
|
||||
return
|
||||
|
||||
# 获取窗口
|
||||
try:
|
||||
tasks_window = tasks_app.windows()[0] # 直接获取第一个窗口
|
||||
print(f"✅ Got window: '{tasks_window.window_text()}'")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to get window: {e}")
|
||||
return
|
||||
|
||||
# 查找并点击"下一步"按钮(使用默认的附加任务设置)
|
||||
try:
|
||||
print("Looking for '下一步' button...")
|
||||
controls = tasks_window.descendants()
|
||||
|
||||
# 通过类名和文本查找按钮(使用前面步骤成功的方法)
|
||||
for ctrl in controls:
|
||||
try:
|
||||
if (ctrl.class_name() == "TNewButton" and "下一步" in ctrl.window_text()):
|
||||
print(f"✅ Found button: '{ctrl.window_text()}'")
|
||||
ctrl.click()
|
||||
print("✅ Successfully clicked '下一步' button!")
|
||||
time.sleep(3)
|
||||
break
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
print("❌ '下一步' button not found")
|
||||
return
|
||||
|
||||
print("✅ Step 5 completed - should now be in next installation step")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error in Step 5: {e}")
|
||||
return
|
||||
|
||||
# === 步骤 6:处理准备安装界面 ===
|
||||
print("\n=== Step 6: Handle Ready to Install ===")
|
||||
|
||||
# 等待准备安装界面出现
|
||||
print("Waiting for ready to install window to appear...")
|
||||
time.sleep(2)
|
||||
|
||||
# 连接到安装窗口(使用成功的方法5)
|
||||
try:
|
||||
print("Connecting to ready to install window...")
|
||||
install_app = Application(backend="uia").connect(title_re=".*安装.*", timeout=10)
|
||||
print(f"✅ Connected to ready to install window! PID: {install_app.process}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to connect to ready to install window: {e}")
|
||||
return
|
||||
|
||||
# 获取窗口
|
||||
try:
|
||||
install_window = install_app.windows()[0] # 直接获取第一个窗口
|
||||
print(f"✅ Got window: '{install_window.window_text()}'")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to get window: {e}")
|
||||
return
|
||||
|
||||
# 查找并点击"安装"按钮
|
||||
try:
|
||||
print("Looking for '安装' button...")
|
||||
controls = install_window.descendants()
|
||||
|
||||
# 通过类名和文本查找安装按钮
|
||||
for ctrl in controls:
|
||||
try:
|
||||
if (ctrl.class_name() == "TNewButton" and "安装" in ctrl.window_text()):
|
||||
print(f"✅ Found button: '{ctrl.window_text()}'")
|
||||
ctrl.click()
|
||||
print("✅ Successfully clicked '安装' button!")
|
||||
print("🚀 Installation started! This may take a few minutes...")
|
||||
time.sleep(5) # 等待安装开始
|
||||
break
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
print("❌ '安装' button not found")
|
||||
return
|
||||
|
||||
print("✅ Step 6 completed - installation process started")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error in Step 6: {e}")
|
||||
return
|
||||
|
||||
# === 步骤 7:等待安装完成并点击完成按钮 ===
|
||||
print("\n=== Step 7: Wait for Installation to Complete ===")
|
||||
print("⏳ Waiting for installation to complete...")
|
||||
|
||||
# 等待安装完成(通过检测"Kiro 安装完成"文本)
|
||||
max_wait_time = 300 # 最多等待5分钟
|
||||
wait_interval = 5 # 每5秒检查一次(更频繁的检查)
|
||||
|
||||
for i in range(0, max_wait_time, wait_interval):
|
||||
time.sleep(wait_interval)
|
||||
print(f"⏳ Installation in progress... ({i + wait_interval}s elapsed)")
|
||||
|
||||
# 检查是否出现"Kiro 安装完成"界面
|
||||
try:
|
||||
# 连接到安装窗口
|
||||
complete_app = Application(backend="uia").connect(title_re=".*安装.*", timeout=5)
|
||||
complete_window = complete_app.windows()[0]
|
||||
print(f"🔍 Connected to window: '{complete_window.window_text()}'")
|
||||
|
||||
# 查找"Kiro 安装完成"文本
|
||||
controls = complete_window.descendants()
|
||||
print(f"🔍 Found {len(controls)} controls to check")
|
||||
|
||||
installation_completed = False
|
||||
|
||||
# 调试:打印所有TNewStaticText控件的文本
|
||||
static_texts = []
|
||||
for ctrl in controls:
|
||||
try:
|
||||
if ctrl.class_name() == "TNewStaticText":
|
||||
text = ctrl.window_text()
|
||||
static_texts.append(text)
|
||||
print(f"🔍 TNewStaticText: '{text}'")
|
||||
|
||||
if "Kiro 安装完成" in text: # 使用包含检查,忽略换行符
|
||||
print("✅ Found 'Kiro 安装完成' text!")
|
||||
installation_completed = True
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not installation_completed:
|
||||
print(f"⚠️ 'Kiro 安装完成' not found. Found {len(static_texts)} TNewStaticText controls")
|
||||
continue
|
||||
|
||||
print("✅ Installation completed!")
|
||||
|
||||
# 查找并点击"完成"按钮
|
||||
print("Looking for '完成' button...")
|
||||
button_found = False
|
||||
|
||||
# 调试:打印所有TNewButton控件
|
||||
buttons = []
|
||||
for ctrl in controls:
|
||||
try:
|
||||
if ctrl.class_name() == "TNewButton":
|
||||
btn_text = ctrl.window_text()
|
||||
buttons.append(btn_text)
|
||||
print(f"🔍 TNewButton: '{btn_text}'")
|
||||
|
||||
if "完成" in btn_text:
|
||||
print(f"✅ Found button: '{btn_text}'")
|
||||
ctrl.click()
|
||||
print("✅ Successfully clicked '完成' button!")
|
||||
print("🎉 Kiro has been successfully installed and setup completed!")
|
||||
button_found = True
|
||||
|
||||
# 等待 Kiro 启动并调用登录自动化脚本
|
||||
print("\n⏳ 等待 Kiro 应用程序启动...")
|
||||
print("💡 等待登录界面完全加载...")
|
||||
|
||||
# 智能等待:检测登录界面是否真正准备就绪
|
||||
max_wait_time = 30 # 最多等待30秒
|
||||
wait_interval = 3 # 每3秒检查一次
|
||||
login_ready = False
|
||||
|
||||
for elapsed in range(0, max_wait_time, wait_interval):
|
||||
time.sleep(wait_interval)
|
||||
print(f"⏳ 检查登录界面状态... ({elapsed + wait_interval}s)")
|
||||
|
||||
try:
|
||||
test_app = Application(backend="uia").connect(title_re=".*Kiro.*", timeout=3)
|
||||
test_window = test_app.windows()[0]
|
||||
controls = test_window.descendants()
|
||||
control_count = len(controls)
|
||||
window_title = test_window.window_text()
|
||||
|
||||
print(f" 窗口标题: '{window_title}', 控件数: {control_count}")
|
||||
|
||||
# 检查是否是完整的登录界面
|
||||
if control_count > 100 and "Getting started" in window_title:
|
||||
# 进一步检查是否有登录按钮
|
||||
has_login_buttons = False
|
||||
for ctrl in controls:
|
||||
try:
|
||||
ctrl_name = getattr(ctrl.element_info, 'name', '') if hasattr(ctrl, 'element_info') else ''
|
||||
if "Sign in with" in ctrl_name:
|
||||
has_login_buttons = True
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if has_login_buttons:
|
||||
login_ready = True
|
||||
print(f"✅ 登录界面已完全准备就绪!")
|
||||
break
|
||||
else:
|
||||
print(f" 界面加载中,等待登录按钮出现...")
|
||||
else:
|
||||
print(f" 界面还在加载中...")
|
||||
|
||||
except Exception as e:
|
||||
print(f" 等待应用程序响应... ({str(e)[:30]}...)")
|
||||
|
||||
if not login_ready:
|
||||
print("⚠️ 等待超时,但继续尝试启动登录脚本...")
|
||||
else:
|
||||
print("🎯 界面准备就绪,启动登录脚本...")
|
||||
|
||||
# 调用登录自动化脚本
|
||||
print("🚀 启动登录自动化脚本...")
|
||||
try:
|
||||
import subprocess
|
||||
subprocess.run([sys.executable, "C:\\sandbox_files\\kiro_login_automation.py"],
|
||||
cwd="C:\\sandbox_files")
|
||||
print("✅ 登录自动化脚本执行完成")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 登录自动化脚本执行失败: {e}")
|
||||
print("💡 请手动运行: python kiro_login_automation.py")
|
||||
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"🔍 Button check error: {e}")
|
||||
continue
|
||||
|
||||
print(f"🔍 Found {len(buttons)} TNewButton controls: {buttons}")
|
||||
|
||||
if not button_found:
|
||||
print("⚠️ Installation completed but could not find '完成' button")
|
||||
print("🎉 Kiro has been successfully installed!")
|
||||
|
||||
# 即使没找到完成按钮,也尝试启动登录自动化
|
||||
print("\n⏳ 等待 Kiro 应用程序完全启动...")
|
||||
print("💡 等待登录界面完全加载(这可能需要15-30秒)...")
|
||||
|
||||
# 智能等待:检测登录界面是否真正准备就绪
|
||||
max_wait_time = 30 # 最多等待30秒
|
||||
wait_interval = 3 # 每3秒检查一次
|
||||
login_ready = False
|
||||
|
||||
for elapsed in range(0, max_wait_time, wait_interval):
|
||||
time.sleep(wait_interval)
|
||||
print(f"⏳ 检查登录界面状态... ({elapsed + wait_interval}s)")
|
||||
|
||||
try:
|
||||
test_app = Application(backend="uia").connect(title_re=".*Kiro.*", timeout=3)
|
||||
test_window = test_app.windows()[0]
|
||||
controls = test_window.descendants()
|
||||
control_count = len(controls)
|
||||
window_title = test_window.window_text()
|
||||
|
||||
print(f" 窗口标题: '{window_title}', 控件数: {control_count}")
|
||||
|
||||
# 检查是否是完整的登录界面
|
||||
if control_count > 100 and "Getting started" in window_title:
|
||||
# 进一步检查是否有登录按钮
|
||||
has_login_buttons = False
|
||||
for ctrl in controls:
|
||||
try:
|
||||
ctrl_name = getattr(ctrl.element_info, 'name', '') if hasattr(ctrl, 'element_info') else ''
|
||||
if "Sign in with" in ctrl_name:
|
||||
has_login_buttons = True
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if has_login_buttons:
|
||||
login_ready = True
|
||||
print(f"✅ 登录界面已完全准备就绪!")
|
||||
break
|
||||
else:
|
||||
print(f" 界面加载中,等待登录按钮出现...")
|
||||
else:
|
||||
print(f" 界面还在加载中...")
|
||||
|
||||
except Exception as e:
|
||||
print(f" 等待应用程序响应... ({str(e)[:30]}...)")
|
||||
|
||||
if not login_ready:
|
||||
print("⚠️ 等待超时,但继续尝试启动登录脚本...")
|
||||
else:
|
||||
print("🎯 界面准备就绪,启动登录脚本...")
|
||||
|
||||
print("🚀 启动登录自动化脚本...")
|
||||
try:
|
||||
import subprocess
|
||||
subprocess.run([sys.executable, "C:\\sandbox_files\\kiro_login_automation.py"],
|
||||
cwd="C:\\sandbox_files")
|
||||
print("✅ 登录自动化脚本执行完成")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 登录自动化脚本执行失败: {e}")
|
||||
print("💡 请手动运行: python kiro_login_automation.py")
|
||||
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
# 如果连接失败,可能安装还在进行中
|
||||
print(f"🔍 Connection attempt failed: {str(e)[:50]}...")
|
||||
continue
|
||||
|
||||
print("⚠️ Installation may still be in progress after 5 minutes")
|
||||
print("🎉 Automation completed! Please check the installation status manually.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Kiro进程调试脚本
|
||||
用于诊断为什么找不到kiro.exe的窗口
|
||||
"""
|
||||
|
||||
import time
|
||||
import psutil
|
||||
from pywinauto import Application, Desktop
|
||||
from pywinauto.findwindows import find_elements
|
||||
|
||||
def debug_kiro_process():
|
||||
print("🔧 Kiro进程调试器")
|
||||
print("="*50)
|
||||
|
||||
# 1. 检查kiro.exe进程
|
||||
print("1️⃣ 检查kiro.exe进程...")
|
||||
kiro_processes = []
|
||||
for proc in psutil.process_iter(['pid', 'name', 'exe']):
|
||||
try:
|
||||
if 'kiro' in proc.info['name'].lower():
|
||||
kiro_processes.append(proc.info)
|
||||
except:
|
||||
continue
|
||||
|
||||
if not kiro_processes:
|
||||
print("❌ 未找到kiro.exe进程")
|
||||
return
|
||||
|
||||
print(f"✅ 找到 {len(kiro_processes)} 个kiro进程:")
|
||||
for proc in kiro_processes:
|
||||
print(f" PID: {proc['pid']}, 名称: {proc['name']}, 路径: {proc.get('exe', '未知')}")
|
||||
|
||||
# 2. 尝试连接每个进程
|
||||
print("\n2️⃣ 尝试连接进程...")
|
||||
for proc in kiro_processes:
|
||||
pid = proc['pid']
|
||||
print(f"\n🔗 连接PID {pid}...")
|
||||
|
||||
try:
|
||||
# 尝试UIA后端
|
||||
app_uia = Application(backend="uia").connect(process=pid)
|
||||
print(f"✅ UIA连接成功")
|
||||
|
||||
windows_uia = app_uia.windows()
|
||||
print(f" UIA窗口数: {len(windows_uia)}")
|
||||
|
||||
for i, win in enumerate(windows_uia):
|
||||
try:
|
||||
title = win.window_text()
|
||||
class_name = win.class_name()
|
||||
visible = win.is_visible()
|
||||
print(f" 窗口{i+1}: '{title}' (类名: {class_name}, 可见: {visible})")
|
||||
except Exception as e:
|
||||
print(f" 窗口{i+1}: 获取信息失败 - {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ UIA连接失败: {e}")
|
||||
|
||||
try:
|
||||
# 尝试Win32后端
|
||||
app_win32 = Application(backend="win32").connect(process=pid)
|
||||
print(f"✅ Win32连接成功")
|
||||
|
||||
windows_win32 = app_win32.windows()
|
||||
print(f" Win32窗口数: {len(windows_win32)}")
|
||||
|
||||
for i, win in enumerate(windows_win32):
|
||||
try:
|
||||
title = win.window_text()
|
||||
class_name = win.class_name()
|
||||
visible = win.is_visible()
|
||||
print(f" 窗口{i+1}: '{title}' (类名: {class_name}, 可见: {visible})")
|
||||
except Exception as e:
|
||||
print(f" 窗口{i+1}: 获取信息失败 - {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Win32连接失败: {e}")
|
||||
|
||||
# 3. 搜索所有包含kiro的窗口
|
||||
print("\n3️⃣ 搜索所有包含'kiro'的窗口...")
|
||||
try:
|
||||
elements = find_elements(title_re=".*kiro.*", backend="uia")
|
||||
print(f"✅ 找到 {len(elements)} 个匹配的窗口元素")
|
||||
for i, elem in enumerate(elements):
|
||||
print(f" 元素{i+1}: {elem}")
|
||||
except Exception as e:
|
||||
print(f"❌ 搜索失败: {e}")
|
||||
|
||||
# 4. 列出所有可见窗口
|
||||
print("\n4️⃣ 列出所有可见窗口(前10个)...")
|
||||
try:
|
||||
desktop = Desktop(backend="uia")
|
||||
all_windows = desktop.windows()
|
||||
visible_windows = [w for w in all_windows if w.is_visible()]
|
||||
|
||||
print(f"✅ 总共 {len(all_windows)} 个窗口,其中 {len(visible_windows)} 个可见")
|
||||
|
||||
for i, win in enumerate(visible_windows[:10]):
|
||||
try:
|
||||
title = win.window_text()
|
||||
class_name = win.class_name()
|
||||
pid = win.process_id()
|
||||
print(f" {i+1}. '{title}' (类名: {class_name}, PID: {pid})")
|
||||
except:
|
||||
print(f" {i+1}. [无法获取窗口信息]")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 列出窗口失败: {e}")
|
||||
|
||||
# 5. 检查kiro.exe是否有界面
|
||||
print("\n5️⃣ 检查kiro.exe类型...")
|
||||
for proc in kiro_processes:
|
||||
pid = proc['pid']
|
||||
try:
|
||||
process = psutil.Process(pid)
|
||||
# 检查是否有窗口句柄
|
||||
print(f" PID {pid}:")
|
||||
print(f" 状态: {process.status()}")
|
||||
print(f" 创建时间: {time.ctime(process.create_time())}")
|
||||
|
||||
# 检查命令行参数
|
||||
try:
|
||||
cmdline = process.cmdline()
|
||||
print(f" 命令行: {' '.join(cmdline)}")
|
||||
except:
|
||||
print(f" 命令行: 无法获取")
|
||||
|
||||
except Exception as e:
|
||||
print(f" PID {pid}: 检查失败 - {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_kiro_process()
|
||||
@@ -0,0 +1,368 @@
|
||||
# 作者: Grok
|
||||
# 描述: 此脚本在 Windows Sandbox 中运行,显示安装进度,生成随机硬件指纹,安装 Python,验证版本,安装 pywinauto,并自动化安装 kiro.exe。
|
||||
# 注意: 需要互联网连接。假设 kiro.exe 是安装程序,automate_kiro.py 用于自动化。
|
||||
|
||||
# 设置编码以支持输出
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
chcp 65001 | Out-Null
|
||||
|
||||
# 设置窗口标题
|
||||
$host.UI.RawUI.WindowTitle = "Installation Progress - Do Not Close"
|
||||
|
||||
# 初始化日志文件
|
||||
$logPath = "C:\sandbox_files\install_log.txt"
|
||||
"安装日志开始: $(Get-Date)" | Out-File -FilePath $logPath -Encoding UTF8
|
||||
|
||||
# 显示头部
|
||||
Write-Host "`n`n=========================================" -ForegroundColor Blue
|
||||
Write-Host " Installation Script Running... " -ForegroundColor Blue
|
||||
Write-Host "=========================================`n" -ForegroundColor Blue
|
||||
"头部显示完成" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
# 进度初始化
|
||||
Write-Progress -Activity "Installation Progress" -Status "Initializing..." -PercentComplete 0
|
||||
|
||||
# 1. 生成硬件指纹
|
||||
Write-Progress -Activity "Installation Progress" -Status "Generating Hardware Fingerprints..." -PercentComplete 10
|
||||
Write-Host "[1/7] Generating Random Hardware Fingerprints..." -ForegroundColor Yellow
|
||||
"[1/7] 生成硬件指纹" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
$random = {
|
||||
param($min, $max)
|
||||
(Get-Random -Minimum $min -Maximum $max) -as [int]
|
||||
}
|
||||
|
||||
$hardwareFingerprints = @{
|
||||
"MAC_Address" = (1..6 | ForEach-Object { "{0:X2}" -f (& $random 0 256) }) -join ":"
|
||||
"CPU_ID" = [guid]::NewGuid().ToString()
|
||||
"Hard_Drive_Serial" = "HD{0:D8}" -f (& $random 10000000 99999999)
|
||||
"BIOS_Version" = "BIOS_v{0}.{1}" -f (& $random 1 10), (& $random 0 99)
|
||||
"BIOS_UUID" = [guid]::NewGuid().ToString()
|
||||
"Motherboard_Serial" = "MB_SN_{0}" -f [guid]::NewGuid().ToString().Substring(0,12)
|
||||
"Motherboard_Model" = "Model_{0}" -f (& $random 1000 9999)
|
||||
"RAM_ID" = "RAM_SN_{0}" -f (& $random 100000 999999)
|
||||
"GPU_ID" = "GPU_{0}" -f [guid]::NewGuid().ToString().Substring(0,8)
|
||||
"GPU_Model" = "NVIDIA_GTX_{0}" -f (& $random 1000 4000)
|
||||
"System_UUID" = [guid]::NewGuid().ToString()
|
||||
"System_Product_ID" = "Product_{0}" -f [guid]::NewGuid().ToString().Substring(0,10)
|
||||
"Computer_Name" = "PC_{0:D5}" -f (& $random 10000 99999)
|
||||
"User_Name" = "User_{0}" -f (& $random 1000 9999)
|
||||
"OS_Build_Number" = & $random 19000 26000
|
||||
"Network_Adapter_ID" = [guid]::NewGuid().ToString()
|
||||
"IP_Address" = "{0}.{1}.{2}.{3}" -f (& $random 1 255), (& $random 0 255), (& $random 0 255), (& $random 1 254)
|
||||
"USB_Device_ID" = "USB_{0}" -f [guid]::NewGuid().ToString().Substring(0,8)
|
||||
"Monitor_Serial" = "MON_SN_{0}" -f (& $random 100000 999999)
|
||||
"Monitor_Model" = "Display_{0}" -f (& $random 100 999)
|
||||
"Printer_ID" = "Printer_{0}" -f [guid]::NewGuid().ToString().Substring(0,6)
|
||||
"VM_Detection_ID" = & $random 0 2
|
||||
"Browser_UserAgent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{0}.0.0.0 Safari/537.36" -f (& $random 90 120)
|
||||
"TimeZone" = "UTC{0}{1:D2}" -f $(if ((& $random 0 2) -eq 0) { "-" } else { "+" }), (& $random 0 12)
|
||||
"Audio_Device_ID" = "Audio_{0}" -f [guid]::NewGuid().ToString().Substring(0,10)
|
||||
"Keyboard_Layout" = "Layout_{0}" -f (& $random 100 999)
|
||||
}
|
||||
|
||||
|
||||
# ==================== 浏览器指纹增强 + 写入系统 ====================
|
||||
|
||||
# 随机分辨率
|
||||
$screenResolutions = @("1920x1080", "1366x768", "1536x864", "1280x720", "1440x900", "1600x900", "1280x800", "2560x1440")
|
||||
$resIndex = & $random 0 ($screenResolutions.Count)
|
||||
$resolution = $screenResolutions[$resIndex]
|
||||
$width, $height = $resolution -split 'x'
|
||||
|
||||
# 随机语言
|
||||
$languages = @("zh-CN", "en-US", "en-GB", "zh-TW", "ja-JP", "ko-KR")
|
||||
$acceptLang = ($languages | Get-Random -Count (& $random 1 3)) -join ", "
|
||||
|
||||
# WebGL
|
||||
$webglVendors = @("NVIDIA Corporation", "Intel Inc.", "AMD", "Google Inc.")
|
||||
$webglRenderers = @(
|
||||
"NVIDIA GeForce GTX {0} OpenGL Engine" -f (& $random 900 3090)
|
||||
"Intel(R) UHD Graphics {0}" -f (& $random 600 900)
|
||||
"ANGLE (NVIDIA, NVIDIA GeForce RTX {0} Direct3D11 vs_5_0 ps_5_0)" -f (& $random 2000 4000)
|
||||
)
|
||||
$vendorIndex = & $random 0 ($webglVendors.Count)
|
||||
$rendererIndex = & $random 0 ($webglRenderers.Count)
|
||||
$webglVendor = $webglVendors[$vendorIndex]
|
||||
$webglRenderer = $webglRenderers[$rendererIndex]
|
||||
|
||||
# Canvas / Audio 伪哈希
|
||||
$canvasData = (& $random 100000000 999999999).ToString() + (& $random 100000000 999999999).ToString()
|
||||
$canvasBase64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($canvasData))
|
||||
$canvasHashLen = [Math]::Min(32, $canvasBase64.Length)
|
||||
$canvasHash = "canvas_{0}" -f $canvasBase64.Substring(0, $canvasHashLen)
|
||||
$audioHash = "audio_{0:X8}" -f (& $random 268435456 2147483647)
|
||||
|
||||
# 插件列表
|
||||
$plugins = @("Chrome PDF Plugin", "Chrome PDF Viewer", "Native Client") -join "; "
|
||||
|
||||
# ========== 1. 写入环境变量(供程序读取)==========
|
||||
[Environment]::SetEnvironmentVariable("BROWSER_USERAGENT", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/$(& $random 118 124).0.0.0 Safari/537.36", "Machine")
|
||||
[Environment]::SetEnvironmentVariable("BROWSER_ACCEPTLANG", $acceptLang, "Machine")
|
||||
[Environment]::SetEnvironmentVariable("SCREEN_RESOLUTION", $resolution, "Machine")
|
||||
[Environment]::SetEnvironmentVariable("WEBGL_VENDOR", $webglVendor, "Machine")
|
||||
[Environment]::SetEnvironmentVariable("WEBGL_RENDERER", $webglRenderer, "Machine")
|
||||
|
||||
# ========== 2. 写入注册表(Chrome / Edge 读取)==========
|
||||
$regPath = "HKLM:\SOFTWARE\Policies\Google\Chrome"
|
||||
if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null }
|
||||
|
||||
Set-ItemProperty -Path $regPath -Name "DefaultBrowserSettingEnabled" -Value 0 -Type DWord -Force
|
||||
Set-ItemProperty -Path $regPath -Name "UserAgent" -Value $env:BROWSER_USERAGENT -Force
|
||||
Set-ItemProperty -Path $regPath -Name "AcceptLanguage" -Value $acceptLang -Force
|
||||
|
||||
# WebGL 伪装(通过扩展策略)
|
||||
$extPath = "HKLM:\SOFTWARE\Policies\Google\Chrome\3rdparty\extensions"
|
||||
if (-not (Test-Path $extPath)) { New-Item -Path $extPath -Force | Out-Null }
|
||||
# 模拟 WebGL 指纹修改扩展(实际需配合 Selenium CDP)
|
||||
$extSubPath = "$extPath\cjpalhdlnbpafiamejdajceeocdbgejm"
|
||||
if (-not (Test-Path $extSubPath)) { New-Item -Path $extSubPath -Force | Out-Null }
|
||||
Set-ItemProperty -Path $extSubPath -Name "override_webgl" -Value 1 -Force
|
||||
|
||||
# ========== 3. 写入系统分辨率(模拟真实屏幕)==========
|
||||
# 注意:SystemInformation.VirtualScreen 是只读属性,无法直接修改
|
||||
# 此处通过注册表设置显示分辨率信息
|
||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" -Name "DisplayWidth" -Value $width -Type DWord -Force
|
||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" -Name "DisplayHeight" -Value $height -Type DWord -Force
|
||||
Write-Host "Screen resolution set to $resolution via Registry" -ForegroundColor Cyan
|
||||
|
||||
# ========== 4. 写入 JSON(供 Selenium 读取)==========
|
||||
$hardwareFingerprints["Browser_UserAgent"] = $env:BROWSER_USERAGENT
|
||||
$hardwareFingerprints["Browser_AcceptLanguage"] = $acceptLang
|
||||
$hardwareFingerprints["Browser_Platform"] = "Win64"
|
||||
$hardwareFingerprints["Browser_Vendor"] = "Google Inc."
|
||||
$hardwareFingerprints["Browser_Renderer"] = "Blink"
|
||||
$hardwareFingerprints["WebGL_Vendor"] = $webglVendor
|
||||
$hardwareFingerprints["WebGL_Renderer"] = $webglRenderer
|
||||
$hardwareFingerprints["Canvas_Fingerprint"] = $canvasHash
|
||||
$hardwareFingerprints["AudioContext_Fingerprint"] = $audioHash
|
||||
$hardwareFingerprints["Screen_Resolution"] = $resolution
|
||||
$hardwareFingerprints["Plugins_List"] = $plugins
|
||||
$dntIndex = & $random 0 2
|
||||
$hardwareFingerprints["DoNotTrack"] = @("1", "0")[$dntIndex]
|
||||
$timezoneOffset = (& $random 0 1441) - 720 # -720 到 +720 分钟
|
||||
$hardwareFingerprints["Timezone_Offset"] = $timezoneOffset
|
||||
|
||||
Write-Host "Browser fingerprints injected into SYSTEM (Env + Registry + JSON)" -ForegroundColor Green
|
||||
|
||||
|
||||
$jsonPath = "C:\sandbox_files\config.json"
|
||||
$hardwareFingerprints | ConvertTo-Json -Depth 3 | Out-File -FilePath $jsonPath -Encoding UTF8 -Force
|
||||
Write-Host "config.json Generated: $jsonPath" -ForegroundColor Green
|
||||
"配置.json 生成完成: $jsonPath" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
# 导入指纹到系统
|
||||
Write-Host "[1/7] Applying Fingerprints to System (Simulating Random Hardware, Skipping MAC Apply)..." -ForegroundColor Yellow
|
||||
"应用指纹到系统" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
try {
|
||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography" -Name "MachineGuid" -Value $hardwareFingerprints.System_UUID -Force -ErrorAction Stop
|
||||
Write-Host "Applied System_UUID to Registry." -ForegroundColor Green
|
||||
|
||||
[Environment]::SetEnvironmentVariable("COMPUTERNAME", $hardwareFingerprints.Computer_Name, "Machine")
|
||||
Write-Host "Applied Computer_Name to Environment." -ForegroundColor Green
|
||||
|
||||
[Environment]::SetEnvironmentVariable("USERNAME", $hardwareFingerprints.User_Name, "User")
|
||||
Write-Host "Applied User_Name to Environment." -ForegroundColor Green
|
||||
|
||||
if (-not (Test-Path "HKLM:\SYSTEM\HardwareConfig")) { New-Item -Path "HKLM:\SYSTEM\HardwareConfig" -Force | Out-Null }
|
||||
Set-ItemProperty -Path "HKLM:\SYSTEM\HardwareConfig" -Name "BIOSUUID" -Value $hardwareFingerprints.BIOS_UUID -Force -ErrorAction Stop
|
||||
Write-Host "Applied BIOS_UUID to Registry." -ForegroundColor Green
|
||||
|
||||
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion" -Name "HardDriveSerial" -Value $hardwareFingerprints.Hard_Drive_Serial -Force -ErrorAction Stop
|
||||
Write-Host "Applied Hard_Drive_Serial to Registry." -ForegroundColor Green
|
||||
|
||||
Write-Host "MAC Address Generated but Not Applied to Adapter." -ForegroundColor Cyan
|
||||
|
||||
[Environment]::SetEnvironmentVariable("OS_BUILD", $hardwareFingerprints.OS_Build_Number, "Machine")
|
||||
[Environment]::SetEnvironmentVariable("USERAGENT", $hardwareFingerprints.Browser_UserAgent, "User")
|
||||
Write-Host "Applied Other Fingerprints (OS Build, UserAgent, etc.)." -ForegroundColor Green
|
||||
|
||||
} catch {
|
||||
Write-Host "Warning: Some Fingerprint Applications Failed: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||
"指纹应用失败: $($_.Exception.Message)" | Add-Content -Path $logPath -Encoding UTF8
|
||||
}
|
||||
|
||||
# 2. 本地优先 + 官网下载(PowerShell 兼容,无 goto)
|
||||
Write-Progress -Activity "Installation Progress" -Status "Checking Local Python..." -PercentComplete 25
|
||||
Write-Host "[2/7] Checking for Local Python Installer..." -ForegroundColor Yellow
|
||||
"[2/7] 检查本地 Python" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
$localPythonPath = "C:\sandbox_files\python-3.12.7-amd64.exe"
|
||||
$installerPath = "C:\Temp\python-installer.exe"
|
||||
New-Item -ItemType Directory -Path "C:\Temp" -Force | Out-Null
|
||||
|
||||
$useLocal = $false
|
||||
|
||||
if (Test-Path $localPythonPath) {
|
||||
$fileSize = [math]::Round((Get-Item $localPythonPath).Length / 1MB, 2)
|
||||
if ($fileSize -gt 20) {
|
||||
Write-Host "Found Local Python: $localPythonPath (Size: ${fileSize} MB)" -ForegroundColor Green
|
||||
"使用本地 Python: $localPythonPath" | Add-Content -Path $logPath -Encoding UTF8
|
||||
Copy-Item $localPythonPath $installerPath -Force
|
||||
$useLocal = $true
|
||||
} else {
|
||||
Write-Host "Local file too small (${fileSize} MB), downloading from web..." -ForegroundColor Yellow
|
||||
"本地文件过小,联网下载" | Add-Content -Path $logPath -Encoding UTF8
|
||||
}
|
||||
} else {
|
||||
Write-Host "No local Python installer found, downloading from python.org..." -ForegroundColor Yellow
|
||||
"未找到本地安装包,联网下载" | Add-Content -Path $logPath -Encoding UTF8
|
||||
}
|
||||
|
||||
# === 下载逻辑(仅当未使用本地时)===
|
||||
if (-not $useLocal) {
|
||||
Write-Progress -Activity "Installation Progress" -Status "Downloading Python..." -PercentComplete 30
|
||||
Write-Host "[2/7] Downloading Python 3.12.7 from python.org..." -ForegroundColor Yellow
|
||||
|
||||
$pythonUrl = "https://www.python.org/ftp/python/3.12.7/python-3.12.7-amd64.exe"
|
||||
$downloaded = $false
|
||||
$attempt = 0
|
||||
$maxAttempts = 15
|
||||
|
||||
while (-not $downloaded -and $attempt -lt $maxAttempts) {
|
||||
$attempt++
|
||||
Write-Host " Attempt ${attempt}/${maxAttempts}: Downloading..."
|
||||
try {
|
||||
Invoke-WebRequest -Uri $pythonUrl -OutFile $installerPath -UseBasicParsing -TimeoutSec 900 -ErrorAction Stop
|
||||
$fileSize = [math]::Round((Get-Item $installerPath).Length / 1MB, 2)
|
||||
if ($fileSize -gt 20) {
|
||||
Write-Host " Download Complete! (Size: ${fileSize} MB)" -ForegroundColor Green
|
||||
"Python 下载成功: $pythonUrl" | Add-Content -Path $logPath -Encoding UTF8
|
||||
$downloaded = $true
|
||||
} else {
|
||||
throw "File too small"
|
||||
}
|
||||
} catch {
|
||||
Write-Host " Failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
"下载失败 [尝试 $attempt]: $($_.Exception.Message)" | Add-Content -Path $logPath -Encoding UTF8
|
||||
if (Test-Path $installerPath) { Remove-Item $installerPath -Force }
|
||||
}
|
||||
if (-not $downloaded) {
|
||||
Start-Sleep -Seconds 15
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $downloaded) {
|
||||
Write-Host "ERROR: Python Download Failed!" -ForegroundColor Red
|
||||
"Python 下载失败" | Add-Content -Path $logPath -Encoding UTF8
|
||||
Write-Host "`nPress Any Key to Exit..." -ForegroundColor Gray
|
||||
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# === 安装 Python(无论本地或下载)===
|
||||
Write-Progress -Activity "Installation Progress" -Status "Installing Python..." -PercentComplete 50
|
||||
Write-Host "[3/7] Installing Python 3.12.7 (Silent Mode)..." -ForegroundColor Yellow
|
||||
"[3/7] 安装 Python" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
Start-Process -FilePath $installerPath -ArgumentList "/quiet InstallAllUsers=1 PrependPath=1" -Wait -NoNewWindow
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
"Python 安装完成" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
# 验证 Python
|
||||
Write-Progress -Activity "Installation Progress" -Status "Verifying Python..." -PercentComplete 60
|
||||
Write-Host "[4/7] Verifying Python Installation..." -ForegroundColor Yellow
|
||||
"[4/7] 验证 Python" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
Start-Sleep -Seconds 5
|
||||
$pythonVersion = & python --version 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "Python 3.12.7 Installed Successfully! Version: $pythonVersion" -ForegroundColor Green
|
||||
"Python 成功: $pythonVersion" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
Write-Host "[4/7] Installing pywinauto Library..." -ForegroundColor Yellow
|
||||
for ($i = 1; $i -le 2; $i++) {
|
||||
& python -m pip install pywinauto --quiet
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "pywinauto Installed Successfully!" -ForegroundColor Green
|
||||
"pywinauto 安装成功" | Add-Content -Path $logPath -Encoding UTF8
|
||||
break
|
||||
} else {
|
||||
Write-Host "pywinauto Attempt $i Failed, Retrying..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "pywinauto Failed After Retries. Continuing..." -ForegroundColor Yellow
|
||||
"pywinauto 最终失败" | Add-Content -Path $logPath -Encoding UTF8
|
||||
}
|
||||
} else {
|
||||
Write-Host "Python Installation Failed!" -ForegroundColor Red
|
||||
"Python 安装失败" | Add-Content -Path $logPath -Encoding UTF8
|
||||
Write-Host "`nPress Any Key to Exit..." -ForegroundColor Gray
|
||||
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 5. 安装 kiro.exe 并自动化
|
||||
Write-Progress -Activity "Installation Progress" -Status "Installing kiro.exe..." -PercentComplete 70
|
||||
Write-Host "[5/7] Starting kiro.exe Installer..." -ForegroundColor Yellow
|
||||
"[5/7] 启动 kiro.exe" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
$kiroPath = "C:\sandbox_files\kiro.exe"
|
||||
if (-not (Test-Path $kiroPath)) {
|
||||
Write-Host "Error: kiro.exe Not Found" -ForegroundColor Red
|
||||
"kiro 未找到" | Add-Content -Path $logPath -Encoding UTF8
|
||||
Write-Progress -Activity "Installation Progress" -Status "Failed" -PercentComplete 100
|
||||
pause
|
||||
exit 1
|
||||
}
|
||||
|
||||
$process = Start-Process -FilePath $kiroPath -PassThru -NoNewWindow
|
||||
Write-Host "kiro Installer Started (PID: $($process.Id))" -ForegroundColor Cyan
|
||||
|
||||
if ($pythonVersion -ne "Skipped" -and (Test-Path "C:\sandbox_files\automate_kiro.py")) {
|
||||
Write-Host "[5/7] Running automate_kiro.py for Automation..." -ForegroundColor Yellow
|
||||
& python "C:\sandbox_files\automate_kiro.py"
|
||||
Write-Host "Automation Script Executed." -ForegroundColor Green
|
||||
"自动化脚本执行完成" | Add-Content -Path $logPath -Encoding UTF8
|
||||
}
|
||||
|
||||
# 等待主程序启动
|
||||
$timeout = 300
|
||||
$elapsed = 0
|
||||
$kiroInstalled = $false
|
||||
|
||||
while ($elapsed -lt $timeout -and -not $kiroInstalled) {
|
||||
Start-Sleep -Seconds 3
|
||||
$elapsed += 3
|
||||
|
||||
$kiroProcess = Get-Process -Name "kiro" -ErrorAction SilentlyContinue | Where-Object {
|
||||
$_.Path -like "*\Programs\Kiro\kiro.exe"
|
||||
}
|
||||
|
||||
if ($kiroProcess) {
|
||||
Write-Host "Detected kiro Main Program Running! Installation Complete." -ForegroundColor Green
|
||||
"kiro 主程序运行: PID $($kiroProcess.Id)" | Add-Content -Path $logPath -Encoding UTF8
|
||||
$kiroInstalled = $true
|
||||
break
|
||||
}
|
||||
|
||||
$installDir = "$env:LOCALAPPDATA\Programs\Kiro"
|
||||
if (Test-Path $installDir) {
|
||||
Write-Host "Detected kiro Install Directory, Waiting for Main Program..." -ForegroundColor Cyan
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $kiroInstalled) {
|
||||
Write-Host "Warning: Timeout - kiro Main Program Not Detected." -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host "kiro.exe Installed and Started Successfully!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 完成
|
||||
Write-Progress -Activity "Installation Progress" -Status "Completed" -PercentComplete 100
|
||||
Write-Host "`n`n=========================================" -ForegroundColor Green
|
||||
Write-Host " All Installations Completed! " -ForegroundColor Green
|
||||
Write-Host "=========================================`n" -ForegroundColor Green
|
||||
"安装完成: $(Get-Date)" | Add-Content -Path $logPath -Encoding UTF8
|
||||
|
||||
Write-Host "Tip: Open Command Prompt and Run 'python --version' to Verify" -ForegroundColor Cyan
|
||||
Write-Host "kiro.exe Installed to $env:LOCALAPPDATA\Programs\Kiro" -ForegroundColor Cyan
|
||||
Write-Host "Log Saved to: $logPath" -ForegroundColor Cyan
|
||||
|
||||
Write-Host "`nPress Any Key to Close This Window..." -ForegroundColor Gray
|
||||
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||
@@ -0,0 +1,485 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Kiro 登录自动化脚本
|
||||
功能:连接到 Kiro 应用程序,检测登录按钮,让用户选择登录方式并自动点击
|
||||
作者:Claude Code Assistant
|
||||
"""
|
||||
|
||||
import time
|
||||
import sys
|
||||
from pywinauto import Application
|
||||
|
||||
class KiroLoginAutomator:
|
||||
def __init__(self):
|
||||
self.app = None
|
||||
self.window = None
|
||||
self.login_buttons = []
|
||||
|
||||
def connect_to_kiro(self):
|
||||
"""连接到 Kiro 应用程序"""
|
||||
print("🔗 正在连接到 Kiro 应用程序...")
|
||||
|
||||
# 使用成功的连接方法(方法9:通过 Kiro 标题连接)
|
||||
connection_attempts = [
|
||||
lambda: Application(backend="uia").connect(title_re=".*Kiro.*", timeout=10),
|
||||
lambda: Application(backend="win32").connect(title_re=".*Kiro.*", timeout=10),
|
||||
lambda: Application(backend="uia").connect(title_re=".*Getting started.*", timeout=10),
|
||||
lambda: Application(backend="win32").connect(title_re=".*Getting started.*", timeout=10),
|
||||
]
|
||||
|
||||
for i, attempt in enumerate(connection_attempts, 1):
|
||||
try:
|
||||
print(f" 尝试方法 {i}...")
|
||||
self.app = attempt()
|
||||
print(f"✅ 连接成功! (方法{i}) PID: {self.app.process}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f" 方法 {i} 失败: {str(e)[:50]}...")
|
||||
continue
|
||||
|
||||
print("❌ 所有连接方法都失败了")
|
||||
return False
|
||||
|
||||
def get_window(self):
|
||||
"""获取 Kiro 窗口"""
|
||||
try:
|
||||
windows = self.app.windows()
|
||||
if len(windows) == 0:
|
||||
print("❌ 未找到窗口")
|
||||
return False
|
||||
|
||||
self.window = windows[0]
|
||||
window_title = self.window.window_text()
|
||||
print(f"✅ 获取到窗口: '{window_title}'")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ 获取窗口失败: {e}")
|
||||
return False
|
||||
|
||||
def analyze_login_buttons(self):
|
||||
"""分析登录按钮"""
|
||||
print("\n🔍 分析登录按钮...")
|
||||
|
||||
try:
|
||||
controls = self.window.descendants()
|
||||
print(f"📊 找到 {len(controls)} 个控件")
|
||||
|
||||
# 查找可能的登录按钮
|
||||
potential_buttons = []
|
||||
|
||||
# 首先显示所有控件信息以便调试
|
||||
print("🔍 显示所有控件信息(前20个):")
|
||||
for i, ctrl in enumerate(controls[:20]):
|
||||
try:
|
||||
# 获取控件信息
|
||||
ctrl_type = ""
|
||||
ctrl_name = ""
|
||||
ctrl_text = ""
|
||||
ctrl_class = ""
|
||||
|
||||
if hasattr(ctrl, 'element_info'):
|
||||
try:
|
||||
ctrl_type = ctrl.element_info.control_type
|
||||
ctrl_name = ctrl.element_info.name or ""
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
ctrl_text = ctrl.window_text() or ""
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
ctrl_class = ctrl.class_name() if hasattr(ctrl, 'class_name') else ""
|
||||
except:
|
||||
pass
|
||||
|
||||
print(f" {i}: 类型={ctrl_type}, 名称='{ctrl_name}', 文本='{ctrl_text}', 类名='{ctrl_class}'")
|
||||
|
||||
except Exception as e:
|
||||
print(f" {i}: 获取信息失败: {e}")
|
||||
|
||||
print("\n🔍 查找登录相关控件...")
|
||||
|
||||
for i, ctrl in enumerate(controls):
|
||||
try:
|
||||
# 获取控件信息
|
||||
ctrl_type = ""
|
||||
ctrl_name = ""
|
||||
ctrl_text = ""
|
||||
|
||||
if hasattr(ctrl, 'element_info'):
|
||||
try:
|
||||
ctrl_type = ctrl.element_info.control_type
|
||||
ctrl_name = ctrl.element_info.name or ""
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
ctrl_text = ctrl.window_text() or ""
|
||||
except:
|
||||
pass
|
||||
|
||||
# 扩展登录关键词,包含更多可能的文本
|
||||
login_keywords = [
|
||||
"sign in", "google", "github", "aws", "builder", "login", "登录",
|
||||
"sign", "continue", "get started", "start", "begin", "connect",
|
||||
"authenticate", "account", "oauth", "sso"
|
||||
]
|
||||
combined_text = f"{ctrl_name} {ctrl_text}".lower()
|
||||
|
||||
# 降低检测门槛:只要包含任何一个关键词就加入候选
|
||||
if any(keyword in combined_text for keyword in login_keywords) or \
|
||||
(ctrl_type and "button" in ctrl_type.lower()) or \
|
||||
(ctrl_text and len(ctrl_text.strip()) > 0):
|
||||
potential_buttons.append({
|
||||
'index': i,
|
||||
'ctrl': ctrl,
|
||||
'type': ctrl_type,
|
||||
'name': ctrl_name,
|
||||
'text': ctrl_text,
|
||||
'combined': combined_text,
|
||||
'class': ctrl.class_name() if hasattr(ctrl, 'class_name') else ""
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
print(f"🔍 找到 {len(potential_buttons)} 个可能的登录按钮:")
|
||||
|
||||
# 显示找到的按钮
|
||||
for i, btn in enumerate(potential_buttons, 1):
|
||||
print(f" {i}. 类型: {btn['type']}")
|
||||
print(f" 名称: '{btn['name']}'")
|
||||
print(f" 文本: '{btn['text']}'")
|
||||
print(f" 类名: '{btn['class']}'")
|
||||
print(f" 索引: {btn['index']}")
|
||||
print()
|
||||
|
||||
# 尝试识别具体的登录按钮,放宽条件
|
||||
self.login_buttons = []
|
||||
seen_buttons = set() # 用于去重
|
||||
|
||||
for btn in potential_buttons:
|
||||
combined = btn['combined']
|
||||
|
||||
# 放宽条件:不仅限于Button类型,也包含其他可点击控件
|
||||
if btn['type'] and btn['type'] in ['Document', 'Text', 'Group']:
|
||||
continue # 跳过明显不可点击的控件
|
||||
|
||||
# 获取按钮位置信息用于去重
|
||||
try:
|
||||
rect = btn['ctrl'].rectangle()
|
||||
position_key = f"{rect.left}_{rect.top}_{rect.width()}_{rect.height()}"
|
||||
except:
|
||||
position_key = f"{btn['index']}"
|
||||
|
||||
button_info = None
|
||||
|
||||
# 更宽松的匹配条件
|
||||
if "google" in combined and "google" not in seen_buttons:
|
||||
button_info = {
|
||||
'name': 'Google',
|
||||
'description': 'Sign in with Google',
|
||||
'ctrl': btn['ctrl'],
|
||||
'info': btn,
|
||||
'position': position_key
|
||||
}
|
||||
seen_buttons.add("google")
|
||||
elif "github" in combined and "github" not in seen_buttons:
|
||||
button_info = {
|
||||
'name': 'Github',
|
||||
'description': 'Sign in with Github',
|
||||
'ctrl': btn['ctrl'],
|
||||
'info': btn,
|
||||
'position': position_key
|
||||
}
|
||||
seen_buttons.add("github")
|
||||
elif ("aws" in combined or "builder" in combined) and "aws" not in seen_buttons:
|
||||
button_info = {
|
||||
'name': 'AWS Builder ID',
|
||||
'description': 'Sign in with AWS Builder ID',
|
||||
'ctrl': btn['ctrl'],
|
||||
'info': btn,
|
||||
'position': position_key
|
||||
}
|
||||
seen_buttons.add("aws")
|
||||
elif "organization" in combined and "organization" not in seen_buttons:
|
||||
button_info = {
|
||||
'name': 'Organization',
|
||||
'description': 'Sign in with your organization identity',
|
||||
'ctrl': btn['ctrl'],
|
||||
'info': btn,
|
||||
'position': position_key
|
||||
}
|
||||
seen_buttons.add("organization")
|
||||
elif any(keyword in combined for keyword in ["sign", "start", "continue", "connect"]) and \
|
||||
btn['type'] and "button" in btn['type'].lower() and "generic" not in seen_buttons:
|
||||
# 通用登录按钮
|
||||
button_info = {
|
||||
'name': 'Generic Login',
|
||||
'description': f'通用登录按钮 ({btn["name"] or btn["text"] or "未知"})',
|
||||
'ctrl': btn['ctrl'],
|
||||
'info': btn,
|
||||
'position': position_key
|
||||
}
|
||||
seen_buttons.add("generic")
|
||||
|
||||
if button_info:
|
||||
self.login_buttons.append(button_info)
|
||||
|
||||
print(f"✅ 识别出 {len(self.login_buttons)} 个登录选项:")
|
||||
for i, btn in enumerate(self.login_buttons, 1):
|
||||
try:
|
||||
rect = btn['ctrl'].rectangle()
|
||||
position_info = f"位置: ({rect.left},{rect.top}) 大小: {rect.width()}x{rect.height()}"
|
||||
except:
|
||||
position_info = f"索引: {btn['info']['index']}"
|
||||
print(f" {i}. {btn['description']} ({position_info})")
|
||||
|
||||
return len(self.login_buttons) > 0
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 分析登录按钮失败: {e}")
|
||||
return False
|
||||
|
||||
def show_login_options_with_timeout(self):
|
||||
"""显示登录选项并让用户选择(带倒计时)"""
|
||||
if not self.login_buttons:
|
||||
print("❌ 未找到登录按钮")
|
||||
return None
|
||||
|
||||
print("\n🎯 登录方式选择:")
|
||||
for i, btn in enumerate(self.login_buttons, 1):
|
||||
try:
|
||||
rect = btn['ctrl'].rectangle()
|
||||
position_info = f"位置: ({rect.left},{rect.top})"
|
||||
except:
|
||||
position_info = f"索引: {btn['info']['index']}"
|
||||
print(f" {i}. {btn['description']} ({position_info})")
|
||||
print(" 4. 退出程序(让用户手动操作)")
|
||||
print("\n💡 提示:")
|
||||
print(" - 默认选择: 3 (Github)")
|
||||
print(" - 选择 4 将退出程序")
|
||||
print(" - 3秒后自动选择默认选项")
|
||||
|
||||
# 倒计时输入
|
||||
import select
|
||||
import sys
|
||||
import time
|
||||
|
||||
print(f"\n⏰ 请在3秒内输入选择 (1-4),或按回车使用默认选项3: ", end='', flush=True)
|
||||
|
||||
# Windows系统的非阻塞输入实现
|
||||
import msvcrt
|
||||
import threading
|
||||
|
||||
user_input = []
|
||||
input_received = threading.Event()
|
||||
|
||||
def get_input():
|
||||
try:
|
||||
while not input_received.is_set():
|
||||
if msvcrt.kbhit():
|
||||
char = msvcrt.getch().decode('utf-8')
|
||||
if char == '\r': # 回车键
|
||||
input_received.set()
|
||||
break
|
||||
elif char.isdigit():
|
||||
user_input.append(char)
|
||||
print(char, end='', flush=True)
|
||||
elif char == '\b' and user_input: # 退格键
|
||||
user_input.pop()
|
||||
print('\b \b', end='', flush=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
input_thread = threading.Thread(target=get_input)
|
||||
input_thread.daemon = True
|
||||
input_thread.start()
|
||||
|
||||
# 倒计时
|
||||
for i in range(3, 0, -1):
|
||||
if input_received.is_set():
|
||||
break
|
||||
time.sleep(1)
|
||||
if not input_received.is_set():
|
||||
print(f"\r⏰ 请在{i-1}秒内输入选择 (1-4),或按回车使用默认选项3: {''.join(user_input)}", end='', flush=True)
|
||||
|
||||
input_received.set() # 停止输入线程
|
||||
|
||||
# 处理用户输入
|
||||
if user_input:
|
||||
try:
|
||||
choice_num = int(''.join(user_input))
|
||||
except ValueError:
|
||||
choice_num = 3 # 默认选择
|
||||
else:
|
||||
choice_num = 3 # 默认选择
|
||||
|
||||
print(f"\n")
|
||||
|
||||
# 特殊处理选择4
|
||||
if choice_num == 4:
|
||||
print("👋 您选择了退出程序,请手动完成登录操作")
|
||||
return None
|
||||
|
||||
# 验证选择范围
|
||||
if choice_num < 1 or choice_num > len(self.login_buttons):
|
||||
print(f"⚠️ 无效选择,使用默认选项3 (Github)")
|
||||
choice_num = 3
|
||||
|
||||
selected_button = self.login_buttons[choice_num - 1]
|
||||
print(f"✅ 选择了: {selected_button['description']}")
|
||||
return selected_button
|
||||
|
||||
def click_login_button(self, button):
|
||||
"""点击选择的登录按钮"""
|
||||
try:
|
||||
print(f"\n🖱️ 正在点击 '{button['description']}' 按钮...")
|
||||
print(f"🔍 按钮信息: 索引={button['info']['index']}, 类型={button['info']['type']}")
|
||||
|
||||
# 尝试多种点击方法
|
||||
ctrl = button['ctrl']
|
||||
|
||||
# 验证按钮是否仍然有效
|
||||
try:
|
||||
if not ctrl.exists():
|
||||
print("❌ 按钮不存在")
|
||||
return False
|
||||
if not ctrl.is_visible():
|
||||
print("❌ 按钮不可见")
|
||||
return False
|
||||
if not ctrl.is_enabled():
|
||||
print("❌ 按钮被禁用")
|
||||
return False
|
||||
print("✅ 按钮验证通过")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 按钮验证失败: {e}")
|
||||
|
||||
# 方法1:设置焦点后发送回车键(最成功的方法)
|
||||
try:
|
||||
ctrl.set_focus()
|
||||
time.sleep(0.5)
|
||||
ctrl.type_keys("{ENTER}")
|
||||
print("✅ 方法1成功: 焦点+回车键")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"⚠️ 方法1失败: {e}")
|
||||
|
||||
# 方法2:尝试使用 UIA 的 invoke 模式
|
||||
try:
|
||||
if hasattr(ctrl, 'element_info'):
|
||||
element = ctrl.element_info
|
||||
# 尝试获取 Invoke 模式
|
||||
try:
|
||||
invoke_pattern = element.GetCurrentPattern(10000) # UIA_InvokePatternId
|
||||
if invoke_pattern:
|
||||
invoke_pattern.Invoke()
|
||||
print("✅ 方法2成功: UIA Invoke 模式")
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# 尝试直接调用 invoke 方法
|
||||
if hasattr(element, 'invoke'):
|
||||
element.invoke()
|
||||
print("✅ 方法2成功: 直接 invoke")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"⚠️ 方法2失败: {e}")
|
||||
|
||||
# 方法3:尝试设置焦点后发送空格键
|
||||
try:
|
||||
ctrl.set_focus()
|
||||
time.sleep(0.5)
|
||||
ctrl.type_keys("{SPACE}")
|
||||
print("✅ 方法3成功: 焦点+空格键")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"⚠️ 方法3失败: {e}")
|
||||
|
||||
# 方法4:尝试使用 Windows API 发送点击消息
|
||||
try:
|
||||
import win32gui
|
||||
import win32con
|
||||
|
||||
# 获取窗口句柄
|
||||
hwnd = ctrl.handle
|
||||
# 发送 BN_CLICKED 消息
|
||||
win32gui.SendMessage(hwnd, win32con.BM_CLICK, 0, 0)
|
||||
print("✅ 方法4成功: Windows API 点击")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"⚠️ 方法4失败: {e}")
|
||||
|
||||
# 方法5:最后尝试坐标点击(作为备用方案)
|
||||
try:
|
||||
rect = ctrl.rectangle()
|
||||
center_x = rect.left + rect.width() // 2
|
||||
center_y = rect.top + rect.height() // 2
|
||||
print(f"🔍 尝试点击坐标: ({center_x}, {center_y})")
|
||||
ctrl.click_input(coords=(center_x, center_y))
|
||||
print("✅ 方法5成功: 坐标点击")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"⚠️ 方法5失败: {e}")
|
||||
|
||||
print("❌ 所有点击方法都失败了")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 点击按钮失败: {e}")
|
||||
return False
|
||||
|
||||
def run(self):
|
||||
"""运行主程序"""
|
||||
print("🚀 Kiro 登录自动化脚本")
|
||||
print("=" * 50)
|
||||
|
||||
# 步骤1:连接到 Kiro
|
||||
if not self.connect_to_kiro():
|
||||
return
|
||||
|
||||
# 步骤2:获取窗口
|
||||
if not self.get_window():
|
||||
return
|
||||
|
||||
# 等待界面稳定
|
||||
print("\n⏳ 等待界面稳定...")
|
||||
time.sleep(3)
|
||||
|
||||
# 步骤3:分析登录按钮
|
||||
if not self.analyze_login_buttons():
|
||||
print("❌ 未找到登录按钮,可能需要手动操作")
|
||||
return
|
||||
|
||||
# 步骤4:智能选择登录方式
|
||||
selected_button = self.show_login_options_with_timeout()
|
||||
if not selected_button:
|
||||
print("👋 程序结束")
|
||||
return
|
||||
|
||||
# 点击按钮
|
||||
if self.click_login_button(selected_button):
|
||||
print("🎉 登录按钮点击成功!")
|
||||
print("⏳ 请等待浏览器打开并完成登录...")
|
||||
print("💡 登录完成后,您可以返回 Kiro 继续使用")
|
||||
else:
|
||||
print("❌ 登录按钮点击失败,请手动点击登录按钮")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
try:
|
||||
automator = KiroLoginAutomator()
|
||||
automator.run()
|
||||
except KeyboardInterrupt:
|
||||
print("\n⚠️ 用户中断操作")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 程序异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Kiro安装自动化 - 第一步
|
||||
处理"安装"提醒窗口,点击确定按钮进入协议界面
|
||||
"""
|
||||
|
||||
import time
|
||||
from pywinauto import Application
|
||||
|
||||
def step1_handle_install_prompt():
|
||||
"""
|
||||
第一步:处理安装提醒窗口
|
||||
找到并点击确定按钮
|
||||
"""
|
||||
print("🚀 Kiro安装自动化 - 第一步")
|
||||
print("="*50)
|
||||
|
||||
print("🔍 正在查找'安装'提醒窗口...")
|
||||
|
||||
# 尝试多种方式连接到"安装"窗口
|
||||
connection_methods = [
|
||||
lambda: Application(backend="uia").connect(title="安装", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title="安装", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=".*安装.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*安装.*", timeout=5),
|
||||
]
|
||||
|
||||
app = None
|
||||
for i, method in enumerate(connection_methods, 1):
|
||||
try:
|
||||
print(f" 尝试连接方法 {i}...")
|
||||
app = method()
|
||||
print(f"✅ 连接成功! PID: {app.process}")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f" 方法 {i} 失败: {str(e)[:50]}...")
|
||||
continue
|
||||
|
||||
if not app:
|
||||
print("❌ 无法连接到安装提醒窗口")
|
||||
return False
|
||||
|
||||
# 获取窗口
|
||||
try:
|
||||
window = app.top_window()
|
||||
print(f"📋 窗口标题: '{window.window_text()}'")
|
||||
print(f"📋 窗口类名: '{window.class_name()}'")
|
||||
|
||||
# 等待窗口稳定
|
||||
print("⏳ 等待窗口稳定...")
|
||||
time.sleep(2)
|
||||
|
||||
# 查找确定按钮的多种方式
|
||||
print("🔍 正在查找确定按钮...")
|
||||
|
||||
button_search_methods = [
|
||||
# 方法1:通过常见的确定按钮文本
|
||||
lambda: window.child_window(title="确定", control_type="Button"),
|
||||
lambda: window.child_window(title="OK", control_type="Button"),
|
||||
lambda: window.child_window(title="是", control_type="Button"),
|
||||
lambda: window.child_window(title="Yes", control_type="Button"),
|
||||
|
||||
# 方法2:通过类名查找按钮
|
||||
lambda: window.child_window(class_name="Button"),
|
||||
lambda: window.child_window(class_name="TButton"),
|
||||
lambda: window.child_window(class_name="TNewButton"),
|
||||
|
||||
# 方法3:通过控件类型查找第一个按钮
|
||||
lambda: window.child_window(control_type="Button"),
|
||||
]
|
||||
|
||||
confirm_button = None
|
||||
for i, search_method in enumerate(button_search_methods, 1):
|
||||
try:
|
||||
print(f" 尝试查找方法 {i}...")
|
||||
button = search_method()
|
||||
if button.exists() and button.is_visible():
|
||||
confirm_button = button
|
||||
button_text = button.window_text() or "[无文本]"
|
||||
button_class = button.class_name()
|
||||
print(f"✅ 找到按钮: '{button_text}' (类名: {button_class})")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f" 方法 {i} 失败: {str(e)[:30]}...")
|
||||
continue
|
||||
|
||||
if not confirm_button:
|
||||
print("❌ 未找到确定按钮")
|
||||
# 显示所有可用控件帮助调试
|
||||
print("🔍 显示窗口中的所有控件:")
|
||||
try:
|
||||
controls = window.descendants()
|
||||
for i, ctrl in enumerate(controls[:10]): # 只显示前10个
|
||||
try:
|
||||
text = ctrl.window_text() or "[无文本]"
|
||||
class_name = ctrl.class_name()
|
||||
control_type = getattr(ctrl.element_info, 'control_type', '未知') if hasattr(ctrl, 'element_info') else '未知'
|
||||
print(f" {i+1}. '{text}' (类名: {class_name}, 类型: {control_type})")
|
||||
except:
|
||||
print(f" {i+1}. [无法获取控件信息]")
|
||||
except Exception as e:
|
||||
print(f" 获取控件列表失败: {e}")
|
||||
return False
|
||||
|
||||
# 点击确定按钮
|
||||
print("🖱️ 正在点击确定按钮...")
|
||||
try:
|
||||
confirm_button.click()
|
||||
print("✅ 成功点击确定按钮!")
|
||||
|
||||
# 等待窗口切换
|
||||
print("⏳ 等待进入协议界面...")
|
||||
time.sleep(3)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 点击按钮失败: {e}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 处理窗口失败: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
success = step1_handle_install_prompt()
|
||||
|
||||
if success:
|
||||
print("\n🎉 第一步完成!应该已经进入协议界面")
|
||||
print("💡 现在可以运行协议界面的分析脚本了")
|
||||
else:
|
||||
print("\n❌ 第一步失败,请检查安装程序状态")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n⚠️ 用户中断操作")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 程序异常: {e}")
|
||||
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
快速窗口控件分析器 - 简化版
|
||||
专门用于快速分析应用程序控件结构
|
||||
"""
|
||||
|
||||
import time
|
||||
import sys
|
||||
from pywinauto import Application
|
||||
|
||||
def quick_analyze(app_path):
|
||||
"""快速分析指定应用的窗口控件"""
|
||||
|
||||
print(f"🔗 连接应用程序: {app_path}")
|
||||
|
||||
# 特殊处理kiro.exe - 连接到安装界面或应用程序进程
|
||||
if "kiro.exe" in app_path.lower():
|
||||
print("🔍 检测到kiro.exe,尝试连接进程...")
|
||||
|
||||
# 扩展的连接方法:支持安装程序和已安装的应用程序
|
||||
connection_attempts = [
|
||||
# 安装程序连接方法(保留原有功能)
|
||||
lambda: Application(backend="uia").connect(title="安装", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title="安装", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title="安装-Kiro (User)", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title="安装-Kiro (User)", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=".*安装.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*安装.*", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=".*[Ss]etup.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*[Ss]etup.*", timeout=5),
|
||||
# 已安装应用程序连接方法(新增功能)
|
||||
lambda: Application(backend="uia").connect(title_re=".*Kiro.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*Kiro.*", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=".*Getting started.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*Getting started.*", timeout=5),
|
||||
lambda: Application(backend="uia").connect(path=app_path, timeout=5),
|
||||
lambda: Application(backend="win32").connect(path=app_path, timeout=5),
|
||||
]
|
||||
|
||||
app = None
|
||||
for i, attempt in enumerate(connection_attempts, 1):
|
||||
try:
|
||||
print(f" 尝试方法 {i}...")
|
||||
app = attempt()
|
||||
print(f"✅ 连接成功! (方法{i}) PID: {app.process}")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f" 方法 {i} 失败: {str(e)[:50]}...")
|
||||
continue
|
||||
|
||||
if not app:
|
||||
print("❌ 所有连接方法都失败了")
|
||||
return
|
||||
else:
|
||||
# 原有的连接逻辑
|
||||
try:
|
||||
app = Application(backend="uia").connect(path=app_path, timeout=10)
|
||||
print(f"✅ 连接成功! PID: {app.process}")
|
||||
except:
|
||||
try:
|
||||
app = Application(backend="win32").connect(path=app_path, timeout=10)
|
||||
print(f"✅ 连接成功! (Win32模式) PID: {app.process}")
|
||||
except Exception as e:
|
||||
print(f"❌ 连接失败: {e}")
|
||||
return
|
||||
|
||||
# 等待5秒
|
||||
print("⏳ 等待5秒...")
|
||||
for i in range(5, 0, -1):
|
||||
print(f" {i}秒", end='\r')
|
||||
time.sleep(1)
|
||||
print(" 完成!")
|
||||
|
||||
# 获取所有窗口
|
||||
print("\n" + "="*60)
|
||||
print("🔍 分析窗口控件...")
|
||||
print("="*60)
|
||||
|
||||
try:
|
||||
# 尝试多种方式获取窗口
|
||||
print("🔍 尝试获取窗口...")
|
||||
windows = app.windows()
|
||||
print(f"📊 发现 {len(windows)} 个窗口")
|
||||
|
||||
# 如果没有窗口,尝试其他方法
|
||||
if len(windows) == 0:
|
||||
print("⚠️ 未发现窗口,尝试其他方法...")
|
||||
|
||||
# 方法1:尝试获取顶级窗口
|
||||
try:
|
||||
top_window = app.top_window()
|
||||
print(f"✅ 找到顶级窗口: {top_window.window_text()}")
|
||||
windows = [top_window]
|
||||
except Exception as e:
|
||||
print(f"❌ 获取顶级窗口失败: {e}")
|
||||
|
||||
# 方法2:尝试通过进程ID查找所有窗口
|
||||
if len(windows) == 0:
|
||||
try:
|
||||
from pywinauto import Desktop
|
||||
desktop = Desktop(backend="uia")
|
||||
all_windows = desktop.windows()
|
||||
kiro_windows = []
|
||||
for win in all_windows:
|
||||
try:
|
||||
if win.process_id() == app.process:
|
||||
kiro_windows.append(win)
|
||||
except:
|
||||
continue
|
||||
if kiro_windows:
|
||||
print(f"✅ 通过进程ID找到 {len(kiro_windows)} 个窗口")
|
||||
windows = kiro_windows
|
||||
except Exception as e:
|
||||
print(f"❌ 通过进程ID查找失败: {e}")
|
||||
|
||||
print(f"📊 最终发现 {len(windows)} 个窗口\n")
|
||||
|
||||
for i, window in enumerate(windows, 1):
|
||||
try:
|
||||
title = window.window_text()
|
||||
class_name = window.class_name()
|
||||
visible = window.is_visible()
|
||||
|
||||
print(f"🪟 窗口 {i}: {title or '[无标题]'}")
|
||||
print(f" 类名: {class_name}")
|
||||
print(f" 可见: {'是' if visible else '否'}")
|
||||
|
||||
# 分析所有窗口,不管是否可见
|
||||
print(" 📋 控件列表:")
|
||||
|
||||
# 特殊处理InnoSetup窗口
|
||||
if "InnoSetup" in title or "InnoSetup" in class_name:
|
||||
print(" 🔍 检测到InnoSetup安装程序,尝试激活窗口...")
|
||||
try:
|
||||
window.set_focus()
|
||||
window.restore()
|
||||
time.sleep(1) # 等待窗口激活
|
||||
print(" ✅ 窗口已激活")
|
||||
except Exception as e:
|
||||
print(f" ⚠️ 激活窗口失败: {e}")
|
||||
|
||||
# 获取所有控件
|
||||
try:
|
||||
controls = window.descendants()
|
||||
print(f" 总控件数: {len(controls)}")
|
||||
|
||||
# 显示前20个有用的控件
|
||||
useful_controls = []
|
||||
for ctrl in controls:
|
||||
try:
|
||||
text = ctrl.window_text().strip()
|
||||
ctrl_class_name = ctrl.class_name()
|
||||
|
||||
# 获取UIA属性
|
||||
control_type = ""
|
||||
automation_id = ""
|
||||
if hasattr(ctrl, 'element_info'):
|
||||
try:
|
||||
control_type = ctrl.element_info.control_type
|
||||
automation_id = ctrl.element_info.automation_id or ""
|
||||
except:
|
||||
pass
|
||||
|
||||
# 过滤有用的控件 - 放宽条件,显示更多控件
|
||||
if (text or automation_id or control_type or
|
||||
ctrl_class_name in ['Button', 'Edit', 'ComboBox', 'RadioButton', 'CheckBox', 'Static', 'TButton', 'TEdit'] or
|
||||
control_type in ['Button', 'Edit', 'ComboBox', 'RadioButton', 'CheckBox', 'Text', 'Window', 'Pane']):
|
||||
|
||||
useful_controls.append({
|
||||
'text': text or '[无文本]',
|
||||
'type': control_type or ctrl_class_name,
|
||||
'auto_id': automation_id,
|
||||
'class': ctrl_class_name,
|
||||
'visible': ctrl.is_visible(),
|
||||
'enabled': ctrl.is_enabled()
|
||||
})
|
||||
|
||||
if len(useful_controls) >= 20:
|
||||
break
|
||||
|
||||
except:
|
||||
continue
|
||||
|
||||
# 打印有用的控件
|
||||
for j, ctrl_info in enumerate(useful_controls, 1):
|
||||
status = []
|
||||
if not ctrl_info['visible']:
|
||||
status.append("隐藏")
|
||||
if not ctrl_info['enabled']:
|
||||
status.append("禁用")
|
||||
status_str = f" ({', '.join(status)})" if status else ""
|
||||
|
||||
print(f" {j:2d}. {ctrl_info['text']}{status_str}")
|
||||
print(f" 类型: {ctrl_info['type']}")
|
||||
if ctrl_info['auto_id']:
|
||||
print(f" ID: {ctrl_info['auto_id']}")
|
||||
print(f" 类名: {ctrl_info['class']}")
|
||||
print()
|
||||
|
||||
if len(controls) > len(useful_controls):
|
||||
print(f" ... 还有 {len(controls) - len(useful_controls)} 个其他控件")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 获取控件失败: {e}")
|
||||
|
||||
print("-" * 50)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 分析窗口 {i} 失败: {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 获取窗口失败: {e}")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🚀 快速窗口控件分析器")
|
||||
print("="*40)
|
||||
|
||||
# 获取应用程序路径
|
||||
if len(sys.argv) > 1:
|
||||
app_path = sys.argv[1]
|
||||
else:
|
||||
app_path = input("请输入应用程序路径 (例如: kiro.exe): ").strip()
|
||||
|
||||
if not app_path:
|
||||
print("❌ 未提供应用程序路径")
|
||||
return
|
||||
|
||||
quick_analyze(app_path)
|
||||
print("\n✅ 分析完成!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n⚠️ 用户中断")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 错误: {e}")
|
||||
@@ -0,0 +1,315 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
窗口控件分析器
|
||||
功能:连接指定路径的应用程序,等待5秒后分析并打印所有窗口的控件信息
|
||||
作者:Claude Code Assistant
|
||||
"""
|
||||
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pywinauto import Application
|
||||
from pywinauto.findwindows import ElementNotFoundError
|
||||
|
||||
class WindowAnalyzer:
|
||||
def __init__(self):
|
||||
self.app = None
|
||||
self.analysis_results = []
|
||||
|
||||
def connect_to_application(self, app_path, timeout=30):
|
||||
"""
|
||||
连接到指定路径的应用程序
|
||||
|
||||
Args:
|
||||
app_path (str): 应用程序路径或进程名
|
||||
timeout (int): 连接超时时间(秒)
|
||||
|
||||
Returns:
|
||||
bool: 连接是否成功
|
||||
"""
|
||||
print(f"🔗 正在连接应用程序: {app_path}")
|
||||
print(f"⏱️ 连接超时时间: {timeout}秒")
|
||||
|
||||
# 特殊处理kiro.exe - 支持安装程序和已安装的应用程序
|
||||
if "kiro.exe" in app_path.lower():
|
||||
print("🔍 检测到kiro.exe,尝试连接进程...")
|
||||
connection_methods = [
|
||||
# 安装程序连接方法(保留原有功能)
|
||||
lambda: Application(backend="uia").connect(title_re=".*[Ss]etup.*", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=".*[Ii]nstall.*", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=".*安装.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*[Ss]etup.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*[Ii]nstall.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*安装.*", timeout=5),
|
||||
# 已安装应用程序连接方法(新增功能)
|
||||
lambda: Application(backend="uia").connect(title_re=".*Kiro.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*Kiro.*", timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=".*Getting started.*", timeout=5),
|
||||
lambda: Application(backend="win32").connect(title_re=".*Getting started.*", timeout=5),
|
||||
lambda: Application(backend="uia").connect(path=app_path, timeout=5),
|
||||
lambda: Application(backend="win32").connect(path=app_path, timeout=5),
|
||||
]
|
||||
else:
|
||||
# 原有的连接方式
|
||||
connection_methods = [
|
||||
lambda: Application(backend="uia").connect(path=app_path, timeout=5),
|
||||
lambda: Application(backend="win32").connect(path=app_path, timeout=5),
|
||||
lambda: Application(backend="uia").connect(title_re=f".*{os.path.basename(app_path)}.*", timeout=5),
|
||||
]
|
||||
|
||||
start_time = time.time()
|
||||
attempt = 0
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
attempt += 1
|
||||
print(f"🔄 连接尝试 {attempt}...")
|
||||
|
||||
for i, method in enumerate(connection_methods):
|
||||
try:
|
||||
self.app = method()
|
||||
print(f"✅ 连接成功! 使用方法 {i+1}")
|
||||
print(f"📋 进程ID: {self.app.process}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f" 方法 {i+1} 失败: {str(e)[:50]}...")
|
||||
continue
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
print(f"❌ 连接失败: 超时 {timeout}秒")
|
||||
return False
|
||||
|
||||
def analyze_control(self, ctrl, level=0):
|
||||
"""
|
||||
分析单个控件的详细信息
|
||||
|
||||
Args:
|
||||
ctrl: pywinauto控件对象
|
||||
level (int): 层级深度,用于缩进显示
|
||||
|
||||
Returns:
|
||||
dict: 控件信息字典
|
||||
"""
|
||||
indent = " " * level
|
||||
control_info = {}
|
||||
|
||||
try:
|
||||
# 基本信息
|
||||
control_info['text'] = ctrl.window_text()
|
||||
control_info['class_name'] = ctrl.class_name()
|
||||
control_info['visible'] = ctrl.is_visible()
|
||||
control_info['enabled'] = ctrl.is_enabled()
|
||||
|
||||
# UIA特有属性
|
||||
if hasattr(ctrl, 'element_info'):
|
||||
try:
|
||||
control_info['control_type'] = ctrl.element_info.control_type
|
||||
control_info['automation_id'] = ctrl.element_info.automation_id
|
||||
control_info['name'] = ctrl.element_info.name
|
||||
except:
|
||||
pass
|
||||
|
||||
# 位置信息
|
||||
try:
|
||||
rect = ctrl.rectangle()
|
||||
control_info['position'] = f"({rect.left},{rect.top})-({rect.right},{rect.bottom})"
|
||||
control_info['size'] = f"{rect.width()}x{rect.height()}"
|
||||
except:
|
||||
control_info['position'] = "未知"
|
||||
control_info['size'] = "未知"
|
||||
|
||||
except Exception as e:
|
||||
control_info['error'] = str(e)
|
||||
|
||||
return control_info
|
||||
|
||||
def print_control_tree(self, window, max_depth=3):
|
||||
"""
|
||||
打印窗口的控件树结构
|
||||
|
||||
Args:
|
||||
window: pywinauto窗口对象
|
||||
max_depth (int): 最大遍历深度
|
||||
"""
|
||||
def print_control_recursive(ctrl, level=0):
|
||||
if level > max_depth:
|
||||
return
|
||||
|
||||
indent = " " * level
|
||||
info = self.analyze_control(ctrl, level)
|
||||
|
||||
# 格式化输出
|
||||
display_text = info.get('text', '').strip()
|
||||
if not display_text:
|
||||
display_text = info.get('name', '').strip()
|
||||
if not display_text:
|
||||
display_text = f"[{info.get('class_name', 'Unknown')}]"
|
||||
|
||||
status_indicators = []
|
||||
if not info.get('visible', True):
|
||||
status_indicators.append("隐藏")
|
||||
if not info.get('enabled', True):
|
||||
status_indicators.append("禁用")
|
||||
|
||||
status_str = f" ({', '.join(status_indicators)})" if status_indicators else ""
|
||||
|
||||
print(f"{indent}├─ {display_text}{status_str}")
|
||||
|
||||
# 详细信息
|
||||
if info.get('control_type'):
|
||||
print(f"{indent}│ 类型: {info['control_type']}")
|
||||
if info.get('automation_id'):
|
||||
print(f"{indent}│ ID: {info['automation_id']}")
|
||||
if info.get('class_name'):
|
||||
print(f"{indent}│ 类名: {info['class_name']}")
|
||||
if info.get('position') != "未知":
|
||||
print(f"{indent}│ 位置: {info['position']} 大小: {info['size']}")
|
||||
|
||||
# 递归处理子控件
|
||||
try:
|
||||
children = ctrl.children()
|
||||
for child in children[:20]: # 限制子控件数量避免输出过多
|
||||
print_control_recursive(child, level + 1)
|
||||
|
||||
if len(children) > 20:
|
||||
print(f"{indent} └─ ... 还有 {len(children) - 20} 个子控件")
|
||||
|
||||
except Exception as e:
|
||||
if level == 0: # 只在顶级显示错误
|
||||
print(f"{indent}│ ⚠️ 获取子控件失败: {e}")
|
||||
|
||||
try:
|
||||
print_control_recursive(window)
|
||||
except Exception as e:
|
||||
print(f"❌ 分析窗口控件失败: {e}")
|
||||
|
||||
def analyze_all_windows(self):
|
||||
"""
|
||||
分析应用程序的所有窗口
|
||||
"""
|
||||
if not self.app:
|
||||
print("❌ 应用程序未连接")
|
||||
return
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("🔍 开始分析窗口控件结构...")
|
||||
print("="*80)
|
||||
|
||||
try:
|
||||
windows = self.app.windows()
|
||||
print(f"📊 发现 {len(windows)} 个窗口")
|
||||
|
||||
for i, window in enumerate(windows, 1):
|
||||
try:
|
||||
window_title = window.window_text()
|
||||
window_class = window.class_name()
|
||||
is_visible = window.is_visible()
|
||||
is_enabled = window.is_enabled()
|
||||
|
||||
print(f"\n🪟 窗口 {i}: {window_title or '[无标题]'}")
|
||||
print(f" 类名: {window_class}")
|
||||
print(f" 状态: {'可见' if is_visible else '隐藏'}, {'启用' if is_enabled else '禁用'}")
|
||||
|
||||
try:
|
||||
rect = window.rectangle()
|
||||
print(f" 位置: ({rect.left},{rect.top}) 大小: {rect.width()}x{rect.height()}")
|
||||
except:
|
||||
print(" 位置: 无法获取")
|
||||
|
||||
print(f" 控件结构:")
|
||||
self.print_control_tree(window)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 分析窗口 {i} 失败: {e}")
|
||||
|
||||
print("-" * 60)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 获取窗口列表失败: {e}")
|
||||
|
||||
def save_analysis_to_file(self, filename=None):
|
||||
"""
|
||||
将分析结果保存到文件
|
||||
|
||||
Args:
|
||||
filename (str): 输出文件名,默认使用时间戳
|
||||
"""
|
||||
if not filename:
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"window_analysis_{timestamp}.txt"
|
||||
|
||||
try:
|
||||
# 重定向输出到文件
|
||||
import io
|
||||
from contextlib import redirect_stdout
|
||||
|
||||
output_buffer = io.StringIO()
|
||||
with redirect_stdout(output_buffer):
|
||||
self.analyze_all_windows()
|
||||
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
f.write(f"窗口控件分析报告\n")
|
||||
f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
f.write("="*80 + "\n\n")
|
||||
f.write(output_buffer.getvalue())
|
||||
|
||||
print(f"📄 分析结果已保存到: {filename}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 保存文件失败: {e}")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🔧 窗口控件分析器")
|
||||
print("="*50)
|
||||
|
||||
# 获取应用程序路径
|
||||
if len(sys.argv) > 1:
|
||||
app_path = sys.argv[1]
|
||||
print(f"📝 使用命令行参数: {app_path}")
|
||||
else:
|
||||
app_path = input("请输入应用程序路径或进程名 (例如: kiro.exe 或 C:\\path\\to\\app.exe): ").strip()
|
||||
|
||||
if not app_path:
|
||||
print("❌ 未提供应用程序路径")
|
||||
return
|
||||
|
||||
# 创建分析器
|
||||
analyzer = WindowAnalyzer()
|
||||
|
||||
# 连接应用程序
|
||||
if not analyzer.connect_to_application(app_path):
|
||||
print("❌ 无法连接到应用程序,请确保:")
|
||||
print(" 1. 应用程序正在运行")
|
||||
print(" 2. 路径或进程名正确")
|
||||
print(" 3. 应用程序有可见的用户界面")
|
||||
return
|
||||
|
||||
# 等待5秒
|
||||
print("\n⏳ 等待5秒让应用程序稳定...")
|
||||
for i in range(5, 0, -1):
|
||||
print(f" {i}秒...", end='\r')
|
||||
time.sleep(1)
|
||||
print(" 完成! ")
|
||||
|
||||
# 分析窗口控件
|
||||
analyzer.analyze_all_windows()
|
||||
|
||||
# 询问是否保存到文件
|
||||
save_choice = input("\n💾 是否保存分析结果到文件? (y/n): ").lower()
|
||||
if save_choice in ['y', 'yes', '是']:
|
||||
analyzer.save_analysis_to_file()
|
||||
|
||||
print("\n✅ 分析完成!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⚠️ 用户中断操作")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 程序异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,28 @@
|
||||
$wsbFilePath = "$PSScriptRoot\sandbox_config.wsb"
|
||||
|
||||
$wsbContent = @"
|
||||
<Configuration>
|
||||
<MappedFolders>
|
||||
<MappedFolder>
|
||||
<HostFolder>$PSScriptRoot\sandbox_files</HostFolder>
|
||||
<SandboxFolder>C:\sandbox_files</SandboxFolder>
|
||||
<ReadOnly>false</ReadOnly>
|
||||
</MappedFolder>
|
||||
</MappedFolders>
|
||||
<LogonCommand>
|
||||
<Command>powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "Start-Process powershell.exe -ArgumentList '-NoExit -ExecutionPolicy Bypass -File C:\sandbox_files\install.ps1' -WindowStyle Normal"</Command>
|
||||
</LogonCommand>
|
||||
<AudioInput>false</AudioInput>
|
||||
<ClipboardRedirection>true</ClipboardRedirection>
|
||||
</Configuration>
|
||||
"@
|
||||
|
||||
$wsbContent | Out-File -FilePath $wsbFilePath -Encoding UTF8 -Force
|
||||
|
||||
Write-Host "Sandbox 配置文件已生成: $wsbFilePath" -ForegroundColor Green
|
||||
Write-Host "正在启动 Windows Sandbox,请稍等..." -ForegroundColor Yellow
|
||||
Start-Process -FilePath $wsbFilePath
|
||||
|
||||
Write-Host "Sandbox 已启动!" -ForegroundColor Cyan
|
||||
Write-Host "稍后会自动弹出一个 PowerShell 窗口,显示蓝色安装进度。" -ForegroundColor Magenta
|
||||
Write-Host "请耐心等待 Python 下载和安装(约 1~3 分钟)。" -ForegroundColor Yellow
|
||||
Reference in New Issue
Block a user