aizz-server 1.0.0__tar.gz
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.
Potentially problematic release.
This version of aizz-server might be problematic. Click here for more details.
- aizz_server-1.0.0/PKG-INFO +546 -0
- aizz_server-1.0.0/README.md +529 -0
- aizz_server-1.0.0/aizz_server/CHAT_INSTRUCTIONS.example.md +4 -0
- aizz_server-1.0.0/aizz_server/CHAT_MEMORY.example.md +3 -0
- aizz_server-1.0.0/aizz_server/__init__.py +2 -0
- aizz_server-1.0.0/aizz_server/app.py +2184 -0
- aizz_server-1.0.0/aizz_server/auth_middleware.py +73 -0
- aizz_server-1.0.0/aizz_server/background_tasks.py +248 -0
- aizz_server-1.0.0/aizz_server/chat_tools.py +487 -0
- aizz_server-1.0.0/aizz_server/cli.py +105 -0
- aizz_server-1.0.0/aizz_server/codex_bridge.py +386 -0
- aizz_server-1.0.0/aizz_server/codex_proxy.py +836 -0
- aizz_server-1.0.0/aizz_server/config.example.json +34 -0
- aizz_server-1.0.0/aizz_server/device_auth.py +155 -0
- aizz_server-1.0.0/aizz_server/migrate_storage.py +127 -0
- aizz_server-1.0.0/aizz_server/pairing.py +30 -0
- aizz_server-1.0.0/aizz_server/service_logging.py +27 -0
- aizz_server-1.0.0/aizz_server/skills.py +137 -0
- aizz_server-1.0.0/aizz_server/storage_paths.py +28 -0
- aizz_server-1.0.0/aizz_server/task_notifications.py +145 -0
- aizz_server-1.0.0/aizz_server/terminal.py +140 -0
- aizz_server-1.0.0/aizz_server/tool_policy.example.json +11 -0
- aizz_server-1.0.0/aizz_server/tool_policy.py +114 -0
- aizz_server-1.0.0/aizz_server/workspace_git.py +409 -0
- aizz_server-1.0.0/aizz_server/workspace_preview.py +30 -0
- aizz_server-1.0.0/aizz_server/workspaces.example.json +4 -0
- aizz_server-1.0.0/aizz_server/worktrees.py +45 -0
- aizz_server-1.0.0/aizz_server.egg-info/PKG-INFO +546 -0
- aizz_server-1.0.0/aizz_server.egg-info/SOURCES.txt +83 -0
- aizz_server-1.0.0/aizz_server.egg-info/dependency_links.txt +1 -0
- aizz_server-1.0.0/aizz_server.egg-info/entry_points.txt +2 -0
- aizz_server-1.0.0/aizz_server.egg-info/requires.txt +7 -0
- aizz_server-1.0.0/aizz_server.egg-info/top_level.txt +1 -0
- aizz_server-1.0.0/pyproject.toml +35 -0
- aizz_server-1.0.0/setup.cfg +4 -0
- aizz_server-1.0.0/tests/test_background_tasks.py +292 -0
- aizz_server-1.0.0/tests/test_chat_agent.py +646 -0
- aizz_server-1.0.0/tests/test_chat_edit.py +216 -0
- aizz_server-1.0.0/tests/test_cli.py +163 -0
- aizz_server-1.0.0/tests/test_codex.py +369 -0
- aizz_server-1.0.0/tests/test_codex_homes.py +190 -0
- aizz_server-1.0.0/tests/test_codex_proxy.py +533 -0
- aizz_server-1.0.0/tests/test_codex_quota.py +109 -0
- aizz_server-1.0.0/tests/test_command_grants.py +164 -0
- aizz_server-1.0.0/tests/test_device_auth.py +193 -0
- aizz_server-1.0.0/tests/test_generation_stop.py +133 -0
- aizz_server-1.0.0/tests/test_git_commit_message.py +91 -0
- aizz_server-1.0.0/tests/test_model_settings.py +235 -0
- aizz_server-1.0.0/tests/test_notification_config.py +55 -0
- aizz_server-1.0.0/tests/test_regeneration.py +193 -0
- aizz_server-1.0.0/tests/test_session_preferences.py +202 -0
- aizz_server-1.0.0/tests/test_skills.py +288 -0
- aizz_server-1.0.0/tests/test_storage.py +147 -0
- aizz_server-1.0.0/tests/test_temporary_sessions.py +71 -0
- aizz_server-1.0.0/tests/test_terminal.py +99 -0
- aizz_server-1.0.0/tests/test_tool_policy.py +158 -0
- aizz_server-1.0.0/tests/test_workspace_files.py +183 -0
- aizz_server-1.0.0/tests/test_workspace_git.py +429 -0
- aizz_server-1.0.0/tests/test_workspaces.py +121 -0
- aizz_server-1.0.0/tests/test_worktrees.py +196 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aizz-server
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Self-hosted AIZZ server for remote chat and Codex coding
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Operating System :: MacOS
|
|
7
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: fastapi<1,>=0.115
|
|
11
|
+
Requires-Dist: uvicorn[standard]<1,>=0.30
|
|
12
|
+
Requires-Dist: httpx[http2]<1,>=0.27
|
|
13
|
+
Requires-Dist: anyio<5,>=3.7.1
|
|
14
|
+
Requires-Dist: PyJWT[crypto]<3,>=2.9
|
|
15
|
+
Requires-Dist: qrcode<9,>=8
|
|
16
|
+
Requires-Dist: PyYAML<7,>=6
|
|
17
|
+
|
|
18
|
+
# AIZZ 服务端
|
|
19
|
+
|
|
20
|
+
自托管 Python API,为 [AIZZ Flutter 客户端](https://github.com/362228416/aizz-client)提供聊天、Codex 任务、设备配对、搜索与通知。本仓库可独立安装和运行。
|
|
21
|
+
|
|
22
|
+
## 私人配置与数据目录
|
|
23
|
+
|
|
24
|
+
配置、工作目录列表、聊天指令、长期知识、工具策略和 SQLite 数据库统一放在服务端用户的 `~/.aizz/` 下;项目只保留代码和通用 `.example` 示例。目录权限为 `700`,私人文件权限为 `600`。迁移命令必须使用与服务端相同的系统用户运行。
|
|
25
|
+
|
|
26
|
+
旧版升级时,先手动停止服务端,再执行:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
.venv/bin/python migrate_storage.py
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
命令检查数据库占用(需要 `lsof`),并将旧项目 `server/` 中的 `config.json`、`workspaces.json`、`CHAT_INSTRUCTIONS.md`、`CHAT_MEMORY.md`、`assistant.db` 和 `tool_policy.json` 迁移到新目录。它保留尚未提交的文件内容,将 workspace 相对路径转换为原来的绝对路径,检查数据库完整性和各表记录数量,并在 `~/.aizz/backups/` 留下数据库备份。所有目标文件校验成功后才删除旧文件;目标已存在同名文件时停止,避免覆盖。迁移完成后由你手动启动服务端。重复执行不会覆盖已迁移的数据。新安装执行同一命令即可从通用示例初始化配置,数据库在服务端启动时创建。
|
|
33
|
+
|
|
34
|
+
`ASSISTANT_CONFIG`、`ASSISTANT_WORKSPACES`、`ASSISTANT_DB` 仍支持指定其他文件(支持 `~`);如果以前设置过,请删除旧路径覆盖或更新到新位置。服务端与设备授权命令必须使用相同的 `ASSISTANT_DB`。`tests/test_openai.sh` 默认读取 `~/.aizz/config.json`,也支持 `OPENAI_CONFIG_FILE` 和 `ASSISTANT_CONFIG` 覆盖。
|
|
35
|
+
|
|
36
|
+
两个私人聊天文件和 `workspaces.json` 不再纳入 Git 跟踪。此次只清理当前文件,不改写已有 Git 历史;历史提交仍可能包含原来的内容。
|
|
37
|
+
|
|
38
|
+
## 启动服务端
|
|
39
|
+
|
|
40
|
+
需要 Python 3.11+ 与已安装、已登录的 `codex` CLI。默认聊天 Provider 使用服务端内置的 Codex 兼容实现,无需额外运行代理。
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd /路径/aizz-server
|
|
44
|
+
python3 -m venv .venv
|
|
45
|
+
source .venv/bin/activate
|
|
46
|
+
pip install -r requirements.txt
|
|
47
|
+
python3 migrate_storage.py # 初始化 ~/.aizz,或迁移旧版私人文件;迁移前手动停止服务端
|
|
48
|
+
python3 -m uvicorn app:app --host 127.0.0.1 --port 8000
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
服务端默认只打印应用错误日志。排查聊天 Agent 或 `/v1` 代理问题时,在**手动启动服务端的同一个终端**先执行 `export ASSISTANT_DEBUG=1`,再运行上面的 Uvicorn 命令;会额外打印模型、轮次、工具名、调用结果状态等调试信息。上线时不设置 `ASSISTANT_DEBUG`(或设为 `0`),仅保留错误日志。配置在进程启动时读取,修改后需要你自行重启服务端。日志不记录请求正文、工具参数、工具输出或 API Key;上游 400 若返回 JSON 错误信息,控制台会打印脱敏后的具体错误。
|
|
52
|
+
|
|
53
|
+
在左上角会话列表底部打开“设置”,可以切换聊天 Provider,选择保存到服务端的 `~/.aizz/config.json`。Provider 地址与密钥仅在后端配置。聊天模型从后端 JSON 中各 Provider 的 `models` 数组读取,不再请求上游 `/models`;模型 ID 必须与对应服务接受的名称一致。数组顺序就是 APP 展示顺序,第一个模型作为尚未选择模型的会话默认值。Codex 模型仍通过 CLI 的 `model/list` 获取。输入框上方只显示“模型名 思考级别”,点击后从底部弹出设置面板,每行一个参数并带图标;聊天和 Codex 可以分别选择模型。Codex 面板包含模型、思考级别、Codex 模式、Fast、Plan 和 Workdir,修改即时生效于下一条任务。思考档位默认 Medium,也可选 Low、High、XHigh、Ultra。Ultra 在请求中映射为 `max`。Provider 或模型不支持所选档位时,上游错误会显示在回复气泡中。
|
|
54
|
+
|
|
55
|
+
聊天模型在 `~/.aizz/config.json` 中按 Provider 分别配置,例如(请替换为该服务实际支持的模型 ID):
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"active_provider": "local",
|
|
60
|
+
"providers": {
|
|
61
|
+
"local": {
|
|
62
|
+
"type": "codex",
|
|
63
|
+
"models": ["gpt-6-luna", "gpt-6.1-sol", "gpt-5.6-terra"]
|
|
64
|
+
},
|
|
65
|
+
"openai": {
|
|
66
|
+
"base_url": "https://api.openai.com/v1",
|
|
67
|
+
"api_key_env": "OPENAI_API_KEY",
|
|
68
|
+
"models": ["your-openai-model-id"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Provider 的 `type` 为 `codex` 时直接使用内置实现,不需要 `base_url` 或 API Key;省略 `type` 或设为 `openai` 时,继续使用外部 OpenAI 兼容服务。已有 `config.json` 不会自动迁移:将原 `local` 项替换为上述 `type` 和 `models` 配置即可,其他 Provider 保留原配置。
|
|
75
|
+
|
|
76
|
+
每个 Provider 的 `models` 必须是非空字符串数组;重复名称只显示一次。修改配置后,在 APP 模型选择面板刷新即可重新读取,无需重启服务端。发送消息时也会按当前配置校验模型;已有会话保存的模型不会因调整数组顺序而改变。旧配置需要补充 `models`,缺失或为空时会提示具体配置位置。
|
|
77
|
+
|
|
78
|
+
会话会自动保存当前“聊天 / Codex”模式,并分别记住两种模式的模型和思考级别。Codex 的权限模式、Fast、Plan、Workdir 也随会话保存在 SQLite 中;切换会话、重新打开 APP 或临时切换到聊天模式都不会重置。设置在修改时保存,快速连续修改按顺序写入。旧会话首次打开时根据最后一条用户消息恢复模式;编辑消息产生的新分支继承原会话设置,并切到被编辑消息的模式。已选模型或目录即使暂时不在可选列表中也会保留,需要用户主动选择其他项。
|
|
79
|
+
|
|
80
|
+
Codex 任务弹窗顶部显示当前本机 Codex 登录账号的剩余额度与重置时间,打开弹窗自动读取,也可手动刷新。5h、Weekly 等窗口按接口返回展示,时间转换为 APP 本地时间;读取失败或当前账号不提供额度信息时显示提示。额度查询使用 `account/rateLimits/read`,不创建线程或发送模型任务。
|
|
81
|
+
|
|
82
|
+
代码任务使用本机 `codex app-server`(stdio 协议)。Codex 模式提供原生权限组合:
|
|
83
|
+
|
|
84
|
+
| 模式 | 行为 |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| 默认权限 | `workspace-write` + `on-request`,工作目录内读写,额外权限由用户审核 |
|
|
87
|
+
| 自动审核 | 同样的沙箱边界,`approvalsReviewer=auto_review`,额外权限由 Codex 原生审核器处理 |
|
|
88
|
+
| Full Access | `danger-full-access` + `never`,完整文件与网络访问,无操作审核 |
|
|
89
|
+
| 只读 | `read-only` + `on-request`,只读沙箱,额外权限由用户审核 |
|
|
90
|
+
|
|
91
|
+
客户端初始选择“默认权限”;旧客户端只发送 `sandbox` 字段时仍保留原来的只读/读写行为。APP 显示请求原因、命令或文件修改详情,可批准本次、本次任务内批准、拒绝或中断任务。权限请求仅授予本次 turn。正常沙箱内操作不会逐条弹审核。此处审核指操作权限审批,不是代码 Review。
|
|
92
|
+
|
|
93
|
+
Fast 开启时使用 Codex 原生 `serviceTier=fast`,关闭时明确使用 `default`;是否支持取决于所选模型、账号和 Provider。Plan 使用原生 `collaborationMode`,选中的模型和思考级别也随模式一起传入;需要时可在 APP 中选择或输入问题答案并继续任务。Plan 关闭时回到原生默认协作模式。回复按文本增量实时显示。每条 Codex 回复上方提供可展开的执行过程和耗时,执行中默认展开,完成后自动收起;过程说明、命令输出、文件差异、工具调用和计划随消息保存,命令与文件详情可以单独展开、复制。最终回复始终显示在过程区下方。
|
|
94
|
+
|
|
95
|
+
Codex 设置弹窗支持勾选 Worktree,默认关闭,开关随会话保存。开启后,首次发送任务会从所选 Workdir 所在 Git 仓库的 HEAD 创建 `aizz/<随机 ID>` 分支和独立目录(默认位于服务端 `~/.aizz/worktrees/`),Codex 在该目录执行;同一会话、同一项目后续任务复用该目录,弹窗展示分支和路径,执行过程也会记录实际目录。所选目录为仓库子目录时保留相对位置。项目必须已有提交,未提交和未跟踪文件不会复制。切换 Worktree 开关或项目目录会在下次执行时重建 Codex 线程并携带已有聊天上下文。关闭后回到原项目;关闭开关、删除会话或结束临时会话均保留 Git 分支和目录,代码不会自动合并或清理。已绑定目录丢失时会报错,不会自动创建新目录覆盖绑定。
|
|
96
|
+
|
|
97
|
+
工作目录单独配置在 `~/.aizz/workspaces.json`:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"default": "~/code",
|
|
102
|
+
"directories": ["/opt/code", "~/code/cx/cx_app"]
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`default` 自动加入可选列表;`~` 指服务端用户的主目录,相对路径基于该配置文件所在目录,与服务启动目录无关。新安装默认值为 `~`,可直接改成上述路径;旧配置迁移时会将相对路径按旧位置转换为绝对路径,保持目录选择不变。目录必须已存在,APP 不会自动创建目录。打开任务设置面板、刷新 Workdir 或切换到 Codex 会重新读取文件,无需重启服务;删除或变更后的旧选项在发送时也会被服务端校验。可通过 `ASSISTANT_WORKSPACES` 环境变量指定另一个配置文件。
|
|
107
|
+
|
|
108
|
+
设置页的「Worktree / 项目目录」支持添加和移除项目:填写服务器上已有目录的绝对路径(支持 `~`),修改会立即保存到工作目录配置,无需手动编辑文件或重启服务。移除只影响可选列表,保留服务器文件和 Git worktree;至少保留一个项目,移除默认项目时自动选择剩余项目。返回聊天页会刷新列表,已移除的当前选项会切换到可用项目。这与任务设置中创建独立 Git 工作副本的 Worktree 开关分别管理。
|
|
109
|
+
|
|
110
|
+
设置页也可以切换聊天 Provider;Provider 的地址、API Key 或环境变量名统一在 `~/.aizz/config.json` 中配置。Codex 模型列表通过本机 CLI 的 `model/list` 获取,默认模型优先展示,支持刷新,与聊天 Provider 无关;旧配置中的 `codex.provider` 不再使用。Codex 执行继续使用 CLI 自身的配置与登录。每个会话绑定一个持久 Codex 线程,后续任务恢复该线程;可在任务设置中查看或清除绑定,编辑消息创建的分支使用新线程。会话文本存于 `~/.aizz/assistant.db`。执行记录随回复保存在数据库,重新打开会话仍可查看;旧消息没有保存的执行记录不会补齐。普通会话的待审核请求跟随服务端任务,断开手机连接后仍可恢复;上游空闲超过 `codex.timeout_seconds`(默认 600 秒)会结束任务,等待审批或回答单独允许 30 分钟。运行服务端时使用单 worker,以便审核请求回到持有该任务的进程。配置与数据库中的敏感信息不会提交到版本控制。
|
|
111
|
+
|
|
112
|
+
可在 `~/.aizz/config.json` 的 `codex.homes` 中配置多个本机 Codex Home,然后在会话的 Codex 弹窗中选择:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"codex": {
|
|
117
|
+
"timeout_seconds": 600,
|
|
118
|
+
"homes": {
|
|
119
|
+
"CODEX1": "~/.codex1",
|
|
120
|
+
"CODEX2": "~/.codex2"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
将上述字段合并到现有配置,保留其他配置项。名称用于弹窗显示,`default` 为保留名称;目录必须事先存在,支持绝对路径或 `~/` 路径。各 Home 需要事先分别登录,例如手动执行 `CODEX_HOME=~/.codex1 codex login`。修改配置后重新打开弹窗即可刷新选项。
|
|
127
|
+
|
|
128
|
+
“默认”沿用服务端 `CODEX_HOME`(未设置时使用 `~/.codex`)。选择按会话保存,仅影响代码执行、Codex 模型列表和账号额度,不影响普通聊天代理或图片生成,也不会自动轮换账号。每轮子进程单独设置环境,不会改变其他会话的账号。
|
|
129
|
+
|
|
130
|
+
切换 Home 会清除本会话的线程绑定,下轮创建新线程并带入可见对话记录,原生工具上下文不会迁移;旧 Home 的历史文件保留。运行中的会话不能切换。已配置名称被移除或目录失效时会报错,需重新选择;同名配置指向新路径后也会创建新线程。
|
|
131
|
+
|
|
132
|
+
接口:`GET /api/codex/homes` 返回可选项;模型与额度接口接受 `codex_home` 查询参数;会话偏好及代码生成请求使用 `codex_home` 标识(省略生成请求字段时使用会话偏好)。
|
|
133
|
+
|
|
134
|
+
协议以本机 Codex CLI 0.162.1 生成的 schema 校验,参考 [Codex App Server 官方文档](https://developers.openai.com/codex/app-server)。当前支持命令、文件修改、权限审批和工具主动提问;MCP 表单等其他交互会明确返回不支持,不会自动批准。Plan 使用协议的实验性字段,初始化时已显式启用 `experimentalApi`。权限组合参考 [Agent approvals & security](https://developers.openai.com/codex/agent-approvals-security),Fast 参考 [Speed](https://developers.openai.com/codex/speed)。
|
|
135
|
+
|
|
136
|
+
离线验证(不会启动 APP、HTTP 服务或真实 Codex 任务):
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
.venv/bin/python -m unittest discover -s tests -t . -p 'test_*.py' -v
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
在 `server/` 目录下运行单个测试模块:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
.venv/bin/python -m unittest tests.test_codex_quota -v
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
从项目根目录 `aizz/` 也可以直接运行,测试包会自动加入服务端模块搜索路径:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
server/.venv/bin/python -m server.tests.test_storage
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
IDE 中请使用模块运行方式:工作目录为 `aizz/` 时,模块名为 `server.tests.test_storage`;工作目录为 `server/` 时,模块名为 `tests.test_storage`。
|
|
155
|
+
|
|
156
|
+
## 内置 OpenAI 兼容接口
|
|
157
|
+
|
|
158
|
+
现有服务端同时提供 `GET /v1/models` 和 `POST /v1/chat/completions`,固定调用内置 Codex,不受 APP 当前 Provider 选择影响。支持普通 JSON 和 SSE 流式输出、usage、用户图片、function 工具调用与结构化输出;每轮传完整消息历史,接口本身不创建 APP 会话或保存聊天记录。本次不提供 `/v1/responses`。
|
|
159
|
+
|
|
160
|
+
外部调用需要服务端启动前设置 `CODEX_PROXY_API_KEY`,未设置时 `/v1` 返回 503,密钥错误返回 401。APP 使用内置 Provider 无需这个密钥,仍由设备授权保护。独立 API Key 不能访问 `/api`;设备凭证也不能代替独立 API Key。支持 HTTP 与 HTTPS;HTTP 明文传输 API Key 和内容,公网使用应配置 HTTPS。
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# 在你手动启动服务端的终端中设置;使用自己生成的密钥
|
|
164
|
+
export CODEX_PROXY_API_KEY='<你的独立密钥>'
|
|
165
|
+
# 服务端由你按上文命令启动后,在另一个终端调用
|
|
166
|
+
OPENAI_API_KEY='<你的独立密钥>' ./tests/test_openai.sh '你好'
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
OpenAI SDK 使用 `base_url="http://127.0.0.1:8000/v1"`、`api_key="<你的独立密钥>"`。真机或远程客户端改用可达的服务器地址,建议使用 HTTPS。非流式请求返回 Chat Completions JSON;`stream=true` 返回 SSE,最后是 `data: [DONE]`,可设置 `stream_options.include_usage=true`。流开始前的错误使用对应 HTTP 状态;流开始后的错误使用 `data: {"error": ...}`,随后发送 `[DONE]`,客户端应检查错误事件。
|
|
170
|
+
|
|
171
|
+
内置实现的环境变量在服务端启动时读取,修改后需自行重启:
|
|
172
|
+
|
|
173
|
+
| 配置 | 默认值 / 行为 |
|
|
174
|
+
| --- | --- |
|
|
175
|
+
| `CODE_HOME` / `CODEX_HOME` | 按此优先级查找 `auth.json`;均未设置时使用 `~/.codex` |
|
|
176
|
+
| `CODEX_PROXY_API_KEY` | 独立访问密钥;未设置则关闭外部兼容接口 |
|
|
177
|
+
| `CODEX_PROXY_MODEL` | `gpt-6-luna`,外部请求省略 model 时使用 |
|
|
178
|
+
| `CODEX_PROXY_MODELS` | 逗号分隔;默认 `gpt-6-luna,gpt-6.1-sol,gpt-5.6-terra`,仅用于 `/v1/models` 展示,不限制请求模型 |
|
|
179
|
+
| `CODEX_PROXY_TIMEOUT` | 120 秒,上游连接及相邻数据读取超时 |
|
|
180
|
+
| `CODEX_PROXY_CODEX_BIN` | `codex`,认证刷新所用 CLI |
|
|
181
|
+
| `CODEX_PROXY_REFRESH_MODEL` | `gpt-6-luna` |
|
|
182
|
+
| `CODEX_PROXY_REFRESH_TIMEOUT` | 120 秒,认证刷新任务超时 |
|
|
183
|
+
|
|
184
|
+
APP 模型列表仍读取各 Provider 的 `models`,与 `/v1/models` 展示列表分别配置。旧代理的 HOST、PORT 变量不再使用,监听地址由现有 Uvicorn 命令决定。
|
|
185
|
+
|
|
186
|
+
上游使用原代理的 Codex 非公开 Responses 地址。认证失败时检查最新登录文件,通过 CLI 最小任务刷新并最多重试一次;同一失败凭证冷却 30 秒。刷新任务不接收业务消息,不直接修改认证文件。无法刷新时需要自行登录 Codex。它与公开 OpenAI API 的参数支持范围可能不同;不支持的普通参数沿用原代理行为忽略,并仅记录参数名。客户端断开或 APP 停止生成时释放上游连接。
|
|
187
|
+
|
|
188
|
+
## 通用安装与扫码连接
|
|
189
|
+
|
|
190
|
+
服务端支持 macOS/Linux、Python 3.11+;Windows 请在 WSL 中安装。电脑需安装并登录 Codex CLI,确保 `codex` 在 PATH 中。APP 不再内置服务器域名,也不需要通过 `API_BASE_URL` 构建;同一份 APP 可以连接不同用户的服务器,当前只保存一个连接。
|
|
191
|
+
|
|
192
|
+
本项目尚未发布 PyPI。下载本次 wheel 后可通过 pipx 安装,或在虚拟环境中安装:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
python3 -m venv ~/.venvs/aizz
|
|
196
|
+
source ~/.venvs/aizz/bin/activate
|
|
197
|
+
python -m pip install /路径/aizz_server-1.0.0-py3-none-any.whl
|
|
198
|
+
aizz serve
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
源码安装在本仓库根目录执行 `python -m pip install .`,开发环境可以使用 `python -m pip install -e .`。`aizz serve` 首次启动会自动初始化;也可以提前执行 `aizz init` 检查配置。初始化只创建缺失文件,已有的 `~/.aizz/` 配置、设备、数据库和聊天记录不会被覆盖;`ASSISTANT_CONFIG`、`ASSISTANT_WORKSPACES`、`ASSISTANT_DB` 仍有效。私人目录权限为 `700`,新配置及数据库为 `600`。
|
|
202
|
+
|
|
203
|
+
安装后由你手动执行以下命令启动服务端,无需先执行 `aizz init`。默认配置使用已登录的 Codex 和用户主目录作为 Workdir,可在 `~/.aizz/config.json` 和 `~/.aizz/workspaces.json` 中调整:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
aizz serve # 默认 0.0.0.0:8000,单 worker,允许同一内网设备连接
|
|
207
|
+
# 仅本机访问或同机 HTTPS 反代时:
|
|
208
|
+
# aizz serve --host 127.0.0.1
|
|
209
|
+
# 代理位于其他机器时,指定实际可信的代理 IP;不要信任任意来源
|
|
210
|
+
# aizz serve --host <内网监听地址> --forwarded-allow-ips <实际代理IP>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
内网可直接使用 `http://<电脑内网IP>:8000`,无需证书或额外开关;支持任意 HTTP 和 HTTPS 源地址(可含端口),不支持 `/assistant` 等路径前缀。手机必须能访问该地址,防火墙需允许对应端口;`0.0.0.0` 是监听地址,不是手机填写的服务器地址。
|
|
214
|
+
|
|
215
|
+
APP 每次配对 HTTP 服务器前会提示:HTTP 明文传输配对码、设备 token、聊天、图片和终端内容,可能被监听或篡改,token 被窃取后可冒用设备读取文件和执行命令。取消不发送配对请求,确认后继续;成功连接后的自动重连不重复提示。二维码不会提供网络穿透。
|
|
216
|
+
|
|
217
|
+
HTTPS 由你自行配置反向代理,需转发协议、客户端地址及 `/api/terminal` WebSocket 升级。证书必须受设备信任,不支持关闭证书校验。只允许 HTTPS 访问的部署,应绑定回环地址或通过防火墙阻止外部直连 HTTP 后端。
|
|
218
|
+
|
|
219
|
+
在**服务端电脑**另一个终端使用相同系统用户及 `ASSISTANT_DB`:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
aizz pair --url http://192.168.1.10:8000 # 替换为电脑实际内网 IP
|
|
223
|
+
# 或:aizz pair --url https://你的服务器地址
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
终端会显示二维码、服务地址及 6 位一次性数字码。打开 APP →「扫描配对二维码」→ 核对服务器地址和设备名称 →「确认服务器并连接」。点击扫码后才请求相机权限;拒绝权限、无摄像头或使用模拟器时,可选择「手动输入地址和授权码」。二维码只包含版本标识、服务器地址和一次性码,不包含长期登录凭证;不要截图分享。
|
|
227
|
+
|
|
228
|
+
二维码五分钟有效,只能授权一台设备,重新生成使旧码失效。每台手机或 iPad 分别生成一个码,可同时连接同一服务器并共享聊天记录。首次配对使用现有 `POST /api/auth/pair`,没有远程生成授权码的接口。单次兑换、并发最多成功一次、每码最多 10 次尝试及全局每分钟 10 次/每小时 60 次限制继续有效,重启或生成新码不重置限流。
|
|
229
|
+
|
|
230
|
+
连接成功后,地址和设备独立的 256 位随机凭证作为一份记录存入系统安全存储,重启 APP 自动恢复。iOS 使用仅本设备的 Keychain,Android 使用加密存储并关闭备份;服务器只保存凭证摘要。网络故障保留连接,401 或设备撤销返回配对页。旧版本升级首次需要重新扫码,不根据旧固定域名寻找凭证;原服务器的旧设备授权可用下面的命令清理。
|
|
231
|
+
|
|
232
|
+
「登录设备」可管理授权,或点击「更换服务器」。更换时先撤销旧授权,清除本机连接、草稿、图片缓存和通知状态,再扫码连接新电脑;服务器聊天记录保留。离线时可明确选择仅清除本机,之后需要在原电脑撤销旧授权。
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
aizz devices list
|
|
236
|
+
aizz devices revoke --id <设备ID>
|
|
237
|
+
aizz devices revoke --all
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
设备凭证持续有效直至退出或撤销;撤销会阻止后续请求,进行中的连接和任务沿用现有取消机制。所有已授权设备仍属于同一个私人助手,具有相同管理权限。
|
|
241
|
+
|
|
242
|
+
源码运行方式和 `device_auth.py code|list|revoke` 管理入口继续可用。旧数据迁移仍须你先手动停止服务端,可使用原迁移脚本,或执行:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
aizz migrate --from /旧项目/server
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
自建服务器无需 APNs 配置即可聊天和编码;iPhone 任务通知仍需服务器自行配置 APNs,应用的 APNs 私钥不会包含在安装包中。完成本功能不等于完成应用商店审核,审核演示服务器及材料需另行准备。
|
|
249
|
+
|
|
250
|
+
将来发布到 PyPI 后,用户可用 `python -m pip install aizz-server` 安装,再运行 `aizz serve`。当前尚未发布,不能把这个命令当作已可用的安装渠道。Python 包不包含 Python 运行时或 Codex CLI,仍需 Python 3.11+ 及已安装、已登录且位于 PATH 的 Codex CLI。brew 和 curl 安装暂未提供。
|
|
251
|
+
|
|
252
|
+
### 打包与发布到 PyPI
|
|
253
|
+
|
|
254
|
+
以下步骤由维护者手动执行,不会自动启动 APP 或服务端。发行名为 `aizz-server`,安装后的命令为 `aizz`。本节提供发布操作说明,不代表已经上传;是否发布成功以 PyPI 项目页面为准。
|
|
255
|
+
|
|
256
|
+
#### 1. 首次发布前准备账号
|
|
257
|
+
|
|
258
|
+
1. 登录 [PyPI](https://pypi.org/),在[账号设置](https://pypi.org/manage/account/)验证邮箱并开启双因素认证(2FA),保存恢复码。
|
|
259
|
+
2. 在 **API tokens → Add API token** 创建令牌。首次创建项目时,Scope 选择 **Entire account**;已有项目后,使用仅限 `aizz-server` 的项目令牌。
|
|
260
|
+
3. 保存完整的 `pypi-...` token。它用于上传时的密码,不是 PyPI 登录密码;不要写入代码、README、聊天或提交到 Git。
|
|
261
|
+
4. 新项目名必须可用;若 `aizz-server` 已被其他人占用且你没有发布权限,需要修改 `pyproject.toml` 中的包名并重新打包,相应安装命令和产物名也要更新。
|
|
262
|
+
|
|
263
|
+
首次发布成功后,可以创建项目专用 token,再撤销首次使用的全账号 token。参见 [PyPI 官方发布说明](https://docs.pypi.org/publishing/publishing-to-pypi/)。
|
|
264
|
+
|
|
265
|
+
#### 2. 确认版本并打包
|
|
266
|
+
|
|
267
|
+
首次发布当前版本为 `1.0.0`。以后每次更新,都要先将以下两处版本号改成相同的新版本(例如 `1.0.1`):
|
|
268
|
+
|
|
269
|
+
- `pyproject.toml` 的 `[project].version`。
|
|
270
|
+
- `aizz_server/__init__.py` 的 `__version__`。
|
|
271
|
+
|
|
272
|
+
在项目根目录进入 `server/`;如果已经位于服务端仓库根目录,跳过 `cd server`:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
cd server
|
|
276
|
+
source .venv/bin/activate
|
|
277
|
+
python -m pip install --upgrade build twine \
|
|
278
|
+
--index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple \
|
|
279
|
+
--timeout 120 --retries 10 --resume-retries 10
|
|
280
|
+
|
|
281
|
+
# 每次发布都设为本次版本,必须与上述两处版本号一致。
|
|
282
|
+
AIZZ_RELEASE_VERSION=1.0.0
|
|
283
|
+
python -m build --outdir "dist/$AIZZ_RELEASE_VERSION"
|
|
284
|
+
|
|
285
|
+
python -m twine check --strict \
|
|
286
|
+
"dist/$AIZZ_RELEASE_VERSION/aizz_server-$AIZZ_RELEASE_VERSION-py3-none-any.whl" \
|
|
287
|
+
"dist/$AIZZ_RELEASE_VERSION/aizz_server-$AIZZ_RELEASE_VERSION.tar.gz"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
上面的依赖安装命令使用[清华 PyPI 镜像](https://mirror.tuna.tsinghua.edu.cn/help/pypi/),仅对本次安装生效,不修改全局 pip 配置。镜像不可用时,将 `--index-url` 的地址替换为 `https://pypi.org/simple`。`twine upload` 仍向官方 PyPI 发布,不向镜像上传。
|
|
291
|
+
|
|
292
|
+
尚未创建虚拟环境时,先执行 `python3 -m venv .venv`。这里按版本存放产物,上传时只指定本次的两个文件,避免 `dist/*` 把旧版本一起上传。早先直接执行 `python -m build` 生成的文件位于 `dist/`,不会自动移入版本子目录。
|
|
293
|
+
|
|
294
|
+
上传前检查两个归档中的文件清单和内容:应包含服务端代码、通用配置示例和说明;源码包还可能包含测试。不要包含私人配置、数据库、Codex `auth.json`、真实 API Key 或 APNs 私钥。wheel 中的 Python 源码也可以被下载和查看,打包不能隐藏实现。
|
|
295
|
+
|
|
296
|
+
#### 3. 正式上传
|
|
297
|
+
|
|
298
|
+
确认包名、版本、内容和 `twine check` 结果后,在同一个终端执行。**以下命令会正式公开发布到 PyPI**:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
python -m twine upload --username __token__ \
|
|
302
|
+
"dist/$AIZZ_RELEASE_VERSION/aizz_server-$AIZZ_RELEASE_VERSION-py3-none-any.whl" \
|
|
303
|
+
"dist/$AIZZ_RELEASE_VERSION/aizz_server-$AIZZ_RELEASE_VERSION.tar.gz"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
提示输入密码时,粘贴完整的 `pypi-...` token。终端不显示输入字符是正常现象;不要把 token 直接写进命令行。首次成功上传会创建 PyPI 项目,无需先手动建立项目。
|
|
307
|
+
|
|
308
|
+
#### 4. 确认发布与用户安装
|
|
309
|
+
|
|
310
|
+
上传成功后,打开 [aizz-server 项目页面](https://pypi.org/project/aizz-server/),核对版本及两个发行文件。用户可以在 Python 3.11+ 虚拟环境中执行:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
python -m pip install --upgrade aizz-server
|
|
314
|
+
aizz --version
|
|
315
|
+
# 由用户手动执行;需要事先安装并登录 Codex CLI:
|
|
316
|
+
aizz serve
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
需要验证指定版本时,可以执行 `python -m pip install aizz-server==1.0.0`(替换为本次发布版本)。
|
|
320
|
+
|
|
321
|
+
#### 后续更新与常见问题
|
|
322
|
+
|
|
323
|
+
- **安装 build / twine 时下载超时或 incomplete-download**:这是依赖下载中断,尚未进入上传步骤。在已激活的虚拟环境中执行 `python -m pip install --upgrade build twine --index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple --timeout 120 --retries 10 --resume-retries 10`,增加超时和下载续传重试次数;安装成功后继续打包、检查与上传。pip 的版本更新通知不是此次失败原因。
|
|
324
|
+
- **更新版本**:修改上述两处版本号,更新 `AIZZ_RELEASE_VERSION`,重新打包、检查并上传;不重复上传旧版本。
|
|
325
|
+
- **403 / 无发布权限**:检查 token 是否来自正式 PyPI、是否完整、是否有该项目权限;首次创建项目不能使用另一个项目的专用 token。
|
|
326
|
+
- **文件已存在**:PyPI 不允许覆盖已上传文件,也不能删除后复用同一发行文件名。正常更新应增加版本号并重新打包。如果只有一个文件上传成功,只补传缺失的那个文件。
|
|
327
|
+
- **发现发布错误**:可以在 PyPI 的项目管理页面将该版本标记为 Yank,再发布修复版本。Yank 后 pip 通常避开该版本,但明确指定版本时仍可能安装。
|
|
328
|
+
- **删除或撤回的限制**:删除不可恢复,无法收回别人已经下载的副本。若误上传真实密钥,应立即撤销或轮换密钥,删除包本身不能解决泄露。
|
|
329
|
+
|
|
330
|
+
### 安装与连接验收
|
|
331
|
+
|
|
332
|
+
1. 在独立虚拟环境安装 wheel,从其他目录运行 `aizz --help` 和 `aizz --version`;由用户手动执行 `aizz serve`,确认自动创建私人配置。
|
|
333
|
+
2. 修改配置并完成设备配对后,手动重启,确认配置、设备和聊天保留;`--host 127.0.0.1` 可覆盖默认监听。
|
|
334
|
+
3. 生成 HTTP 地址的配对码,在真机扫码或手动输入;先取消风险提示,确认未消耗授权码,再确认连接。
|
|
335
|
+
4. 验证聊天、图片和终端,重开 APP 自动重连不再提示;撤销设备后失去访问权限。
|
|
336
|
+
5. 换用受信任的 HTTPS 入口配对,不显示 HTTP 提示,聊天和终端正常;不受信任的证书连接失败。
|
|
337
|
+
|
|
338
|
+
## 聊天工具与命令权限
|
|
339
|
+
|
|
340
|
+
普通聊天每次用户请求最多进行 60 轮模型请求、120 次工具调用,执行预算为 1200 秒(20 分钟),等待审批暂停计时。失败调用也计数,最终回复占用一轮模型请求。单次工具参数最多 32000 字符;超长参数返回工具错误,模型可拆分后重试。单条命令仍最多执行 30 秒、4000 字符。
|
|
341
|
+
|
|
342
|
+
`edit_file(path, old_text, new_text)` 精确替换已有 UTF-8 文件中的唯一匹配文本;先读取文件,`old_text` 必须非空,`new_text` 可为空以删除文本。工具仅能修改当前 Workdir 内的普通文件,不能写入目录外的 skill 资源,符号链接也会校验真实目标。保留文件权限与换行格式,原子写入;文件变化或匹配不唯一时返回错误,成功返回有长度限制的 diff 并保存到执行记录。
|
|
343
|
+
|
|
344
|
+
在 `tools.allow` 中加入 `edit_file` 后直接编辑,无需逐次审批;已有策略不会自动获得该权限。
|
|
345
|
+
|
|
346
|
+
手动编辑 `~/.aizz/tool_policy.json` 即可配置普通聊天的权限。每次向模型发送工具定义、接收工具调用和执行命令时都会重新读取,无需重启。只影响普通聊天工具,不改变 Codex 模式或独立 `/v1` 接口的权限。
|
|
347
|
+
|
|
348
|
+
图片生成可分别配置 GPT 和 Gemini 服务。`imagegen` 使用 `image_generation.default` 指定的默认服务;聊天时说“用 Gemini 生图”或“用 GPT 生图”会调用对应的 `imagegen_gemini`、`imagegen_gpt`。配置示例见 `aizz_server/config.example.json`。Gemini 使用 OpenAI 兼容的 `/v1/chat/completions`,可通过 `image_generation.gemini.provider` 复用已配置聊天 Provider 的地址和密钥;旧的单层 `image_generation` 配置仍作为 GPT 服务使用。现有 `tool_policy.json` 只允许 `imagegen` 时,也会允许这两个对应的生图工具;如果需要分别控制,可只列出特定工具名称。
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"tools": {
|
|
353
|
+
"allow": ["list_files", "read_file", "search_files", "edit_file", "run_command", "web_search", "imagegen", "imagegen_gpt", "imagegen_gemini"]
|
|
354
|
+
},
|
|
355
|
+
"shell": {
|
|
356
|
+
"allow": [["pwd"], ["ls"], ["cat"], ["git", "status"], ["git", "log"]],
|
|
357
|
+
"deny": [["sudo"], ["rm"]],
|
|
358
|
+
"ask": [["git", "add"], ["git", "commit"], ["git", "push"], ["git", "reset"], ["git", "clean"]],
|
|
359
|
+
"default": "ask"
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
- `tools.allow`:允许调用的工具名称,不在列表中的工具禁止调用;设为 `[]` 禁用全部工具。移除 `run_command` 即可禁用整个命令工具。
|
|
365
|
+
- `shell.allow`:免审核执行的命令参数前缀。例如 `["git", "log"]` 匹配 `git log -10 --oneline`,不会匹配 `git logger`;后续参数均在授权范围内。
|
|
366
|
+
- `shell.deny`:禁止执行的命令前缀,不会弹出审核。`shell.ask`:每次都需要用户批准的命令前缀。
|
|
367
|
+
- 优先级为 `deny` > `ask` > `allow` > `default`。`default` 支持 `ask`(未匹配时审核)或 `deny`(未匹配时禁止)。
|
|
368
|
+
- 参数按命令行引号拆分,不支持通配符规则。免审核规则按实际输入的程序名或路径匹配;若要放行 `/usr/bin/git log`,需单独添加 `["/usr/bin/git", "log"]`。禁止和审核规则还会按程序文件名匹配,因此 `["rm"]` 也禁止 `/bin/rm`。
|
|
369
|
+
|
|
370
|
+
命令通过参数数组直接启动,只支持单条命令;不经过 shell,不展开 `~`、通配符或环境变量。`&&`、管道、重定向、命令替换和环境变量赋值会被拒绝,请拆成独立调用;带引号或转义的普通文件名支持空格和标点。规则只匹配直接调用的程序与参数,不分析 `python -c`、`bash -c` 等程序内部的脚本,也不限制进程能访问的路径,因此放行解释器或脚本应按其完整能力理解。
|
|
371
|
+
|
|
372
|
+
JSON 格式、字段或工具名称错误时明确报错并阻止工具执行。审核等待期间若将命令或工具禁用,获批后仍会被拦截。旧安装缺少此文件时保留原有规则(全部现有工具可用;`pwd`、简单 `ls`、`git status` 免审核,其余单条命令审核);运行迁移初始化命令可以从 `tool_policy.example.json` 创建新配置,不覆盖已有文件。
|
|
373
|
+
|
|
374
|
+
普通聊天的命令审核支持“批准本次 / 会话允许 / 永久允许 / 拒绝”。会话允许在当前聊天中跨轮次和服务端重启保留,删除聊天时清除,编辑产生的新会话不继承。永久允许供同一项目的其他会话复用,删除原聊天不影响它。两种授权均按完整命令文本、项目 Workdir 和实际执行目录的规范化绝对路径精确匹配,不匹配命令前缀;参数、空格或目录变化需重新审核。授权保存在上述 SQLite 数据库的 `chat_command_session_grants`、`chat_command_project_grants` 表中,不修改 `tool_policy.json`。
|
|
375
|
+
|
|
376
|
+
工具禁用、`shell.deny` 和显式 `shell.ask` 始终优先于保存的授权。匹配显式 `ask` 的审核卡片提示“策略要求每次审核”,只提供单次批准和拒绝。临时聊天可复用已有永久授权,只能新增临时会话授权,清除临时聊天或重启后失效。本次未提供授权管理或撤销页面;需撤销时可按会话或项目条件删除对应授权表记录。Codex 审核行为不变。
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
## 普通聊天 Skills
|
|
380
|
+
|
|
381
|
+
普通聊天支持 Codex 格式的本地 skill,统一放在服务端用户的 `~/.aizz/skills/` 下。每轮重新发现名称、描述和路径;模型根据任务匹配 skill,或用户发送 `$project-db` 显式调用,然后按需读取完整说明、参考资料和脚本。新增、修改或删除 skill 后,下一条消息生效,无需重启。此功能也适用于以 Codex 为模型 Provider 的普通聊天;Codex 模式仍使用 CLI 原生 skill 目录,不自动同步。
|
|
382
|
+
|
|
383
|
+
已通过 `read_file` 成功读取的 `SKILL.md` 片段会缓存到当前会话,后续消息自动带入上下文,无需重复调用工具;服务端重启后仍保留。文件变更时自动刷新已缓存片段,文件移除或 `read_file` 被禁用后停止注入。分页和截断限制保持不变,未读部分及 references 等资料仍按需读取。缓存按会话隔离,删除会话时清理,重新生成历史回复时重置;缓存正文会占用后续请求的输入 token。
|
|
384
|
+
|
|
385
|
+
`aizz init` 会创建缺失的 skills 目录;已有安装也可以手动创建。支持分类子目录和指向完整 skill 目录的符号链接,循环链接不会重复扫描。每个 skill 目录的标准结构如下,只有 `SKILL.md` 必需:
|
|
386
|
+
|
|
387
|
+
```text
|
|
388
|
+
~/.aizz/skills/project-db/
|
|
389
|
+
├── SKILL.md
|
|
390
|
+
├── scripts/query.py
|
|
391
|
+
├── references/schema.md
|
|
392
|
+
├── assets/
|
|
393
|
+
└── agents/openai.yaml
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
`SKILL.md` 示例:
|
|
397
|
+
|
|
398
|
+
```markdown
|
|
399
|
+
---
|
|
400
|
+
name: project-db
|
|
401
|
+
description: >-
|
|
402
|
+
查询项目数据库、理解订单与客户表的字段及关系时使用。
|
|
403
|
+
不用于修改数据库。
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
先读取 references/schema.md,确认表关系和统计口径。
|
|
407
|
+
所有相对路径以本 skill 目录为基准,文件工具使用完整路径。
|
|
408
|
+
查询前检查 scripts/query.py 的参数说明,再通过 run_command 调用。
|
|
409
|
+
将 cwd 设为本 skill 目录,使用明确的 Python 解释器路径。
|
|
410
|
+
如果数据库查询脚本尚未实现或凭证缺失,说明缺少的配置,不编造查询结果。
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`references/schema.md` 可维护实际项目的表关系、字段含义和业务口径。YAML frontmatter 支持多行字符串及其他可选元数据,`name` 和 `description` 必须为非空字符串。完整说明和参考资料不会全部注入每次请求,较长文件可用 `read_file` 的 `offset`、`limit` 按字符分页;截断结果提供 `next_offset`。
|
|
414
|
+
|
|
415
|
+
脚本由 skill 作者提供和维护,例如查询脚本可约定 `--help`、`--customer-id` 等参数。工具调用示例(替换为服务器实际目录和解释器):
|
|
416
|
+
|
|
417
|
+
```json
|
|
418
|
+
{
|
|
419
|
+
"command": "/opt/project-db/.venv/bin/python scripts/query.py --customer-id 123",
|
|
420
|
+
"cwd": "/Users/yourname/.aizz/skills/project-db"
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
`run_command.cwd` 可省略,省略时沿用聊天 Workdir;指定时限于当前项目或有效 skill 目录,支持 `~/`。脚本通过现有 `tool_policy.json` 的命令规则放行、审核或禁止,审批展示实际目录;安装 skill 不会自动授予执行权限。常用脚本可在 `shell.allow` 中增加精确前缀:
|
|
425
|
+
|
|
426
|
+
```json
|
|
427
|
+
["/opt/project-db/.venv/bin/python", "scripts/query.py"]
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
命令规则匹配程序和参数,不绑定 cwd;需要严格限定具体脚本时,使用绝对脚本路径的规则和命令。脚本执行继续采用现有 30 秒超时、输出限制和精简环境(`PATH`、`LANG`、`LC_ALL`、`TERM`,另设 `GIT_OPTIONAL_LOCKS=0`),不会自动继承数据库凭证环境变量。脚本应自行读取外部凭证文件,数据库权限、只读连接、超时和行数上限由脚本及数据库落实,依赖由用户安装。
|
|
431
|
+
|
|
432
|
+
可选的 `agents/openai.yaml` 支持调用策略:
|
|
433
|
+
|
|
434
|
+
```yaml
|
|
435
|
+
policy:
|
|
436
|
+
allow_implicit_invocation: false
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
默认 `true`,允许按描述自动选择;`false` 时只有当前消息包含 `$project-db` 才向模型提供该 skill。外观元数据可以保留,目前没有 skill 选择器,也不会自动安装 `dependencies` 中声明的 MCP 工具。同名 skill 用路径区分,显式名称有歧义时先澄清。无效 skill 会给出文件路径及原因,不阻塞其他 skill 或普通聊天。
|
|
440
|
+
|
|
441
|
+
文件工具可读取工作目录及已发现的 skill 目录;skill 资源内部指向目录外的链接会被拒绝。skill 属于用户维护的流程指令,执行仍遵循系统规则和工具权限。skill 内容和被读取的资源会发给当前聊天 Provider,请按实际需要维护内容。
|
|
442
|
+
|
|
443
|
+
## 聊天提示词与长期知识
|
|
444
|
+
|
|
445
|
+
普通聊天每轮发送消息前,会重新读取 `~/.aizz/` 下的两份 UTF-8 文件,并与内置工具规则一起加入模型的系统上下文:
|
|
446
|
+
|
|
447
|
+
- `~/.aizz/CHAT_INSTRUCTIONS.md`:通用提示词,例如语言、回答风格、做事规则。
|
|
448
|
+
- `~/.aizz/CHAT_MEMORY.md`:跨会话共享的知识,例如项目名称、别名、目录和用途。
|
|
449
|
+
|
|
450
|
+
修改文件后,下一轮普通聊天自动生效,无需重启服务;新会话、已有会话和临时聊天都会加载。这些文件不会自动从聊天中更新,也不会写入会话消息记录。文件缺失或内容为空时跳过;权限或编码错误会明确报错。此加载逻辑只用于 APP 的普通聊天,不影响 Codex 模式或独立 `/v1` 兼容接口,也不会自动读取 `AGENTS.md`。
|
|
451
|
+
|
|
452
|
+
例如,将下面内容按实际情况填写后放入 `~/.aizz/CHAT_MEMORY.md`:
|
|
453
|
+
|
|
454
|
+
```markdown
|
|
455
|
+
## 项目:lukeny
|
|
456
|
+
- 别名:填写项目别名
|
|
457
|
+
- 项目目录:填写服务端电脑上的实际路径
|
|
458
|
+
- 用途:填写项目用途
|
|
459
|
+
- 技术栈:填写技术栈
|
|
460
|
+
- 当我提到 lukeny 时,默认指这个项目。
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
文件内容每轮都会发送给当前选择的聊天 Provider,并占用模型上下文。项目目录记录用于帮助模型理解,文件工具的访问范围仍由聊天 Workdir 决定。
|
|
464
|
+
|
|
465
|
+
## 后台任务、通知、全文搜索与草稿
|
|
466
|
+
|
|
467
|
+
普通聊天和 Codex 任务现在由服务端独立执行。锁屏、切换 APP、网络中断、切换会话或关闭 APP 都不会停止已接收的任务;重新打开会话时恢复正文、执行过程和待审批/待回答卡片。每个会话最多一个运行中的任务,不同会话可并行执行。会话列表会显示任务状态。
|
|
468
|
+
|
|
469
|
+
点击停止生成会终止任务;删除会话会先停止任务再清理数据。发起任务的设备退出或被撤销时,其任务也会终止。审批和主动提问最多等待 30 分钟,审批等待不占用普通聊天的执行预算。临时聊天仍只保存在内存中,不启用持久任务、推送或草稿;交互终端仍采用原有连接生命周期。
|
|
470
|
+
|
|
471
|
+
服务端重启不会自动重放命令。未完成任务会保留已经产生的内容并显示“已中断”;重新发送前应先确认之前的操作结果。继续使用单 worker 启动服务端。
|
|
472
|
+
|
|
473
|
+
更新后需要你手动重启服务端;客户端代码有变更时也需手动重新构建。服务端启动时会先创建新增的任务、事件、推送设备与通知队列表,兼容已有会话、图片和执行记录;无需手动执行独立 SQL 文件。事件日志保留 7 天,过期游标使用完整任务快照恢复;最终消息和执行记录继续保留。
|
|
474
|
+
|
|
475
|
+
### iPhone / iPad 任务通知
|
|
476
|
+
|
|
477
|
+
在设置中开启“任务通知”,允许系统通知权限后,发起任务的设备会收到完成、失败、等待审批和等待回答提醒。其他设备仍可打开会话查看并处理;点击通知会进入对应会话,已经处理过的审批以服务端最新状态为准。当前正在查看该会话时不弹前台横幅。通知只包含通用提示,不包含聊天正文、命令或文件内容。
|
|
478
|
+
|
|
479
|
+
通知直接使用 Apple APNs,无需 Firebase。需要完成以下配置:
|
|
480
|
+
|
|
481
|
+
1. 在 Apple Developer 中为 `aizz.dphone.io` 的 App ID 开启 Push Notifications,并更新签名描述文件。
|
|
482
|
+
2. 创建支持 APNs 的 `.p8` 认证密钥,保存到服务端仓库以外的私人目录。
|
|
483
|
+
3. 将配置保存到服务端用户的 `~/.aizz/apns.json`,文件权限设为 `600`。服务端会自动读取此文件,更新后下次查询设置或发送通知时生效:
|
|
484
|
+
|
|
485
|
+
```json
|
|
486
|
+
{
|
|
487
|
+
"key_path": "~/.aizz/AuthKey_YOURKEYID.p8",
|
|
488
|
+
"key_id": "<Apple Key ID>",
|
|
489
|
+
"team_id": "<Apple Team ID>",
|
|
490
|
+
"topic": "aizz.dphone.io"
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
也支持 `APNS_CONFIG` 指定其他配置文件路径。原有环境变量方式继续可用,优先于文件中的对应字段;`apns.env` 不会被服务端自动执行,如仍使用该文件,需要在手动启动服务端的同一个终端执行 `source ~/.aizz/apns.env`:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
export APNS_KEY_PATH="$HOME/.aizz/AuthKey_YOURKEYID.p8"
|
|
498
|
+
export APNS_KEY_ID='<Apple Key ID>'
|
|
499
|
+
export APNS_TEAM_ID='<Apple Team ID>'
|
|
500
|
+
# 可选;默认就是现有 Bundle ID
|
|
501
|
+
export APNS_TOPIC='aizz.dphone.io'
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
`.p8` 密钥和上述私人配置不要提交到仓库,也不要放入客户端。服务器需要能通过 HTTPS/HTTP2 访问 Apple APNs。工程默认 Debug 使用 `development`、Release/Profile 使用 `production` 环境;如使用开发签名构建 Release/Profile,请在 Xcode 的 `APNS_ENVIRONMENT` 构建设置中改成与签名 entitlement 一致的环境。
|
|
505
|
+
|
|
506
|
+
服务端未配置、系统权限未授权、令牌注册或推送失败会在通知设置中提示,不影响任务执行。网络类错误和 APNs 临时错误最多尝试 5 次;无效令牌会停用。关闭通知或撤销设备后不再发送。APNs 的真实投递仍需安装带有效签名的真机 APP 验证,模拟测试和模拟器构建不代表推送已经可用。
|
|
507
|
+
|
|
508
|
+
### 新增 API
|
|
509
|
+
|
|
510
|
+
所有接口继续使用设备 Bearer 凭证鉴权:
|
|
511
|
+
|
|
512
|
+
| 接口 | 用途 |
|
|
513
|
+
| --- | --- |
|
|
514
|
+
| `POST /api/sessions/{session_id}/tasks` | 创建或幂等重试任务;沿用聊天请求字段,并增加 16–128 字符的 `request_id` |
|
|
515
|
+
| `GET /api/sessions/{session_id}/task` | 返回该会话最新任务快照,无任务时返回 `null` |
|
|
516
|
+
| `GET /api/tasks/{task_id}` | 返回任务状态、事件序号、消息关联、已有正文、执行过程和待处理交互 |
|
|
517
|
+
| `GET /api/tasks/{task_id}/events?after={seq}` | 按游标订阅 SSE;事件增加 `seq`,过期游标返回 `snapshot` 事件 |
|
|
518
|
+
| `GET /api/search?q=...&limit=30&offset=0` | 搜索标题与正文,返回 `session_id`、`message_id`、`title`、`snippet` 和时间 |
|
|
519
|
+
| `GET /api/notifications` | 查询当前设备的通知开关、注册、服务端配置和错误状态 |
|
|
520
|
+
| `PUT /api/notifications` | 注册令牌;JSON 字段为 `token`、`environment`(`development`/`production`)、`enabled` |
|
|
521
|
+
| `DELETE /api/notifications` | 注销当前设备的推送配置 |
|
|
522
|
+
|
|
523
|
+
同一设备的同一个 `request_id` 必须对应相同会话和请求正文,否则返回 409。用户消息和幂等记录在同一个 SQLite 事务中保存。已有停止、审批和回答接口继续可用;旧的 `/stream` HTTP 入口也复用持久任务,支持通过 `Idempotency-Key` 请求头指定幂等标识。
|
|
524
|
+
|
|
525
|
+
离线验证(不启动 APP、HTTP 监听服务或真实 Codex 任务):
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
.venv/bin/python -m unittest discover -s tests -t . -p 'test_*.py'
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Git 变更与提交
|
|
532
|
+
|
|
533
|
+
客户端文件列表右上角可切换至 Git 变更视图,勾选文件并填写提交消息后可提交;顶部 Push 按钮单独推送。配置上游分支后,按钮显示相对本地上游跟踪分支尚未推送的提交数(`GET /api/git/status` 的 `ahead` 字段);未配置上游时该字段为 `null`。差异预览比较 HEAD 与当前工作文件;提交包含所选文件的全部当前改动,其他文件原有的暂存内容保持不变。子目录只展示该目录内的变更。
|
|
534
|
+
|
|
535
|
+
接口使用现有设备认证与项目目录配置:`GET /api/git/status`、`GET /api/git/diff?path=...`、`POST /api/git/commit`、`POST /api/git/push`。提交请求携带工作目录、所选路径、消息、列表返回的文件 revisions 和 head,以及 push 布尔值;列表过期时返回 409。Git 文本差异预览不再限制为 500 KB;自动生成提交信息仍限制输入差异大小。
|
|
536
|
+
|
|
537
|
+
推送使用服务器已有 Git 凭证,优先使用分支配置的 upstream;尚未配置时推送至 origin 的同名分支并建立关联。不强制推送,不弹出服务器凭证交互。提交成功但推送失败时接口返回提交 SHA 与 push_error,客户端可单独重试推送;重试携带该 SHA,HEAD 变化后要求刷新。提交 hooks 正常运行,未解决的冲突以及正在进行的合并、变基、拣选会阻止提交。
|
|
538
|
+
|
|
539
|
+
提交消息框左下角的魔棒按钮根据勾选文件自动生成中文提交信息,使用当前 active_provider 下配置的第一个模型,结果可继续手动编辑。`POST /api/git/commit-message` 接收 working_directory、paths、revisions、head,返回 message;只读取所选文件的差异,不提交或修改暂存区。单次最多选择 200 个文件,每个文件差异最多提供 8000 字符,总差异最多 48000 字符;超出部分附截断标记,二进制或超大文件只提供路径、状态和不可预览原因。
|
|
540
|
+
|
|
541
|
+
打开包含多个 Git 仓库的父目录时,Git 视图顶部提供仓库下拉框,可切换 client、server 等子仓库。`GET /api/git/repositories` 递归识别当前目录及子目录中的仓库(包含 `.git` 文件形式的 worktree),跳过 Git 元数据、依赖、构建目录和符号链接;扫描有时间、目录数和仓库数上限,未完整扫描时返回 truncated。其他 Git 接口通过可选的 `repository` 字段或查询参数选择仓库,值为相对项目目录的仓库路径,`.` 表示当前目录;省略时保持原有行为。文件路径相对选中仓库,目录必须位于已配置项目内,子仓库无需单独加入项目配置。切换仓库分别保留勾选、提交消息和推送重试状态。
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
文件预览先通过 `GET /api/files/info` 读取类型和大小。500 KB 及以下直接加载;超过 500 KB 时,根据客户端网络情况决定是否获取正文:移动网络或无法确认网络类型时显示大小与“预览”按钮,用户可主动继续加载;Wi-Fi、有线网络等非移动网络自动加载。`GET /api/files/content` 支持完整 UTF-8 大文件读取;`GET /api/files/image` 通过现有设备认证传输图片,支持常见位图及 SVG。大文本采用按可见行渲染的只读预览,避免整篇语法高亮;现有文本编辑保存的大小限制保持不变。
|
|
545
|
+
|
|
546
|
+
Git 弹窗同样使用网络提示:`GET /api/git/preview-info` 返回两侧大小、图片类型和 HEAD;`GET /api/git/image` 通过 side=before/after 及 head 读取提交前/当前图片,支持新增、修改、重命名和删除图片。文本差异支持超过 500 KB 的内容;生成提交信息时的大小限制独立保留。图片原始接口不缓存授权文件。客户端新增 connectivity_plus 原生插件后,需要手动重新运行 APP 完成插件注册。
|