@agentunion/kite 1.0.7 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +208 -0
- package/README.md +48 -0
- package/cli.js +1 -1
- package/extensions/agents/__init__.py +1 -0
- package/extensions/agents/assistant/__init__.py +1 -0
- package/extensions/agents/assistant/entry.py +329 -0
- package/extensions/agents/assistant/module.md +22 -0
- package/extensions/agents/assistant/server.py +197 -0
- package/extensions/channels/__init__.py +1 -0
- package/extensions/channels/acp_channel/__init__.py +1 -0
- package/extensions/channels/acp_channel/entry.py +329 -0
- package/extensions/channels/acp_channel/module.md +22 -0
- package/extensions/channels/acp_channel/server.py +197 -0
- package/extensions/event_hub_bench/entry.py +624 -379
- package/extensions/event_hub_bench/module.md +2 -1
- package/extensions/services/backup/__init__.py +1 -0
- package/extensions/services/backup/entry.py +508 -0
- package/extensions/services/backup/module.md +22 -0
- package/extensions/services/model_service/__init__.py +1 -0
- package/extensions/services/model_service/entry.py +508 -0
- package/extensions/services/model_service/module.md +22 -0
- package/extensions/services/watchdog/entry.py +468 -102
- package/extensions/services/watchdog/module.md +3 -0
- package/extensions/services/watchdog/monitor.py +170 -69
- package/extensions/services/web/__init__.py +1 -0
- package/extensions/services/web/config.yaml +149 -0
- package/extensions/services/web/entry.py +390 -0
- package/extensions/services/web/module.md +24 -0
- package/extensions/services/web/routes/__init__.py +1 -0
- package/extensions/services/web/routes/routes_call.py +189 -0
- package/extensions/services/web/routes/routes_config.py +512 -0
- package/extensions/services/web/routes/routes_contacts.py +98 -0
- package/extensions/services/web/routes/routes_devlog.py +99 -0
- package/extensions/services/web/routes/routes_phone.py +81 -0
- package/extensions/services/web/routes/routes_sms.py +48 -0
- package/extensions/services/web/routes/routes_stats.py +17 -0
- package/extensions/services/web/routes/routes_voicechat.py +554 -0
- package/extensions/services/web/routes/schemas.py +216 -0
- package/extensions/services/web/server.py +375 -0
- package/extensions/services/web/static/css/style.css +1064 -0
- package/extensions/services/web/static/index.html +1445 -0
- package/extensions/services/web/static/js/app.js +4671 -0
- package/extensions/services/web/vendor/__init__.py +1 -0
- package/extensions/services/web/vendor/bluetooth/audio.py +348 -0
- package/extensions/services/web/vendor/bluetooth/contacts.py +251 -0
- package/extensions/services/web/vendor/bluetooth/manager.py +395 -0
- package/extensions/services/web/vendor/bluetooth/sms.py +290 -0
- package/extensions/services/web/vendor/bluetooth/telephony.py +274 -0
- package/extensions/services/web/vendor/config.py +139 -0
- package/extensions/services/web/vendor/conversation/asr.py +936 -0
- package/extensions/services/web/vendor/conversation/engine.py +548 -0
- package/extensions/services/web/vendor/conversation/llm.py +534 -0
- package/extensions/services/web/vendor/conversation/mcp_tools.py +190 -0
- package/extensions/services/web/vendor/conversation/tts.py +322 -0
- package/extensions/services/web/vendor/conversation/vad.py +138 -0
- package/extensions/services/web/vendor/storage/__init__.py +1 -0
- package/extensions/services/web/vendor/storage/identity.py +312 -0
- package/extensions/services/web/vendor/storage/store.py +507 -0
- package/extensions/services/web/vendor/task/manager.py +864 -0
- package/extensions/services/web/vendor/task/models.py +45 -0
- package/extensions/services/web/vendor/task/webhook.py +263 -0
- package/extensions/services/web/vendor/tools/registry.py +321 -0
- package/kernel/__init__.py +0 -0
- package/kernel/entry.py +407 -0
- package/{core/event_hub/hub.py → kernel/event_hub.py} +62 -74
- package/kernel/module.md +33 -0
- package/{core/registry/store.py → kernel/registry_store.py} +23 -8
- package/kernel/rpc_router.py +388 -0
- package/kernel/server.py +267 -0
- package/launcher/__init__.py +10 -0
- package/launcher/__main__.py +6 -0
- package/launcher/count_lines.py +258 -0
- package/launcher/entry.py +1778 -0
- package/launcher/logging_setup.py +289 -0
- package/{core/launcher → launcher}/module_scanner.py +11 -6
- package/launcher/process_manager.py +880 -0
- package/main.py +11 -210
- package/package.json +6 -9
- package/__init__.py +0 -1
- package/__main__.py +0 -15
- package/core/event_hub/BENCHMARK.md +0 -94
- package/core/event_hub/bench.py +0 -459
- package/core/event_hub/bench_extreme.py +0 -308
- package/core/event_hub/bench_perf.py +0 -350
- package/core/event_hub/entry.py +0 -157
- package/core/event_hub/module.md +0 -20
- package/core/event_hub/server.py +0 -206
- package/core/launcher/entry.py +0 -1158
- package/core/launcher/process_manager.py +0 -470
- package/core/registry/entry.py +0 -110
- package/core/registry/module.md +0 -30
- package/core/registry/server.py +0 -289
- package/extensions/services/watchdog/server.py +0 -167
- /package/{core → extensions/services/web/vendor/bluetooth}/__init__.py +0 -0
- /package/{core/event_hub → extensions/services/web/vendor/conversation}/__init__.py +0 -0
- /package/{core/launcher → extensions/services/web/vendor/task}/__init__.py +0 -0
- /package/{core/registry → extensions/services/web/vendor/tools}/__init__.py +0 -0
- /package/{core/event_hub → kernel}/dedup.py +0 -0
- /package/{core/event_hub → kernel}/router.py +0 -0
- /package/{core/launcher → launcher}/module.md +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# v1.3.0
|
|
2
|
+
|
|
3
|
+
**发布日期**:2026-03-04
|
|
4
|
+
**上一版本**:v1.2.0
|
|
5
|
+
**版本级别**:minor(架构重构)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 核心变更
|
|
10
|
+
|
|
11
|
+
本次发布完成 Kite 框架的**核心架构重构**,将原有 `core/` 三模块体系(event_hub、registry、launcher)重组为两个独立顶层模块:
|
|
12
|
+
|
|
13
|
+
- **`kernel/`** — 合并 Registry + Event Hub,统一 WebSocket JSON-RPC 2.0 接口
|
|
14
|
+
- **`launcher/`** — 从 `core/launcher/` 提升为顶层模块,支持 `-m launcher` 启动
|
|
15
|
+
|
|
16
|
+
**零共享代码依赖落地**:删除 `core/kite_log.py` 共享日志库,各模块独立实现日志规范,符合跨语言兼容原则。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 变更统计
|
|
21
|
+
|
|
22
|
+
| 类型 | 数量 | 说明 |
|
|
23
|
+
|------|------|------|
|
|
24
|
+
| 新增 | 17 | `kernel/**` (9), `launcher/**` (8) |
|
|
25
|
+
| 删除 | 28 | `core/**` (25), `extensions/services/*/server.py` (3) |
|
|
26
|
+
| 修改 | 15 | 入口文件、扩展模块适配 |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 关键变更
|
|
31
|
+
|
|
32
|
+
### 新增模块
|
|
33
|
+
|
|
34
|
+
**`kernel/`** (9 个文件)
|
|
35
|
+
- 合并 event_hub + registry 为单一基础设施模块
|
|
36
|
+
- 单一 WebSocket 连接处理 RPC 请求和事件推送
|
|
37
|
+
- 内置跨模块 RPC 转发,无需 HTTP 调用
|
|
38
|
+
|
|
39
|
+
**`launcher/`** (8 个文件)
|
|
40
|
+
- 新增 `logging_setup.py` — 统一日志系统(替代 `core/kite_log.py`)
|
|
41
|
+
- 新增 `count_lines.py` — 代码统计工具
|
|
42
|
+
- 支持 `python -m launcher` 模块化启动
|
|
43
|
+
|
|
44
|
+
### 删除内容
|
|
45
|
+
|
|
46
|
+
- **`core/` 目录完全移除** (25 个文件)
|
|
47
|
+
- `core/event_hub/` → `kernel/`(合并)
|
|
48
|
+
- `core/registry/` → `kernel/`(合并)
|
|
49
|
+
- `core/launcher/` → `launcher/`(提升)
|
|
50
|
+
- `core/kite_log.py` — 共享日志库(违反零依赖原则)
|
|
51
|
+
- `core/event_hub/bench*.py` — 性能测试工具(移至独立仓库)
|
|
52
|
+
|
|
53
|
+
- **扩展模块清理** (3 个文件)
|
|
54
|
+
- 删除 `extensions/services/*/server.py` — 独立 HTTP 服务器(改用 Kernel RPC)
|
|
55
|
+
|
|
56
|
+
### 入口变更
|
|
57
|
+
|
|
58
|
+
**`cli.js`** — Python 启动方式改为 `-m launcher`
|
|
59
|
+
```diff
|
|
60
|
+
- [path.join(versionDir, 'main.py'), ...pythonArgs]
|
|
61
|
+
+ ['-m', 'launcher', ...pythonArgs]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**`main.py`** — 简化为开发入口(357 行 → 18 行)
|
|
65
|
+
- 旧版:完整启动器逻辑(日志、环境变量、进程管理)
|
|
66
|
+
- 新版:仅运行代码统计 + 启动 launcher 模块
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 架构演进
|
|
71
|
+
|
|
72
|
+
| 版本 | 核心模块 | 通信协议 | 共享代码 | 文件数 |
|
|
73
|
+
|------|---------|---------|---------|--------|
|
|
74
|
+
| v1.1.0 | 3 (event_hub, registry, launcher) | HTTP REST | `core/kite_log.py` | 72 |
|
|
75
|
+
| v1.2.0 | 3 (同上) | HTTP REST | `core/kite_log.py` | 97 |
|
|
76
|
+
| v1.3.0 | 2 (kernel, launcher) | WebSocket RPC | 无 | 87 |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 设计原则
|
|
81
|
+
|
|
82
|
+
- ✅ **零共享代码依赖** — 删除 `core/kite_log.py`,各模块独立实现
|
|
83
|
+
- ✅ **跨语言兼容** — WebSocket JSON-RPC 2.0 标准协议
|
|
84
|
+
- ✅ **跨平台兼容** — `-m launcher` 启动方式通用
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 包信息
|
|
89
|
+
|
|
90
|
+
- **文件数**:87(-10 vs v1.2.0)
|
|
91
|
+
- **打包大小**:205.7 kB
|
|
92
|
+
- **解压大小**:966.4 kB
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 破坏性变更
|
|
97
|
+
|
|
98
|
+
虽标记为 minor,但包含以下潜在破坏性变更:
|
|
99
|
+
|
|
100
|
+
1. **目录结构** — `core/**` → `kernel/` + `launcher/`
|
|
101
|
+
2. **Python 入口** — `python main.py` → `python -m launcher`
|
|
102
|
+
3. **通信协议** — HTTP REST → WebSocket RPC
|
|
103
|
+
|
|
104
|
+
对外 CLI 接口(`kite` 命令)保持兼容,用户无感知。
|
|
105
|
+
# v1.2.0
|
|
106
|
+
|
|
107
|
+
**发布日期**:2026-03-03
|
|
108
|
+
**上一版本**:v1.1.0
|
|
109
|
+
**版本级别**:minor
|
|
110
|
+
|
|
111
|
+
## 变更文件
|
|
112
|
+
|
|
113
|
+
### 新增
|
|
114
|
+
- `extensions/services/web/config.yaml`
|
|
115
|
+
- `extensions/services/web/vendor/__init__.py`
|
|
116
|
+
- `extensions/services/web/vendor/config.py`
|
|
117
|
+
- `extensions/services/web/vendor/bluetooth/__init__.py`
|
|
118
|
+
- `extensions/services/web/vendor/bluetooth/audio.py`
|
|
119
|
+
- `extensions/services/web/vendor/bluetooth/contacts.py`
|
|
120
|
+
- `extensions/services/web/vendor/bluetooth/manager.py`
|
|
121
|
+
- `extensions/services/web/vendor/bluetooth/sms.py`
|
|
122
|
+
- `extensions/services/web/vendor/bluetooth/telephony.py`
|
|
123
|
+
- `extensions/services/web/vendor/conversation/__init__.py`
|
|
124
|
+
- `extensions/services/web/vendor/conversation/asr.py`
|
|
125
|
+
- `extensions/services/web/vendor/conversation/engine.py`
|
|
126
|
+
- `extensions/services/web/vendor/conversation/llm.py`
|
|
127
|
+
- `extensions/services/web/vendor/conversation/mcp_tools.py`
|
|
128
|
+
- `extensions/services/web/vendor/conversation/tts.py`
|
|
129
|
+
- `extensions/services/web/vendor/conversation/vad.py`
|
|
130
|
+
- `extensions/services/web/vendor/storage/__init__.py`
|
|
131
|
+
- `extensions/services/web/vendor/storage/identity.py`
|
|
132
|
+
- `extensions/services/web/vendor/storage/store.py`
|
|
133
|
+
- `extensions/services/web/vendor/task/__init__.py`
|
|
134
|
+
- `extensions/services/web/vendor/task/manager.py`
|
|
135
|
+
- `extensions/services/web/vendor/task/models.py`
|
|
136
|
+
- `extensions/services/web/vendor/task/webhook.py`
|
|
137
|
+
- `extensions/services/web/vendor/tools/__init__.py`
|
|
138
|
+
- `extensions/services/web/vendor/tools/registry.py`
|
|
139
|
+
|
|
140
|
+
### 删除
|
|
141
|
+
(无)
|
|
142
|
+
|
|
143
|
+
### 修改
|
|
144
|
+
- `core/event_hub/server.py`
|
|
145
|
+
- `core/launcher/entry.py`
|
|
146
|
+
- `core/launcher/process_manager.py`
|
|
147
|
+
- `extensions/agents/assistant/entry.py`
|
|
148
|
+
- `extensions/agents/assistant/server.py`
|
|
149
|
+
- `extensions/channels/acp_channel/entry.py`
|
|
150
|
+
- `extensions/channels/acp_channel/server.py`
|
|
151
|
+
- `extensions/event_hub_bench/module.md`
|
|
152
|
+
- `extensions/services/backup/entry.py`
|
|
153
|
+
- `extensions/services/backup/server.py`
|
|
154
|
+
- `extensions/services/model_service/entry.py`
|
|
155
|
+
- `extensions/services/model_service/server.py`
|
|
156
|
+
- `extensions/services/watchdog/entry.py`
|
|
157
|
+
- `extensions/services/watchdog/server.py`
|
|
158
|
+
- `extensions/services/web/entry.py`
|
|
159
|
+
- `extensions/services/web/routes/routes_call.py`
|
|
160
|
+
- `extensions/services/web/routes/routes_config.py`
|
|
161
|
+
- `extensions/services/web/routes/routes_contacts.py`
|
|
162
|
+
- `extensions/services/web/routes/routes_devlog.py`
|
|
163
|
+
- `extensions/services/web/routes/routes_phone.py`
|
|
164
|
+
- `extensions/services/web/routes/routes_sms.py`
|
|
165
|
+
- `extensions/services/web/routes/routes_stats.py`
|
|
166
|
+
- `extensions/services/web/routes/routes_voicechat.py`
|
|
167
|
+
- `extensions/services/web/server.py`
|
|
168
|
+
- `main.py`
|
|
169
|
+
|
|
170
|
+
## 变更摘要
|
|
171
|
+
|
|
172
|
+
新增 Web 模块的 vendor 子系统,将 bluetooth、conversation、storage、task、tools 等依赖模块内联到 web 服务中,实现 web 模块的独立运行能力。同时更新了各模块的 entry/server 文件和 web routes,适配新的架构调整。
|
|
173
|
+
# v1.1.0
|
|
174
|
+
|
|
175
|
+
**发布日期**:2026-03-02
|
|
176
|
+
**上一版本**:v1.0.8
|
|
177
|
+
**版本级别**:minor
|
|
178
|
+
|
|
179
|
+
## 变更文件
|
|
180
|
+
|
|
181
|
+
### 新增
|
|
182
|
+
- `core/kite_log.py` — 统一日志工具模块
|
|
183
|
+
- `extensions/agents/` — Agent 扩展目录
|
|
184
|
+
- `extensions/channels/` — Channel 扩展目录
|
|
185
|
+
- `extensions/services/backup/` — 备份服务模块
|
|
186
|
+
- `extensions/services/model_service/` — 模型服务模块
|
|
187
|
+
- `extensions/services/web/` — Web 管理服务模块
|
|
188
|
+
|
|
189
|
+
### 修改
|
|
190
|
+
- `main.py`
|
|
191
|
+
- `core/event_hub/entry.py`
|
|
192
|
+
- `core/event_hub/hub.py`
|
|
193
|
+
- `core/event_hub/server.py`
|
|
194
|
+
- `core/launcher/entry.py`
|
|
195
|
+
- `core/launcher/process_manager.py`
|
|
196
|
+
- `core/registry/entry.py`
|
|
197
|
+
- `core/registry/server.py`
|
|
198
|
+
- `core/registry/store.py`
|
|
199
|
+
- `extensions/event_hub_bench/entry.py`
|
|
200
|
+
- `extensions/event_hub_bench/module.md`
|
|
201
|
+
- `extensions/services/watchdog/entry.py`
|
|
202
|
+
- `extensions/services/watchdog/module.md`
|
|
203
|
+
- `extensions/services/watchdog/monitor.py`
|
|
204
|
+
- `extensions/services/watchdog/server.py`
|
|
205
|
+
|
|
206
|
+
## 变更摘要
|
|
207
|
+
|
|
208
|
+
重大功能更新:新增三个扩展类型目录(agents、channels、services),引入统一日志工具 `kite_log.py`,新增 backup、model_service、web 三个服务模块。核心模块(launcher、registry、event_hub)进行了多项优化和增强,包括进程管理改进、事件路由优化、模块生命周期管理完善。打包文件数从 36 个增至 72 个,框架扩展性和功能完整度显著提升。
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Kite 框架
|
|
2
|
+
|
|
3
|
+
跨平台、跨语言的模块化框架。
|
|
4
|
+
|
|
5
|
+
## 目录结构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Kite/
|
|
9
|
+
├── launcher/ # 启动器(核心模块)
|
|
10
|
+
├── kernel/ # 内核(核心模块)- Registry + Event Hub 合并
|
|
11
|
+
├── extensions/ # 扩展模块
|
|
12
|
+
│ ├── agents/ # AI 代理
|
|
13
|
+
│ ├── channels/ # 通信通道
|
|
14
|
+
│ └── services/ # 服务模块
|
|
15
|
+
├── scripts/ # 工具脚本
|
|
16
|
+
├── docs/ # 文档
|
|
17
|
+
├── tests/ # 测试相关
|
|
18
|
+
│ ├── modules/ # 测试模块
|
|
19
|
+
│ └── test_*.py # 测试脚本
|
|
20
|
+
├── .dev/ # 开发相关(不提交)
|
|
21
|
+
│ ├── changelogs/ # 变更日志
|
|
22
|
+
│ └── releases/ # 发布备份
|
|
23
|
+
├── data/ # 运行时数据(不提交)
|
|
24
|
+
├── main.py # 入口文件
|
|
25
|
+
├── cli.js # CLI 工具
|
|
26
|
+
└── CLAUDE.md # 项目说明
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 启动 Kite
|
|
33
|
+
python main.py
|
|
34
|
+
|
|
35
|
+
# 查看代码统计
|
|
36
|
+
python main.py --count-lines
|
|
37
|
+
|
|
38
|
+
# 查看统计历史
|
|
39
|
+
python scripts/count_lines.py --history
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 核心原则
|
|
43
|
+
|
|
44
|
+
1. **零共享代码依赖** - 模块间无代码级依赖
|
|
45
|
+
2. **跨平台兼容** - Windows / Linux / macOS
|
|
46
|
+
3. **跨语言兼容** - Python / Node.js / Binary
|
|
47
|
+
|
|
48
|
+
详细说明见 `CLAUDE.md` 和 `docs/`。
|
package/cli.js
CHANGED
|
@@ -155,7 +155,7 @@ const env = {
|
|
|
155
155
|
const python = process.platform === 'win32' ? 'python' : 'python3';
|
|
156
156
|
const child = spawn(
|
|
157
157
|
python,
|
|
158
|
-
[
|
|
158
|
+
['-m', 'launcher', ...pythonArgs],
|
|
159
159
|
{ cwd: versionDir, env, stdio: 'inherit' }
|
|
160
160
|
);
|
|
161
161
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Assistant entry point.
|
|
3
|
+
Reads boot_info from stdin, connects to Kernel via WebSocket JSON-RPC 2.0.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import builtins
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import sys
|
|
12
|
+
import threading
|
|
13
|
+
import time
|
|
14
|
+
import traceback
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ── Safe stdout/stderr: ignore BrokenPipeError after Launcher closes stdio ──
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ── Module configuration ──
|
|
22
|
+
MODULE_NAME = "assistant"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _SafeWriter:
|
|
26
|
+
"""Wraps a stream to silently swallow BrokenPipeError on write/flush."""
|
|
27
|
+
def __init__(self, stream):
|
|
28
|
+
self._stream = stream
|
|
29
|
+
|
|
30
|
+
def write(self, s):
|
|
31
|
+
try:
|
|
32
|
+
self._stream.write(s)
|
|
33
|
+
except (BrokenPipeError, OSError):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def flush(self):
|
|
37
|
+
try:
|
|
38
|
+
self._stream.flush()
|
|
39
|
+
except (BrokenPipeError, OSError):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def __getattr__(self, name):
|
|
43
|
+
return getattr(self._stream, name)
|
|
44
|
+
|
|
45
|
+
sys.stdout = _SafeWriter(sys.stdout)
|
|
46
|
+
sys.stderr = _SafeWriter(sys.stderr)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ── Timestamped print + log file writer ──
|
|
50
|
+
# Independent implementation per module (no shared code dependency)
|
|
51
|
+
|
|
52
|
+
_builtin_print = builtins.print
|
|
53
|
+
_start_ts = time.monotonic()
|
|
54
|
+
_last_ts = time.monotonic()
|
|
55
|
+
_ANSI_RE = re.compile(r"\033\[[0-9;]*m")
|
|
56
|
+
_log_lock = threading.Lock()
|
|
57
|
+
_log_latest_path = None
|
|
58
|
+
_log_daily_path = None
|
|
59
|
+
_log_daily_date = ""
|
|
60
|
+
_log_dir = None
|
|
61
|
+
_crash_log_path = None
|
|
62
|
+
|
|
63
|
+
def _strip_ansi(s: str) -> str:
|
|
64
|
+
return _ANSI_RE.sub("", s)
|
|
65
|
+
|
|
66
|
+
def _resolve_daily_log_path():
|
|
67
|
+
"""Resolve daily log path based on current date."""
|
|
68
|
+
global _log_daily_path, _log_daily_date
|
|
69
|
+
if not _log_dir:
|
|
70
|
+
return
|
|
71
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
72
|
+
if today == _log_daily_date and _log_daily_path:
|
|
73
|
+
return
|
|
74
|
+
month_dir = os.path.join(_log_dir, today[:7])
|
|
75
|
+
os.makedirs(month_dir, exist_ok=True)
|
|
76
|
+
_log_daily_path = os.path.join(month_dir, f"{today}.log")
|
|
77
|
+
_log_daily_date = today
|
|
78
|
+
|
|
79
|
+
def _write_log(plain_line: str):
|
|
80
|
+
"""Write a plain-text line to both latest.log and daily log."""
|
|
81
|
+
with _log_lock:
|
|
82
|
+
if _log_latest_path:
|
|
83
|
+
try:
|
|
84
|
+
with open(_log_latest_path, "a", encoding="utf-8") as f:
|
|
85
|
+
f.write(plain_line)
|
|
86
|
+
except Exception:
|
|
87
|
+
pass
|
|
88
|
+
_resolve_daily_log_path()
|
|
89
|
+
if _log_daily_path:
|
|
90
|
+
try:
|
|
91
|
+
with open(_log_daily_path, "a", encoding="utf-8") as f:
|
|
92
|
+
f.write(plain_line)
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
def _write_crash(exc_type, exc_value, exc_tb, thread_name=None, severity="critical", handled=False):
|
|
97
|
+
"""Write crash record to crashes.jsonl + daily crash archive."""
|
|
98
|
+
record = {
|
|
99
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
100
|
+
"module": MODULE_NAME,
|
|
101
|
+
"thread": thread_name or threading.current_thread().name,
|
|
102
|
+
"exception_type": exc_type.__name__ if exc_type else "Unknown",
|
|
103
|
+
"exception_message": str(exc_value),
|
|
104
|
+
"traceback": "".join(traceback.format_exception(exc_type, exc_value, exc_tb)),
|
|
105
|
+
"severity": severity,
|
|
106
|
+
"handled": handled,
|
|
107
|
+
"process_id": os.getpid(),
|
|
108
|
+
"platform": sys.platform,
|
|
109
|
+
"runtime_version": f"Python {sys.version.split()[0]}",
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if exc_tb:
|
|
113
|
+
tb_entries = traceback.extract_tb(exc_tb)
|
|
114
|
+
if tb_entries:
|
|
115
|
+
last = tb_entries[-1]
|
|
116
|
+
record["context"] = {
|
|
117
|
+
"function": last.name,
|
|
118
|
+
"file": os.path.basename(last.filename),
|
|
119
|
+
"line": last.lineno,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
line = json.dumps(record, ensure_ascii=False) + "\n"
|
|
123
|
+
|
|
124
|
+
if _crash_log_path:
|
|
125
|
+
try:
|
|
126
|
+
with open(_crash_log_path, "a", encoding="utf-8") as f:
|
|
127
|
+
f.write(line)
|
|
128
|
+
except Exception:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
if _log_dir:
|
|
132
|
+
try:
|
|
133
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
134
|
+
archive_dir = os.path.join(_log_dir, "crashes", today[:7])
|
|
135
|
+
os.makedirs(archive_dir, exist_ok=True)
|
|
136
|
+
archive_path = os.path.join(archive_dir, f"{today}.jsonl")
|
|
137
|
+
with open(archive_path, "a", encoding="utf-8") as f:
|
|
138
|
+
f.write(line)
|
|
139
|
+
except Exception:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
def _print_crash_summary(exc_type, exc_tb, thread_name=None):
|
|
143
|
+
"""Print crash summary to console (red highlight)."""
|
|
144
|
+
RED = "\033[91m"
|
|
145
|
+
RESET = "\033[0m"
|
|
146
|
+
|
|
147
|
+
if exc_tb:
|
|
148
|
+
tb_entries = traceback.extract_tb(exc_tb)
|
|
149
|
+
if tb_entries:
|
|
150
|
+
last = tb_entries[-1]
|
|
151
|
+
location = f"{os.path.basename(last.filename)}:{last.lineno}"
|
|
152
|
+
else:
|
|
153
|
+
location = "unknown"
|
|
154
|
+
else:
|
|
155
|
+
location = "unknown"
|
|
156
|
+
|
|
157
|
+
prefix = f"[{MODULE_NAME}]"
|
|
158
|
+
if thread_name:
|
|
159
|
+
_builtin_print(f"{prefix} {RED}线程 {thread_name} 崩溃: "
|
|
160
|
+
f"{exc_type.__name__} in {location}{RESET}")
|
|
161
|
+
else:
|
|
162
|
+
_builtin_print(f"{prefix} {RED}崩溃: {exc_type.__name__} in {location}{RESET}")
|
|
163
|
+
if _crash_log_path:
|
|
164
|
+
_builtin_print(f"{prefix} 崩溃日志: {_crash_log_path}")
|
|
165
|
+
|
|
166
|
+
def _setup_exception_hooks():
|
|
167
|
+
"""Set up global exception hooks."""
|
|
168
|
+
_orig_excepthook = sys.excepthook
|
|
169
|
+
|
|
170
|
+
def _excepthook(exc_type, exc_value, exc_tb):
|
|
171
|
+
_write_crash(exc_type, exc_value, exc_tb, severity="critical", handled=False)
|
|
172
|
+
_print_crash_summary(exc_type, exc_tb)
|
|
173
|
+
_orig_excepthook(exc_type, exc_value, exc_tb)
|
|
174
|
+
|
|
175
|
+
sys.excepthook = _excepthook
|
|
176
|
+
|
|
177
|
+
if hasattr(threading, "excepthook"):
|
|
178
|
+
def _thread_excepthook(args):
|
|
179
|
+
_write_crash(args.exc_type, args.exc_value, args.exc_traceback,
|
|
180
|
+
thread_name=args.thread.name if args.thread else "unknown",
|
|
181
|
+
severity="error", handled=False)
|
|
182
|
+
_print_crash_summary(args.exc_type, args.exc_traceback,
|
|
183
|
+
thread_name=args.thread.name if args.thread else None)
|
|
184
|
+
|
|
185
|
+
threading.excepthook = _thread_excepthook
|
|
186
|
+
|
|
187
|
+
def _tprint(*args, **kwargs):
|
|
188
|
+
"""Timestamped print that adds [timestamp] HH:MM:SS.mmm +delta prefix."""
|
|
189
|
+
global _last_ts
|
|
190
|
+
now = time.monotonic()
|
|
191
|
+
elapsed = now - _start_ts
|
|
192
|
+
delta = now - _last_ts
|
|
193
|
+
_last_ts = now
|
|
194
|
+
|
|
195
|
+
if elapsed < 1:
|
|
196
|
+
elapsed_str = f"{elapsed * 1000:.0f}ms"
|
|
197
|
+
elif elapsed < 100:
|
|
198
|
+
elapsed_str = f"{elapsed:.1f}s"
|
|
199
|
+
else:
|
|
200
|
+
elapsed_str = f"{elapsed:.0f}s"
|
|
201
|
+
|
|
202
|
+
if delta < 0.001:
|
|
203
|
+
delta_str = ""
|
|
204
|
+
elif delta < 1:
|
|
205
|
+
delta_str = f"+{delta * 1000:.0f}ms"
|
|
206
|
+
elif delta < 100:
|
|
207
|
+
delta_str = f"+{delta:.1f}s"
|
|
208
|
+
else:
|
|
209
|
+
delta_str = f"+{delta:.0f}s"
|
|
210
|
+
|
|
211
|
+
ts = datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
|
212
|
+
|
|
213
|
+
_builtin_print(*args, **kwargs)
|
|
214
|
+
|
|
215
|
+
if _log_latest_path or _log_daily_path:
|
|
216
|
+
sep = kwargs.get("sep", " ")
|
|
217
|
+
end = kwargs.get("end", "\n")
|
|
218
|
+
text = sep.join(str(a) for a in args)
|
|
219
|
+
prefix = f"[{elapsed_str:>6}] {ts} {delta_str:>8} "
|
|
220
|
+
_write_log(prefix + _strip_ansi(text) + end)
|
|
221
|
+
|
|
222
|
+
builtins.print = _tprint
|
|
223
|
+
|
|
224
|
+
# Ensure project root is on sys.path (set by main.py or cli.js)
|
|
225
|
+
_project_root = os.environ.get("KITE_PROJECT") or os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
226
|
+
if _project_root not in sys.path:
|
|
227
|
+
sys.path.insert(0, _project_root)
|
|
228
|
+
|
|
229
|
+
from extensions.agents.assistant.server import AssistantServer
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _fmt_elapsed(t0: float) -> str:
|
|
233
|
+
d = time.monotonic() - t0
|
|
234
|
+
if d < 1:
|
|
235
|
+
return f"{d * 1000:.0f}ms"
|
|
236
|
+
if d < 10:
|
|
237
|
+
return f"{d:.1f}s"
|
|
238
|
+
return f"{d:.0f}s"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _read_stdin_kite_message(expected_type: str, timeout: float = 10) -> dict | None:
|
|
242
|
+
"""Read a single kite message of expected type from stdin with timeout."""
|
|
243
|
+
result = [None]
|
|
244
|
+
|
|
245
|
+
def _read():
|
|
246
|
+
try:
|
|
247
|
+
line = sys.stdin.readline().strip()
|
|
248
|
+
if line:
|
|
249
|
+
msg = json.loads(line)
|
|
250
|
+
if isinstance(msg, dict) and msg.get("kite") == expected_type:
|
|
251
|
+
result[0] = msg
|
|
252
|
+
except Exception:
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
t = threading.Thread(target=_read, daemon=True)
|
|
256
|
+
t.start()
|
|
257
|
+
t.join(timeout=timeout)
|
|
258
|
+
return result[0]
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def main():
|
|
262
|
+
# Initialize log file paths
|
|
263
|
+
global _log_dir, _log_latest_path, _crash_log_path
|
|
264
|
+
module_data = os.environ.get("KITE_MODULE_DATA")
|
|
265
|
+
if module_data:
|
|
266
|
+
_log_dir = os.path.join(module_data, "log")
|
|
267
|
+
os.makedirs(_log_dir, exist_ok=True)
|
|
268
|
+
suffix = os.environ.get("KITE_INSTANCE_SUFFIX", "")
|
|
269
|
+
|
|
270
|
+
_log_latest_path = os.path.join(_log_dir, f"latest{suffix}.log")
|
|
271
|
+
try:
|
|
272
|
+
with open(_log_latest_path, "w", encoding="utf-8") as f:
|
|
273
|
+
pass
|
|
274
|
+
except Exception:
|
|
275
|
+
_log_latest_path = None
|
|
276
|
+
|
|
277
|
+
_crash_log_path = os.path.join(_log_dir, f"crashes{suffix}.jsonl")
|
|
278
|
+
try:
|
|
279
|
+
with open(_crash_log_path, "w", encoding="utf-8") as f:
|
|
280
|
+
pass
|
|
281
|
+
except Exception:
|
|
282
|
+
_crash_log_path = None
|
|
283
|
+
|
|
284
|
+
_resolve_daily_log_path()
|
|
285
|
+
|
|
286
|
+
_setup_exception_hooks()
|
|
287
|
+
|
|
288
|
+
_t0 = time.monotonic()
|
|
289
|
+
|
|
290
|
+
# Read boot_info from stdin (only token)
|
|
291
|
+
token = ""
|
|
292
|
+
try:
|
|
293
|
+
line = sys.stdin.readline().strip()
|
|
294
|
+
if line:
|
|
295
|
+
boot_info = json.loads(line)
|
|
296
|
+
token = boot_info.get("token", "")
|
|
297
|
+
except Exception:
|
|
298
|
+
pass
|
|
299
|
+
|
|
300
|
+
# Read kernel_port: env first (fast path), stdin fallback (parallel start)
|
|
301
|
+
kernel_port = int(os.environ.get("KITE_KERNEL_PORT", "0"))
|
|
302
|
+
if not kernel_port:
|
|
303
|
+
msg = _read_stdin_kite_message("kernel_port", timeout=10)
|
|
304
|
+
if msg:
|
|
305
|
+
kernel_port = int(msg.get("kernel_port", 0))
|
|
306
|
+
|
|
307
|
+
if not token or not kernel_port:
|
|
308
|
+
print("[assistant] ERROR: Missing token or kernel_port")
|
|
309
|
+
sys.exit(1)
|
|
310
|
+
|
|
311
|
+
print(f"[assistant] Token received ({len(token)} chars), kernel port: {kernel_port} ({_fmt_elapsed(_t0)})")
|
|
312
|
+
|
|
313
|
+
server = AssistantServer(
|
|
314
|
+
token=token,
|
|
315
|
+
kernel_port=kernel_port,
|
|
316
|
+
boot_t0=_t0,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
print(f"[assistant] Starting ({_fmt_elapsed(_t0)})")
|
|
320
|
+
try:
|
|
321
|
+
asyncio.run(server.run())
|
|
322
|
+
except Exception as e:
|
|
323
|
+
_write_crash(type(e), e, e.__traceback__, severity="critical", handled=True)
|
|
324
|
+
_print_crash_summary(type(e), e.__traceback__)
|
|
325
|
+
sys.exit(1)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
if __name__ == "__main__":
|
|
329
|
+
main()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: assistant
|
|
3
|
+
display_name: Assistant
|
|
4
|
+
version: "1.0"
|
|
5
|
+
type: agent
|
|
6
|
+
state: enabled
|
|
7
|
+
runtime: python
|
|
8
|
+
entry: entry.py
|
|
9
|
+
events:
|
|
10
|
+
- assistant.test
|
|
11
|
+
subscriptions:
|
|
12
|
+
- module.started
|
|
13
|
+
- module.stopped
|
|
14
|
+
- module.shutdown
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Assistant(AI 助手代理)
|
|
18
|
+
|
|
19
|
+
AI 助手代理模块,负责对话管理和任务执行。
|
|
20
|
+
|
|
21
|
+
- 对话管理 — 处理用户对话请求并生成响应
|
|
22
|
+
- 事件通知 — 通过 Kernel 发布助手状态事件
|