multicc 0.3.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.
- multicc-0.3.0/LICENSE +21 -0
- multicc-0.3.0/PKG-INFO +29 -0
- multicc-0.3.0/README.md +174 -0
- multicc-0.3.0/multicc.egg-info/PKG-INFO +29 -0
- multicc-0.3.0/multicc.egg-info/SOURCES.txt +29 -0
- multicc-0.3.0/multicc.egg-info/dependency_links.txt +1 -0
- multicc-0.3.0/multicc.egg-info/entry_points.txt +2 -0
- multicc-0.3.0/multicc.egg-info/requires.txt +1 -0
- multicc-0.3.0/multicc.egg-info/top_level.txt +1 -0
- multicc-0.3.0/pyproject.toml +16 -0
- multicc-0.3.0/setup.cfg +4 -0
- multicc-0.3.0/setup.py +10 -0
- multicc-0.3.0/tasker/__init__.py +20 -0
- multicc-0.3.0/tasker/__main__.py +5 -0
- multicc-0.3.0/tasker/approvals.py +65 -0
- multicc-0.3.0/tasker/claude_runner.py +271 -0
- multicc-0.3.0/tasker/codex_app_server_runner.py +758 -0
- multicc-0.3.0/tasker/codex_runner.py +249 -0
- multicc-0.3.0/tasker/config.py +198 -0
- multicc-0.3.0/tasker/console.py +102 -0
- multicc-0.3.0/tasker/live.py +303 -0
- multicc-0.3.0/tasker/llm.py +93 -0
- multicc-0.3.0/tasker/main.py +352 -0
- multicc-0.3.0/tasker/mock_runner.py +116 -0
- multicc-0.3.0/tasker/models.py +133 -0
- multicc-0.3.0/tasker/planner.py +279 -0
- multicc-0.3.0/tasker/ptty.py +86 -0
- multicc-0.3.0/tasker/report.py +57 -0
- multicc-0.3.0/tasker/scheduler.py +318 -0
- multicc-0.3.0/tasker/sdk_runner.py +352 -0
- multicc-0.3.0/tasker/spawn.py +128 -0
multicc-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tasker contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
multicc-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: multicc
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
License: MIT License
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2026 tasker contributors
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
|
25
|
+
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: claude-agent-sdk
|
|
29
|
+
Dynamic: license-file
|
multicc-0.3.0/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# tasker — 多智能体任务编排器(claude code × codex)
|
|
2
|
+
|
|
3
|
+
输入一个目标,由 LLM 把它拆成**有依赖关系的子任务**,分派给 **Claude Code** 和 **Codex** 并行/串行执行;在 CLI 里**实时流式输出两者的完整思维链、工具调用、交互与审批请求**,并且你可以随时打字**继续提要求、中途修改、批准/拒绝**。
|
|
4
|
+
|
|
5
|
+
> 主目标平台:**macOS**。Windows/Linux 也可运行(headless 采集相同),但 pty 交互 attach 仅 macOS/Linux 原生支持。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
| 能力 | 说明 |
|
|
12
|
+
|---|---|
|
|
13
|
+
| 🧠 完整思维链 | Claude 的 `thinking` 块、Codex 的 `reasoning` 逐条实时打印(`--think full/head/off`) |
|
|
14
|
+
| 🔧 工具调用 | `tool_use`(工具名 + 参数 JSON)与 `tool_result`(stdout/stderr/是否报错) |
|
|
15
|
+
| 🔁 交互事件 | 子代理启动/进度/通知、任务轮次 |
|
|
16
|
+
| 🛡️ 审批请求 | 权限拒绝、`permission_request`/`permission_result` 实时浮现;`approval` 策略可 auto/log/ask |
|
|
17
|
+
| 💬 中途修改 | 运行中 `@claude/@codex/@all <消息>` 实时注入(claude 为真·多轮注入;codex 见下方限制) |
|
|
18
|
+
| 🎯 依赖编排 | LLM 拆出依赖图 → 分层并发;上游输出自动作为下游上下文 |
|
|
19
|
+
| 🧪 零依赖 | 纯 Python 标准库;`--mock` 无需任何 CLI/API key 即可演示全流程 |
|
|
20
|
+
| 📋 报告 | 可选 `--report` 落一份完整 Markdown 轨迹 |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 安装
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 1) 准备两个执行器(macOS)
|
|
28
|
+
claude # Claude Code CLI(本工具直接调用)
|
|
29
|
+
npm install -g @openai/codex # Codex CLI(可选,未装时 codex 任务会失败/可先用 --mock)
|
|
30
|
+
|
|
31
|
+
# 2) 安装 tasker(命令名 tasker,PyPI 包名 multicc)
|
|
32
|
+
pip install multicc # 从 PyPI(需联网)
|
|
33
|
+
# 或离线:源码机 python scripts/build_wheel.py 得到 .whl 后 pip install <whl>
|
|
34
|
+
# 或源码直接跑:python tasker_cli.py
|
|
35
|
+
|
|
36
|
+
tasker verify-config # 检查环境
|
|
37
|
+
|
|
38
|
+
# 3) 配置(可选项,见下方"配置")
|
|
39
|
+
tasker init && $EDITOR config.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Python ≥ 3.9,无第三方依赖。完整打包 / 发布说明见 [RELEASE.md](RELEASE.md)。
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 快速开始
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# 真实运行(LLM 拆分 → claude/codex 执行 → CLI 实时输出)
|
|
50
|
+
python -m tasker run "写一个 Python CLI 工具,带子命令,并补上单元测试"
|
|
51
|
+
|
|
52
|
+
# 规则拆分(不用 LLM 也能跑,适合快速试)
|
|
53
|
+
python -m tasker run "实现一个斐波那契脚本" --plan-rules
|
|
54
|
+
|
|
55
|
+
# 无任何 CLI / API key 的纯演示
|
|
56
|
+
python -m tasker run "写一个 python 脚本并测试" --mock --think head
|
|
57
|
+
|
|
58
|
+
# 只打印计划
|
|
59
|
+
python -m tasker plan "重构这个项目" --json
|
|
60
|
+
|
|
61
|
+
# 附加到交互 TUI(macOS 原生,直接看真实审批弹窗)
|
|
62
|
+
python -m tasker attach claude "帮我初始化一个 Go 项目"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 运行中随时输入
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
@all <消息> 发给所有运行中的 agent
|
|
69
|
+
@claude <消息> 只发给 claude 执行的任务
|
|
70
|
+
@codex <消息> 只发给 codex 执行的任务
|
|
71
|
+
@t2 <消息> 发给指定任务
|
|
72
|
+
<裸文本> 等同 @all <文本>
|
|
73
|
+
:allow [<id>] 批准最近/指定审批请求
|
|
74
|
+
:deny [<id>] 拒绝最近/指定审批请求
|
|
75
|
+
:done [<taskid>] 手动收尾任务(关闭其 stdin 让进程退出)
|
|
76
|
+
:status 各任务进度(思考/工具/审批/注入计数)
|
|
77
|
+
:plan 重打计划
|
|
78
|
+
:quit 终止全部并退出
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> claude 子任务是**真·实时多轮**:注入的消息写入其 `stream-json` stdin,下一轮即被采纳(已实测:任务执行到一半注入"第二行改成 xxx",最终产物按修改后内容产出)。
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 架构
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
prompt
|
|
89
|
+
│ LLM API(anthropic / openai 兼容)
|
|
90
|
+
▼
|
|
91
|
+
planner ──► Plan {tasks[], depends_on[], executor: claude|codex}
|
|
92
|
+
│ │ (规则拆分 plan_with_rules 作为无 key 回退)
|
|
93
|
+
▼
|
|
94
|
+
scheduler(按依赖分层,max_parallel 并发)
|
|
95
|
+
├─► ClaudeRunner ─► claude -p --output-format stream-json --verbose --input-format stream-json
|
|
96
|
+
│ │ stdin 保持打开 → @all/@claude 实时注入
|
|
97
|
+
│ ▼
|
|
98
|
+
│ 事件流: thinking / tool_use / tool_result / task_* / result / permission_denials
|
|
99
|
+
├─► CodexRunner ─► codex exec --json --full-trace
|
|
100
|
+
│ │ approval_request / tool_call / reasoning / completed
|
|
101
|
+
│ ▼
|
|
102
|
+
└─► 每个事件 → LiveTui 实时打印 + 写入 workspaces/<run>/<task>.events.jsonl
|
|
103
|
+
│
|
|
104
|
+
▼
|
|
105
|
+
用户在终端输入 → 输入线程 → 路由给对应 runner
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
事件模型(`tasker/models.py`):
|
|
109
|
+
`thinking` `text` `tool_use` `tool_result` `permission_request` `permission_result` `user_message` `interaction` `system` `error` `result` `raw`
|
|
110
|
+
|
|
111
|
+
文件结构:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
tasker/
|
|
115
|
+
main.py CLI 入口(run / plan / attach / verify-config / init)
|
|
116
|
+
scheduler.py 依赖分层、并发、用户输入路由、审批策略接线
|
|
117
|
+
planner.py LLM 拆分 + 规则回退 + 依赖校验/环检测
|
|
118
|
+
claude_runner.py Claude 事件采集 + stdin 实时注入 + 完成判定
|
|
119
|
+
codex_runner.py Codex 事件采集
|
|
120
|
+
live.py LiveTui:实时打印 + 后台输入线程 + 指令解析
|
|
121
|
+
approvals.py 审批策略(auto / log / ask_console)
|
|
122
|
+
ptty.py pty attach(macOS/Linux,真实审批交互)
|
|
123
|
+
llm.py / config.py / spawn.py / models.py / console.py / report.py / mock_runner.py
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 配置(config.json)
|
|
129
|
+
|
|
130
|
+
| 段 | 键 | 默认 | 说明 |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `llm` | `provider` | `anthropic` | 拆分用 LLM;`openai` 可接任意 OpenAI 兼容接口(DeepSeek/Ollama/网关) |
|
|
133
|
+
| `llm` | `base_url` | `""` | 兼容接口地址,如 `https://api.deepseek.com/v1` |
|
|
134
|
+
| `llm` | `api_key_env` | `ANTHROPIC_API_KEY` | 从该环境变量取 key |
|
|
135
|
+
| `llm` | `model` | `claude-sonnet-5` | 拆分模型 |
|
|
136
|
+
| `claude` | `permission_mode` | `acceptEdits` | headless 下自动允许工作区内编辑;要全自主(含 Bash)改 `bypassPermissions` |
|
|
137
|
+
| `claude` | `allowed_tools` | `[]` | 预授权工具白名单 |
|
|
138
|
+
| `claude` | `completion_idle` | `5.0` | 最终 result 后无活动秒数 → 关闭 stdin 收尾 |
|
|
139
|
+
| `codex` | `sandbox` | `workspace-write` | `read-only` / `workspace-write` / `danger-full-access` |
|
|
140
|
+
| `codex` | `auto_approve` | `false` | 传 `--auto-approve` 给 codex exec |
|
|
141
|
+
| `approval` | `mode` | `auto` | `auto` / `log` / `ask_console` |
|
|
142
|
+
| `approval` | `default_allow` | `true` | auto 模式的默认决定 |
|
|
143
|
+
| — | `max_parallel` | `2` | 同层并发数 |
|
|
144
|
+
| — | `timeout_per_task` | `900` | 单任务超时 |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## macOS 行为说明(重点)
|
|
149
|
+
|
|
150
|
+
- **进程启动**:macOS 上 `claude`/`codex` 是 POSIX 二进制或 `#!/bin/sh` shim,`spawn.py` 直接用 `shutil.which` 解析后 `Popen`,无需 `.cmd` 兼容层(Windows 的 `.cmd` 已额外处理)。
|
|
151
|
+
- **交互 attach**:`python -m tasker attach claude|codex` 用 `os.forkpty` 起交互式 TUI 并原样转发到你终端 —— 你能看到**真实的审批弹窗**并直接输入 `y/n` 回答。这是 headless 拿不到的那部分交互(claude -p / codex exec 无 TTY 不弹窗)。
|
|
152
|
+
- **编码**:全程 UTF-8;macOS 终端默认 UTF-8,中文/emoji 无碍。
|
|
153
|
+
- **权限**:headless 下 claude 的写文件/命令会走策略判定,拒绝会以 `tool_result` 拒由 + `result.permission_denials` 呈现(已实测);需要"不问就干"请设 `permission_mode=bypassPermissions`(风险自负)。
|
|
154
|
+
- **SIGINT**:`Ctrl-C` 触发优雅中断(`KeyboardInterrupt` 路径)。
|
|
155
|
+
|
|
156
|
+
## Codex 的已知限制
|
|
157
|
+
|
|
158
|
+
- `codex exec` 是**单轮非交互**模式,**不支持**像 claude 那样中途注入消息。`@codex` 注入会记录到交互日志,并在调度**依赖它的下一个子任务**时作为上下文携带("下一轮注入")。
|
|
159
|
+
- `codex exec --json` 会发 `approval_request` 事件(本工具会实时打印),但非交互下无法经 stdin 回答;真正的审批对话请用 `attach codex`。
|
|
160
|
+
- 未安装 codex 时,含 codex 的任务会失败并给出清晰报错;可用 `--mock` 或 `--plan-rules` 先体验。
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 常见问题
|
|
165
|
+
|
|
166
|
+
**任务拆分 LLM 报 401 / 网络错?**
|
|
167
|
+
`llm` 段用独立 API key。若你的 `ANTHROPIC_API_KEY` 是 Claude Code 网关专用 key,无法直接访问 api.anthropic.com —— 设 `llm.base_url` 指向你的网关,或换 `provider=openai` + DeepSeek/Ollama。任何失败都会自动回退到规则拆分。
|
|
168
|
+
|
|
169
|
+
**headless 下"审批请求"不弹窗?**
|
|
170
|
+
这是 CLI 行为限制:权限弹窗只在交互 TTY 出现。headless 会直接给出工具执行/拒绝事件。要看并回答真实审批,用 `attach` 模式。
|
|
171
|
+
|
|
172
|
+
**运行卡住不结束?**
|
|
173
|
+
claude 发完最终结果不会自己退出(stdin 仍开)。完成判定:最终 `result` 后 `completion_idle` 秒无活动 → 关闭 stdin 收尾;可 `:done` 手动收尾。
|
|
174
|
+
# taskdispatcher
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: multicc
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
License: MIT License
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2026 tasker contributors
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
|
25
|
+
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: claude-agent-sdk
|
|
29
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
multicc.egg-info/PKG-INFO
|
|
6
|
+
multicc.egg-info/SOURCES.txt
|
|
7
|
+
multicc.egg-info/dependency_links.txt
|
|
8
|
+
multicc.egg-info/entry_points.txt
|
|
9
|
+
multicc.egg-info/requires.txt
|
|
10
|
+
multicc.egg-info/top_level.txt
|
|
11
|
+
tasker/__init__.py
|
|
12
|
+
tasker/__main__.py
|
|
13
|
+
tasker/approvals.py
|
|
14
|
+
tasker/claude_runner.py
|
|
15
|
+
tasker/codex_app_server_runner.py
|
|
16
|
+
tasker/codex_runner.py
|
|
17
|
+
tasker/config.py
|
|
18
|
+
tasker/console.py
|
|
19
|
+
tasker/live.py
|
|
20
|
+
tasker/llm.py
|
|
21
|
+
tasker/main.py
|
|
22
|
+
tasker/mock_runner.py
|
|
23
|
+
tasker/models.py
|
|
24
|
+
tasker/planner.py
|
|
25
|
+
tasker/ptty.py
|
|
26
|
+
tasker/report.py
|
|
27
|
+
tasker/scheduler.py
|
|
28
|
+
tasker/sdk_runner.py
|
|
29
|
+
tasker/spawn.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
claude-agent-sdk
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tasker
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "multicc"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
requires-python = ">=3.9"
|
|
9
|
+
license = { file = "LICENSE" }
|
|
10
|
+
dependencies = ["claude-agent-sdk"]
|
|
11
|
+
|
|
12
|
+
[project.scripts]
|
|
13
|
+
tasker = "tasker.main:main"
|
|
14
|
+
|
|
15
|
+
[tool.setuptools]
|
|
16
|
+
packages = ["tasker"]
|
multicc-0.3.0/setup.cfg
ADDED
multicc-0.3.0/setup.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""离线构建入口(配合 --no-build-isolation 使用,避免联网装 setuptools/wheel)。
|
|
2
|
+
|
|
3
|
+
用法(在项目根目录):
|
|
4
|
+
python setup.py --version # 只读版本
|
|
5
|
+
SETUPTOOLS_USE_DISTUTILS=stdlib python setup.py sdist bdist_wheel --dist-dir dist
|
|
6
|
+
"""
|
|
7
|
+
from setuptools import setup
|
|
8
|
+
|
|
9
|
+
# 元数据集中在 pyproject.toml;这里只做最低限度,保证离线 -e 安装可用。
|
|
10
|
+
setup()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""tasker —— 多智能体任务编排器。
|
|
2
|
+
|
|
3
|
+
通过 LLM API 把用户 prompt 拆分为子任务,分派给 Claude Code 与 Codex 执行,
|
|
4
|
+
并完整采集两者的"思维链 / 工具调用 / 交互 / 审批请求"事件,输出 Markdown 报告。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
_pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
|
|
13
|
+
__version__ = "0.0.0"
|
|
14
|
+
try:
|
|
15
|
+
_text = _pyproject.read_text(encoding="utf-8")
|
|
16
|
+
_m = re.search(r'^version\s*=\s*"([^"]+)"', _text, re.MULTILINE)
|
|
17
|
+
if _m:
|
|
18
|
+
__version__ = _m.group(1)
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""审批请求处理策略。
|
|
2
|
+
|
|
3
|
+
headless 模式下各 CLI 对审批的表现不同:
|
|
4
|
+
- claude -p:权限弹窗只在交互 TTY 出现;headless 下工具按 permission_mode 自动允许/拒绝,
|
|
5
|
+
我们把拒绝事件(tool_result / result.permission_denials)采集上报,必要时回注一条用户消息
|
|
6
|
+
告知模型"用户已批准,请继续"。
|
|
7
|
+
- codex exec --json:会在 stdout 发 approval_request 事件,但非交互下无法经 stdin 回答;
|
|
8
|
+
auto_approve 由 --auto-approve 开关控制。
|
|
9
|
+
真正的"人点批准/拒绝"交互请用 ptty attach 模式(macOS 原生支持)。
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from .config import ApprovalConfig
|
|
16
|
+
from .models import Event, TaskRun
|
|
17
|
+
|
|
18
|
+
MODES = ("auto", "log", "ask_console")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ApprovalPolicy:
|
|
22
|
+
def __init__(self, cfg: ApprovalConfig, on_decision=None):
|
|
23
|
+
self.cfg = cfg
|
|
24
|
+
self.on_decision = on_decision
|
|
25
|
+
self.pending: dict[str, dict] = {} # id -> {"run": TaskRun, "event": Event}
|
|
26
|
+
|
|
27
|
+
def handle(self, run: TaskRun, event: Event, emit, send_msg):
|
|
28
|
+
"""处理一条 permission_request 事件。emit: (run, Event) 回调;send_msg: 回注消息函数。"""
|
|
29
|
+
req_id = str(event.data.get("id") or event.data.get("tool_use_id") or time.time())
|
|
30
|
+
mode = self.cfg.mode
|
|
31
|
+
self.pending[req_id] = {"run": run, "event": event, "ts": time.time()}
|
|
32
|
+
|
|
33
|
+
if mode == "auto":
|
|
34
|
+
allowed = self.cfg.default_allow
|
|
35
|
+
self._decide(req_id, allowed, run, event, emit, send_msg)
|
|
36
|
+
elif mode == "ask_console":
|
|
37
|
+
# 在 CLI 里已实时展示,等待用户 :allow / :deny
|
|
38
|
+
return
|
|
39
|
+
else: # log
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
def decide(self, req_id: str, allowed: bool, emit, send_msg) -> bool:
|
|
43
|
+
"""由用户指令 :allow/:deny 触发。"""
|
|
44
|
+
item = self.pending.get(req_id)
|
|
45
|
+
if item is None:
|
|
46
|
+
# 没有登记的请求:允许对所有运行中 runner 注入"用户决定"消息
|
|
47
|
+
return False
|
|
48
|
+
self._decide(req_id, allowed, item["run"], item["event"], emit, send_msg)
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
def _decide(self, req_id, allowed, run, event, emit, send_msg) -> None:
|
|
52
|
+
note = "批准" if allowed else "拒绝"
|
|
53
|
+
emit(
|
|
54
|
+
run,
|
|
55
|
+
Event(
|
|
56
|
+
kind="permission_result",
|
|
57
|
+
source=run.task.executor,
|
|
58
|
+
text=f"{note}请求 {req_id}",
|
|
59
|
+
data={"allowed": allowed, "id": req_id},
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
if send_msg:
|
|
63
|
+
guidance = "用户已批准该工具请求,如适用请继续执行。" if allowed else "用户已拒绝该请求,请换一种安全方式继续。"
|
|
64
|
+
send_msg(f"[审批反馈] {guidance}({event.text})")
|
|
65
|
+
self.pending.pop(req_id, None)
|