opencac 0.2.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.
- opencac-0.2.0/LICENSE +21 -0
- opencac-0.2.0/PKG-INFO +189 -0
- opencac-0.2.0/README.md +166 -0
- opencac-0.2.0/pyproject.toml +61 -0
- opencac-0.2.0/setup.cfg +4 -0
- opencac-0.2.0/src/opencac/__init__.py +12 -0
- opencac-0.2.0/src/opencac/agents.py +18 -0
- opencac-0.2.0/src/opencac/audit.py +90 -0
- opencac-0.2.0/src/opencac/cli.py +166 -0
- opencac-0.2.0/src/opencac/cli_runtime.py +421 -0
- opencac-0.2.0/src/opencac/pipeline.py +124 -0
- opencac-0.2.0/src/opencac/roles.py +569 -0
- opencac-0.2.0/src/opencac/runtime.py +545 -0
- opencac-0.2.0/src/opencac/schemas.py +30 -0
- opencac-0.2.0/src/opencac/service.py +339 -0
- opencac-0.2.0/src/opencac/sidecar.py +108 -0
- opencac-0.2.0/src/opencac.egg-info/PKG-INFO +189 -0
- opencac-0.2.0/src/opencac.egg-info/SOURCES.txt +25 -0
- opencac-0.2.0/src/opencac.egg-info/dependency_links.txt +1 -0
- opencac-0.2.0/src/opencac.egg-info/entry_points.txt +2 -0
- opencac-0.2.0/src/opencac.egg-info/top_level.txt +1 -0
- opencac-0.2.0/tests/test_agent_integration.py +371 -0
- opencac-0.2.0/tests/test_cli.py +201 -0
- opencac-0.2.0/tests/test_core_pipeline.py +388 -0
- opencac-0.2.0/tests/test_packaging.py +84 -0
- opencac-0.2.0/tests/test_service.py +230 -0
- opencac-0.2.0/tests/test_support.py +183 -0
opencac-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lpoee976
|
|
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.
|
opencac-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opencac
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: OpenCAC: Claude Code, Antigravity, and Codex working together across cloud APIs and local models.
|
|
5
|
+
Author: Codex
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/lpoee/opencac
|
|
8
|
+
Project-URL: Repository, https://github.com/lpoee/opencac
|
|
9
|
+
Project-URL: Issues, https://github.com/lpoee/opencac/issues
|
|
10
|
+
Keywords: opencac,claude-code,codex,antigravity,multi-agent-orchestration,ai-coding-agent-pipeline,claude-code-codex-together,ai-coding-workflow-automation,orchestrate-ai-coding-tools,developer-cli-tools,ai-agent-chaining,reduce-ai-api-cost,local-llm-code-quality,air-gapped-ai-coding,private-ai-code-generation,local-llm,hybrid-ai,jsonl,audit-log,speculative-decoding,llama-cpp,validated-handoff,session-resume
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# OpenCAC
|
|
25
|
+
|
|
26
|
+
**OpenCAC is multi-agent orchestration CLI for AI coding tools. Wires Claude Code, Antigravity, and Codex into one pipeline with validated handoffs, audit logging, and speculative decoding for local LLMs.**
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install .
|
|
30
|
+
opencac run "refactor the auth module" --mode private
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Why
|
|
34
|
+
|
|
35
|
+
For developers who already use multiple AI coding agents and want one CLI to orchestrate them — with cloud models, local LLMs, or both.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
- Claude Code, Codex, Antigravity powerful alone, but each runs in its own world
|
|
39
|
+
- Cloud api burns money — every hosted model call costs real tokens.
|
|
40
|
+
- Local LLMs lack quality — small models are cheap but can't reliably produce production-grade code.
|
|
41
|
+
|
|
42
|
+
OpenCAC solves this by chaining agents into a four-role pipeline where each agent does what it's best at, with structured validation at every hop.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
1. FOUR-ROLE PIPELINE
|
|
49
|
+
dispatcher → antigravity (research) → claude-code (plan) → codex (execute)
|
|
50
|
+
- Structured envelopes at every hop; downstream critiques upstream before acting
|
|
51
|
+
- Codex runs assess_plan — dangerous commands rejected, not blindly run
|
|
52
|
+
|
|
53
|
+
2. ROUTING MODES
|
|
54
|
+
private Loopback only. Private guard required. For sensitive / air-gapped work.
|
|
55
|
+
cloud Cloud API tokens. No local infra needed.
|
|
56
|
+
hybrid Cloud first, falls back to local LLM when tokens are missing.
|
|
57
|
+
|
|
58
|
+
3. LOCAL LLM SUPPORT
|
|
59
|
+
- Each role points to its own llama.cpp server endpoint
|
|
60
|
+
- Built-in spec decoding config (n-gram / draft-model) → generates llama-server commands
|
|
61
|
+
- Probe: constrained-grammar check verifies each endpoint before pipeline starts
|
|
62
|
+
|
|
63
|
+
4. SIDECAR VALIDATION
|
|
64
|
+
- Schema check on every hop — agent whitelist, message-type whitelist, payload fields
|
|
65
|
+
- Blocked commands: rm -rf /, shutdown, mkfs, fork bomb
|
|
66
|
+
- Private mode: loopback-only on all URLs including callbacks
|
|
67
|
+
|
|
68
|
+
5. JSONL AUDIT LOG
|
|
69
|
+
- One JSON line per action — timestamp, session_id, kind
|
|
70
|
+
- Filter by session, query last N entries
|
|
71
|
+
- Session resume: rebuild plan from log, skip completed steps
|
|
72
|
+
|
|
73
|
+
6. CLI + HTTP
|
|
74
|
+
CLI opencac run, opencac audit, opencac resume, interactive REPL
|
|
75
|
+
HTTP POST /run, GET /tasks/<id>, per-agent endpoints, /.well-known/agent.json
|
|
76
|
+
Distributed CLI routes through HTTP service, sync and async
|
|
77
|
+
|
|
78
|
+
7. SMART QUESTION ROUTING
|
|
79
|
+
- Ends with ? or starts with who/what/how/why → QA path, skips pipeline
|
|
80
|
+
- Task input → full pipeline, outputs artifacts
|
|
81
|
+
- Mentions docs/code/error/test → research step first
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone https://github.com/lpoee/opencac.git && cd opencac
|
|
88
|
+
python3 -m venv .venv && . .venv/bin/activate && pip install .
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Private (local llama.cpp shards)
|
|
93
|
+
export A2A_ANTIGRAVITY_URL=http://127.0.0.1:18101
|
|
94
|
+
export A2A_CLAUDE_CODE_URL=http://127.0.0.1:18102
|
|
95
|
+
export A2A_CODEX_URL=http://127.0.0.1:18103
|
|
96
|
+
opencac run "task" --mode private
|
|
97
|
+
|
|
98
|
+
# Cloud
|
|
99
|
+
export A2A_ANTIGRAVITY_TOKEN=...
|
|
100
|
+
export A2A_CLAUDE_CODE_TOKEN=...
|
|
101
|
+
export A2A_CODEX_TOKEN=...
|
|
102
|
+
opencac run "task" --mode cloud
|
|
103
|
+
|
|
104
|
+
# Hybrid
|
|
105
|
+
export A2A_CLOUD_FALLBACK_LOCAL=1
|
|
106
|
+
opencac run "task" --mode cloud
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Agent Integration
|
|
110
|
+
|
|
111
|
+
Connect real AI agents for production-quality research, planning, and code generation.
|
|
112
|
+
When set, the pipeline calls real agents first and falls back to local heuristics on failure.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Antigravity — Gemini research via JSON-RPC (A2A protocol)
|
|
116
|
+
export OPENCAC_RESEARCH_URL=http://127.0.0.1:18791
|
|
117
|
+
|
|
118
|
+
# Claude Code — planning via Claude Bridge (Anthropic messages API)
|
|
119
|
+
export OPENCAC_PLANNER_URL=http://127.0.0.1:9300
|
|
120
|
+
|
|
121
|
+
# Codex — AI code generation via CLI
|
|
122
|
+
export OPENCAC_CODEX_BINARY=/usr/local/bin/codex
|
|
123
|
+
|
|
124
|
+
opencac run "refactor the auth module" --mode private
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Variable | Agent | Protocol | Purpose |
|
|
128
|
+
| ---------------------- | ------------- | --------------------------- | -------------------------- |
|
|
129
|
+
| `OPENCAC_RESEARCH_URL` | Antigravity | JSON-RPC 2.0 `message/send` | Web research via Gemini |
|
|
130
|
+
| `OPENCAC_PLANNER_URL` | Claude Bridge | `POST /v1/messages` | Plan generation via Claude |
|
|
131
|
+
| `OPENCAC_CODEX_BINARY` | Codex CLI | JSONL subprocess | AI code generation |
|
|
132
|
+
|
|
133
|
+
The `generate` plan action dispatches work to Codex CLI for AI-assisted code generation.
|
|
134
|
+
Without these variables, the pipeline uses deterministic local heuristics (file search, template plans, subprocess execution).
|
|
135
|
+
|
|
136
|
+
Docker:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
docker build -t opencac .
|
|
140
|
+
docker run --rm -p 8000:8000 -v "$(pwd)/data:/data" opencac
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Speculative Decoding
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
opencac run "task" --mode private \
|
|
147
|
+
--spec-type ngram-simple \
|
|
148
|
+
--draft-max 64 --draft-min 16
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| Strategy | Description | VRAM |
|
|
152
|
+
| -------------------------- | ------------------------------------ | ----------- |
|
|
153
|
+
| Self-speculative (default) | n-gram cache on main model | Zero extra |
|
|
154
|
+
| Draft-model | Smaller model generates draft tokens | Extra model |
|
|
155
|
+
|
|
156
|
+
## HTTP API
|
|
157
|
+
|
|
158
|
+
| Method | Path | Description |
|
|
159
|
+
| ------ | -------------------------------- | ----------------- |
|
|
160
|
+
| `GET` | `/.well-known/agent.json` | Agent card |
|
|
161
|
+
| `GET` | `/tasks/<id>` | Status + steps |
|
|
162
|
+
| `GET` | `/audit?session_id=<id>&last=20` | Audit log |
|
|
163
|
+
| `POST` | `/run` | Run task |
|
|
164
|
+
| `POST` | `/run?distributed=1&async=1` | Async distributed |
|
|
165
|
+
| `POST` | `/agents/<agent>/message/send` | Message an agent |
|
|
166
|
+
|
|
167
|
+
## Output
|
|
168
|
+
|
|
169
|
+
- `artifacts/<session-id>/plan.json` — execution plan
|
|
170
|
+
- `artifacts/<session-id>/result.md` — routing, strategy, step results
|
|
171
|
+
- `.opencac/audit.jsonl` — full event log
|
|
172
|
+
|
|
173
|
+
## Testing
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
PYTHONPATH=src pytest -q # 32 tests
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Security
|
|
180
|
+
|
|
181
|
+
See [SECURITY.md](SECURITY.md).
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT
|
opencac-0.2.0/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# OpenCAC
|
|
2
|
+
|
|
3
|
+
**OpenCAC is multi-agent orchestration CLI for AI coding tools. Wires Claude Code, Antigravity, and Codex into one pipeline with validated handoffs, audit logging, and speculative decoding for local LLMs.**
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install .
|
|
7
|
+
opencac run "refactor the auth module" --mode private
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
For developers who already use multiple AI coding agents and want one CLI to orchestrate them — with cloud models, local LLMs, or both.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
- Claude Code, Codex, Antigravity powerful alone, but each runs in its own world
|
|
16
|
+
- Cloud api burns money — every hosted model call costs real tokens.
|
|
17
|
+
- Local LLMs lack quality — small models are cheap but can't reliably produce production-grade code.
|
|
18
|
+
|
|
19
|
+
OpenCAC solves this by chaining agents into a four-role pipeline where each agent does what it's best at, with structured validation at every hop.
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
1. FOUR-ROLE PIPELINE
|
|
26
|
+
dispatcher → antigravity (research) → claude-code (plan) → codex (execute)
|
|
27
|
+
- Structured envelopes at every hop; downstream critiques upstream before acting
|
|
28
|
+
- Codex runs assess_plan — dangerous commands rejected, not blindly run
|
|
29
|
+
|
|
30
|
+
2. ROUTING MODES
|
|
31
|
+
private Loopback only. Private guard required. For sensitive / air-gapped work.
|
|
32
|
+
cloud Cloud API tokens. No local infra needed.
|
|
33
|
+
hybrid Cloud first, falls back to local LLM when tokens are missing.
|
|
34
|
+
|
|
35
|
+
3. LOCAL LLM SUPPORT
|
|
36
|
+
- Each role points to its own llama.cpp server endpoint
|
|
37
|
+
- Built-in spec decoding config (n-gram / draft-model) → generates llama-server commands
|
|
38
|
+
- Probe: constrained-grammar check verifies each endpoint before pipeline starts
|
|
39
|
+
|
|
40
|
+
4. SIDECAR VALIDATION
|
|
41
|
+
- Schema check on every hop — agent whitelist, message-type whitelist, payload fields
|
|
42
|
+
- Blocked commands: rm -rf /, shutdown, mkfs, fork bomb
|
|
43
|
+
- Private mode: loopback-only on all URLs including callbacks
|
|
44
|
+
|
|
45
|
+
5. JSONL AUDIT LOG
|
|
46
|
+
- One JSON line per action — timestamp, session_id, kind
|
|
47
|
+
- Filter by session, query last N entries
|
|
48
|
+
- Session resume: rebuild plan from log, skip completed steps
|
|
49
|
+
|
|
50
|
+
6. CLI + HTTP
|
|
51
|
+
CLI opencac run, opencac audit, opencac resume, interactive REPL
|
|
52
|
+
HTTP POST /run, GET /tasks/<id>, per-agent endpoints, /.well-known/agent.json
|
|
53
|
+
Distributed CLI routes through HTTP service, sync and async
|
|
54
|
+
|
|
55
|
+
7. SMART QUESTION ROUTING
|
|
56
|
+
- Ends with ? or starts with who/what/how/why → QA path, skips pipeline
|
|
57
|
+
- Task input → full pipeline, outputs artifacts
|
|
58
|
+
- Mentions docs/code/error/test → research step first
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/lpoee/opencac.git && cd opencac
|
|
65
|
+
python3 -m venv .venv && . .venv/bin/activate && pip install .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Private (local llama.cpp shards)
|
|
70
|
+
export A2A_ANTIGRAVITY_URL=http://127.0.0.1:18101
|
|
71
|
+
export A2A_CLAUDE_CODE_URL=http://127.0.0.1:18102
|
|
72
|
+
export A2A_CODEX_URL=http://127.0.0.1:18103
|
|
73
|
+
opencac run "task" --mode private
|
|
74
|
+
|
|
75
|
+
# Cloud
|
|
76
|
+
export A2A_ANTIGRAVITY_TOKEN=...
|
|
77
|
+
export A2A_CLAUDE_CODE_TOKEN=...
|
|
78
|
+
export A2A_CODEX_TOKEN=...
|
|
79
|
+
opencac run "task" --mode cloud
|
|
80
|
+
|
|
81
|
+
# Hybrid
|
|
82
|
+
export A2A_CLOUD_FALLBACK_LOCAL=1
|
|
83
|
+
opencac run "task" --mode cloud
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Agent Integration
|
|
87
|
+
|
|
88
|
+
Connect real AI agents for production-quality research, planning, and code generation.
|
|
89
|
+
When set, the pipeline calls real agents first and falls back to local heuristics on failure.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Antigravity — Gemini research via JSON-RPC (A2A protocol)
|
|
93
|
+
export OPENCAC_RESEARCH_URL=http://127.0.0.1:18791
|
|
94
|
+
|
|
95
|
+
# Claude Code — planning via Claude Bridge (Anthropic messages API)
|
|
96
|
+
export OPENCAC_PLANNER_URL=http://127.0.0.1:9300
|
|
97
|
+
|
|
98
|
+
# Codex — AI code generation via CLI
|
|
99
|
+
export OPENCAC_CODEX_BINARY=/usr/local/bin/codex
|
|
100
|
+
|
|
101
|
+
opencac run "refactor the auth module" --mode private
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Variable | Agent | Protocol | Purpose |
|
|
105
|
+
| ---------------------- | ------------- | --------------------------- | -------------------------- |
|
|
106
|
+
| `OPENCAC_RESEARCH_URL` | Antigravity | JSON-RPC 2.0 `message/send` | Web research via Gemini |
|
|
107
|
+
| `OPENCAC_PLANNER_URL` | Claude Bridge | `POST /v1/messages` | Plan generation via Claude |
|
|
108
|
+
| `OPENCAC_CODEX_BINARY` | Codex CLI | JSONL subprocess | AI code generation |
|
|
109
|
+
|
|
110
|
+
The `generate` plan action dispatches work to Codex CLI for AI-assisted code generation.
|
|
111
|
+
Without these variables, the pipeline uses deterministic local heuristics (file search, template plans, subprocess execution).
|
|
112
|
+
|
|
113
|
+
Docker:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
docker build -t opencac .
|
|
117
|
+
docker run --rm -p 8000:8000 -v "$(pwd)/data:/data" opencac
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Speculative Decoding
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
opencac run "task" --mode private \
|
|
124
|
+
--spec-type ngram-simple \
|
|
125
|
+
--draft-max 64 --draft-min 16
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
| Strategy | Description | VRAM |
|
|
129
|
+
| -------------------------- | ------------------------------------ | ----------- |
|
|
130
|
+
| Self-speculative (default) | n-gram cache on main model | Zero extra |
|
|
131
|
+
| Draft-model | Smaller model generates draft tokens | Extra model |
|
|
132
|
+
|
|
133
|
+
## HTTP API
|
|
134
|
+
|
|
135
|
+
| Method | Path | Description |
|
|
136
|
+
| ------ | -------------------------------- | ----------------- |
|
|
137
|
+
| `GET` | `/.well-known/agent.json` | Agent card |
|
|
138
|
+
| `GET` | `/tasks/<id>` | Status + steps |
|
|
139
|
+
| `GET` | `/audit?session_id=<id>&last=20` | Audit log |
|
|
140
|
+
| `POST` | `/run` | Run task |
|
|
141
|
+
| `POST` | `/run?distributed=1&async=1` | Async distributed |
|
|
142
|
+
| `POST` | `/agents/<agent>/message/send` | Message an agent |
|
|
143
|
+
|
|
144
|
+
## Output
|
|
145
|
+
|
|
146
|
+
- `artifacts/<session-id>/plan.json` — execution plan
|
|
147
|
+
- `artifacts/<session-id>/result.md` — routing, strategy, step results
|
|
148
|
+
- `.opencac/audit.jsonl` — full event log
|
|
149
|
+
|
|
150
|
+
## Testing
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
PYTHONPATH=src pytest -q # 32 tests
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Security
|
|
157
|
+
|
|
158
|
+
See [SECURITY.md](SECURITY.md).
|
|
159
|
+
|
|
160
|
+
## Contributing
|
|
161
|
+
|
|
162
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "opencac"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "OpenCAC: Claude Code, Antigravity, and Codex working together across cloud APIs and local models."
|
|
9
|
+
requires-python = ">=3.9"
|
|
10
|
+
authors = [{ name = "Codex" }]
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
keywords = [
|
|
14
|
+
"opencac",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"codex",
|
|
17
|
+
"antigravity",
|
|
18
|
+
"multi-agent-orchestration",
|
|
19
|
+
"ai-coding-agent-pipeline",
|
|
20
|
+
"claude-code-codex-together",
|
|
21
|
+
"ai-coding-workflow-automation",
|
|
22
|
+
"orchestrate-ai-coding-tools",
|
|
23
|
+
"developer-cli-tools",
|
|
24
|
+
"ai-agent-chaining",
|
|
25
|
+
"reduce-ai-api-cost",
|
|
26
|
+
"local-llm-code-quality",
|
|
27
|
+
"air-gapped-ai-coding",
|
|
28
|
+
"private-ai-code-generation",
|
|
29
|
+
"local-llm",
|
|
30
|
+
"hybrid-ai",
|
|
31
|
+
"jsonl",
|
|
32
|
+
"audit-log",
|
|
33
|
+
"speculative-decoding",
|
|
34
|
+
"llama-cpp",
|
|
35
|
+
"validated-handoff",
|
|
36
|
+
"session-resume",
|
|
37
|
+
]
|
|
38
|
+
classifiers = [
|
|
39
|
+
"Development Status :: 3 - Alpha",
|
|
40
|
+
"Intended Audience :: Developers",
|
|
41
|
+
"Operating System :: OS Independent",
|
|
42
|
+
"Programming Language :: Python :: 3",
|
|
43
|
+
"Programming Language :: Python :: 3.9",
|
|
44
|
+
"Programming Language :: Python :: 3.10",
|
|
45
|
+
"Programming Language :: Python :: 3.11",
|
|
46
|
+
"Programming Language :: Python :: 3.12",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
opencac = "opencac.cli:main"
|
|
51
|
+
|
|
52
|
+
[project.urls]
|
|
53
|
+
Homepage = "https://github.com/lpoee/opencac"
|
|
54
|
+
Repository = "https://github.com/lpoee/opencac"
|
|
55
|
+
Issues = "https://github.com/lpoee/opencac/issues"
|
|
56
|
+
|
|
57
|
+
[tool.setuptools]
|
|
58
|
+
package-dir = {"" = "src"}
|
|
59
|
+
|
|
60
|
+
[tool.setuptools.packages.find]
|
|
61
|
+
where = ["src"]
|
opencac-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .pipeline import resume_pipeline, run_pipeline
|
|
4
|
+
from .roles import Antigravity, ClaudeCodePlanner, CodexExecutor
|
|
5
|
+
from .runtime import InferenceConfig, RoutingConfig, Sidecar, ensure_private_runtime, make_envelope
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"Antigravity",
|
|
9
|
+
"ClaudeCodePlanner",
|
|
10
|
+
"CodexExecutor",
|
|
11
|
+
"InferenceConfig",
|
|
12
|
+
"RoutingConfig",
|
|
13
|
+
"Sidecar",
|
|
14
|
+
"ensure_private_runtime",
|
|
15
|
+
"make_envelope",
|
|
16
|
+
"run_pipeline",
|
|
17
|
+
"resume_pipeline",
|
|
18
|
+
]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import threading
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def utc_now() -> str:
|
|
12
|
+
return datetime.now(timezone.utc).isoformat()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class AuditLog:
|
|
17
|
+
path: Path
|
|
18
|
+
|
|
19
|
+
def __post_init__(self) -> None:
|
|
20
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
self._io_lock = threading.Lock()
|
|
22
|
+
self._session_offsets: Dict[str, List[int]] = {}
|
|
23
|
+
self._all_offsets: List[int] = []
|
|
24
|
+
self._indexed_size = 0
|
|
25
|
+
|
|
26
|
+
def append(self, event: Dict[str, Any]) -> Dict[str, Any]:
|
|
27
|
+
enriched = {"ts": utc_now(), **event}
|
|
28
|
+
encoded = (json.dumps(enriched, ensure_ascii=False) + "\n").encode("utf-8")
|
|
29
|
+
with self._io_lock:
|
|
30
|
+
offset = self.path.stat().st_size if self.path.exists() else 0
|
|
31
|
+
with self.path.open("ab") as handle:
|
|
32
|
+
handle.write(encoded)
|
|
33
|
+
self._all_offsets.append(offset)
|
|
34
|
+
session_id = enriched.get("session_id")
|
|
35
|
+
if isinstance(session_id, str):
|
|
36
|
+
self._session_offsets.setdefault(session_id, []).append(offset)
|
|
37
|
+
self._indexed_size = offset + len(encoded)
|
|
38
|
+
return enriched
|
|
39
|
+
|
|
40
|
+
def read(self, session_id: Optional[str] = None, last: int = 20) -> List[Dict[str, Any]]:
|
|
41
|
+
if not self.path.exists():
|
|
42
|
+
return []
|
|
43
|
+
with self._io_lock:
|
|
44
|
+
self._ensure_index_locked()
|
|
45
|
+
if session_id is None:
|
|
46
|
+
offsets = self._all_offsets[-last:]
|
|
47
|
+
else:
|
|
48
|
+
offsets = self._session_offsets.get(session_id, [])[-last:]
|
|
49
|
+
return self._read_offsets_locked(offsets)
|
|
50
|
+
|
|
51
|
+
def _ensure_index_locked(self) -> None:
|
|
52
|
+
if not self.path.exists():
|
|
53
|
+
self._session_offsets = {}
|
|
54
|
+
self._all_offsets = []
|
|
55
|
+
self._indexed_size = 0
|
|
56
|
+
return
|
|
57
|
+
current_size = self.path.stat().st_size
|
|
58
|
+
if current_size < self._indexed_size:
|
|
59
|
+
self._session_offsets = {}
|
|
60
|
+
self._all_offsets = []
|
|
61
|
+
self._indexed_size = 0
|
|
62
|
+
if current_size == self._indexed_size:
|
|
63
|
+
return
|
|
64
|
+
with self.path.open("rb") as handle:
|
|
65
|
+
handle.seek(self._indexed_size)
|
|
66
|
+
while True:
|
|
67
|
+
offset = handle.tell()
|
|
68
|
+
line = handle.readline()
|
|
69
|
+
if not line:
|
|
70
|
+
break
|
|
71
|
+
try:
|
|
72
|
+
item = json.loads(line.decode("utf-8"))
|
|
73
|
+
except json.JSONDecodeError:
|
|
74
|
+
continue
|
|
75
|
+
self._all_offsets.append(offset)
|
|
76
|
+
session_id = item.get("session_id")
|
|
77
|
+
if isinstance(session_id, str):
|
|
78
|
+
self._session_offsets.setdefault(session_id, []).append(offset)
|
|
79
|
+
self._indexed_size = handle.tell()
|
|
80
|
+
|
|
81
|
+
def _read_offsets_locked(self, offsets: List[int]) -> List[Dict[str, Any]]:
|
|
82
|
+
entries: List[Dict[str, Any]] = []
|
|
83
|
+
with self.path.open("rb") as handle:
|
|
84
|
+
for offset in offsets:
|
|
85
|
+
handle.seek(offset)
|
|
86
|
+
line = handle.readline()
|
|
87
|
+
if not line:
|
|
88
|
+
continue
|
|
89
|
+
entries.append(json.loads(line.decode("utf-8")))
|
|
90
|
+
return entries
|