kartikeya 0.0.1__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.
- kartikeya-0.0.1/.github/workflows/release.yml +55 -0
- kartikeya-0.0.1/.github/workflows/tests.yml +29 -0
- kartikeya-0.0.1/.gitignore +15 -0
- kartikeya-0.0.1/LICENSE +21 -0
- kartikeya-0.0.1/PKG-INFO +79 -0
- kartikeya-0.0.1/README.md +52 -0
- kartikeya-0.0.1/docs/DESIGN.md +197 -0
- kartikeya-0.0.1/pyproject.toml +47 -0
- kartikeya-0.0.1/src/kartikeya/__init__.py +32 -0
- kartikeya-0.0.1/src/kartikeya/data/kart-sandbox.json +87 -0
- kartikeya-0.0.1/src/kartikeya/execute.py +282 -0
- kartikeya-0.0.1/src/kartikeya/home.py +27 -0
- kartikeya-0.0.1/src/kartikeya/lanes.py +69 -0
- kartikeya-0.0.1/src/kartikeya/pyenv.py +75 -0
- kartikeya-0.0.1/src/kartikeya/queue.py +178 -0
- kartikeya-0.0.1/src/kartikeya/sandbox.py +997 -0
- kartikeya-0.0.1/src/kartikeya/security_scan.py +332 -0
- kartikeya-0.0.1/src/kartikeya/task_scan.py +218 -0
- kartikeya-0.0.1/src/kartikeya/worker.py +192 -0
- kartikeya-0.0.1/tests/test_queue.py +98 -0
- kartikeya-0.0.1/tests/test_sandbox.py +72 -0
- kartikeya-0.0.1/tests/test_task_scan.py +98 -0
- kartikeya-0.0.1/tests/test_worker.py +103 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a version tag is pushed (e.g. `git tag v0.0.1 && git push --tags`).
|
|
4
|
+
#
|
|
5
|
+
# Auth: a PyPI API token stored as the repo Actions secret `PYPI_API_TOKEN`
|
|
6
|
+
# (Settings -> Secrets and variables -> Actions). One-time setup: create the
|
|
7
|
+
# token on PyPI and add that secret. Until it exists, the publish step fails.
|
|
8
|
+
# (Trusted Publishing / OIDC is the tokenless alternative but this project uses
|
|
9
|
+
# the token pattern for parity with the operator's other releases.)
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags:
|
|
14
|
+
- "v*"
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.11"
|
|
25
|
+
|
|
26
|
+
- name: Build sdist and wheel
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade build
|
|
29
|
+
python -m build
|
|
30
|
+
|
|
31
|
+
- name: Check metadata
|
|
32
|
+
run: |
|
|
33
|
+
python -m pip install --upgrade twine
|
|
34
|
+
twine check dist/*
|
|
35
|
+
|
|
36
|
+
- uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
publish:
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
environment: pypi
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/download-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: dist
|
|
49
|
+
path: dist/
|
|
50
|
+
|
|
51
|
+
- name: Publish to PyPI
|
|
52
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
53
|
+
with:
|
|
54
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
55
|
+
attestations: false # attestations require OIDC/Trusted Publishing
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["master", "main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["master", "main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
|
|
22
|
+
- name: Install bubblewrap
|
|
23
|
+
run: sudo apt-get update && sudo apt-get install -y bubblewrap
|
|
24
|
+
|
|
25
|
+
- name: Install
|
|
26
|
+
run: pip install -e "." pytest
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: python -m pytest tests/ -q
|
kartikeya-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sean Campbell
|
|
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.
|
kartikeya-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kartikeya
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Kartikeya — a standalone bubblewrap-sandboxed task queue and worker. Backend-agnostic (SQLite or Postgres); the execution engine formerly known as Kart.
|
|
5
|
+
Project-URL: Homepage, https://github.com/rudi193-cmd/kartikeya
|
|
6
|
+
Project-URL: Repository, https://github.com/rudi193-cmd/kartikeya
|
|
7
|
+
Project-URL: Issues, https://github.com/rudi193-cmd/kartikeya/issues
|
|
8
|
+
Author: Sean Campbell
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: bubblewrap,bwrap,kart,sandbox,task-queue,willow,worker
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Provides-Extra: llm
|
|
24
|
+
Provides-Extra: postgres
|
|
25
|
+
Requires-Dist: psycopg2-binary<3.0,>=2.9; extra == 'postgres'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Kartikeya
|
|
29
|
+
|
|
30
|
+
> Skanda · Murugan — the six-faced commander of the divine armies, born to lead
|
|
31
|
+
> the devas. The engine that marshals and runs a fleet's tasks. Colloquially:
|
|
32
|
+
> **Kart**.
|
|
33
|
+
|
|
34
|
+
A standalone, host-agnostic **task queue + sandboxed worker**. Submit shell (or,
|
|
35
|
+
optionally, LLM-workflow) tasks to a queue; a worker claims them and runs each in
|
|
36
|
+
a [bubblewrap](https://github.com/containers/bubblewrap) sandbox with an explicit
|
|
37
|
+
mount/credential/network policy.
|
|
38
|
+
|
|
39
|
+
Kartikeya is the execution engine extracted from the Willow fleet
|
|
40
|
+
(`willow-2.0/core/kart_*`) and made to stand on its own — no fleet, no specific
|
|
41
|
+
host, no required database server.
|
|
42
|
+
|
|
43
|
+
## Status
|
|
44
|
+
|
|
45
|
+
**Pre-alpha, under active extraction.** This repo is the target of a staged lift
|
|
46
|
+
described in `docs/DESIGN.md`. The scaffold and the `TaskQueue` interface are in
|
|
47
|
+
place; the sandbox/worker/execute core is being decoupled from its origin and
|
|
48
|
+
brought over next.
|
|
49
|
+
|
|
50
|
+
## Design goals
|
|
51
|
+
|
|
52
|
+
- **Host-agnostic.** The only coupling — "where do tasks live" — is a small
|
|
53
|
+
`TaskQueue` interface a host implements. Kartikeya owns the sandbox, worker
|
|
54
|
+
loop, lanes, and command scan; the host owns storage and file roots.
|
|
55
|
+
- **Zero-infra by default.** Ships a reference `SqliteTaskQueue`, so
|
|
56
|
+
`pip install kartikeya` can execute tasks with no Postgres and no fleet.
|
|
57
|
+
- **Backend-swappable.** SQLite (bundled), Postgres, or a custom backend behind
|
|
58
|
+
the same interface.
|
|
59
|
+
- **Sandboxed and network-gated.** Tasks run network-isolated unless the stored
|
|
60
|
+
task text carries a `# allow_net` directive; credentials reach only network-
|
|
61
|
+
enabled tasks. (Who is allowed to *write* that directive is the host's call —
|
|
62
|
+
see the security note in `docs/DESIGN.md`.)
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
pip install kartikeya # base: shell tasks, SQLite backend
|
|
68
|
+
pip install "kartikeya[postgres]" # + Postgres backend helpers
|
|
69
|
+
pip install "kartikeya[llm]" # + LLM-workflow task type
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Quickstart
|
|
73
|
+
|
|
74
|
+
_Coming with stage 2 — once the worker core lands, this section documents
|
|
75
|
+
`kartikeya worker` end to end (submit → worker runs → poll)._
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT © Sean Campbell
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Kartikeya
|
|
2
|
+
|
|
3
|
+
> Skanda · Murugan — the six-faced commander of the divine armies, born to lead
|
|
4
|
+
> the devas. The engine that marshals and runs a fleet's tasks. Colloquially:
|
|
5
|
+
> **Kart**.
|
|
6
|
+
|
|
7
|
+
A standalone, host-agnostic **task queue + sandboxed worker**. Submit shell (or,
|
|
8
|
+
optionally, LLM-workflow) tasks to a queue; a worker claims them and runs each in
|
|
9
|
+
a [bubblewrap](https://github.com/containers/bubblewrap) sandbox with an explicit
|
|
10
|
+
mount/credential/network policy.
|
|
11
|
+
|
|
12
|
+
Kartikeya is the execution engine extracted from the Willow fleet
|
|
13
|
+
(`willow-2.0/core/kart_*`) and made to stand on its own — no fleet, no specific
|
|
14
|
+
host, no required database server.
|
|
15
|
+
|
|
16
|
+
## Status
|
|
17
|
+
|
|
18
|
+
**Pre-alpha, under active extraction.** This repo is the target of a staged lift
|
|
19
|
+
described in `docs/DESIGN.md`. The scaffold and the `TaskQueue` interface are in
|
|
20
|
+
place; the sandbox/worker/execute core is being decoupled from its origin and
|
|
21
|
+
brought over next.
|
|
22
|
+
|
|
23
|
+
## Design goals
|
|
24
|
+
|
|
25
|
+
- **Host-agnostic.** The only coupling — "where do tasks live" — is a small
|
|
26
|
+
`TaskQueue` interface a host implements. Kartikeya owns the sandbox, worker
|
|
27
|
+
loop, lanes, and command scan; the host owns storage and file roots.
|
|
28
|
+
- **Zero-infra by default.** Ships a reference `SqliteTaskQueue`, so
|
|
29
|
+
`pip install kartikeya` can execute tasks with no Postgres and no fleet.
|
|
30
|
+
- **Backend-swappable.** SQLite (bundled), Postgres, or a custom backend behind
|
|
31
|
+
the same interface.
|
|
32
|
+
- **Sandboxed and network-gated.** Tasks run network-isolated unless the stored
|
|
33
|
+
task text carries a `# allow_net` directive; credentials reach only network-
|
|
34
|
+
enabled tasks. (Who is allowed to *write* that directive is the host's call —
|
|
35
|
+
see the security note in `docs/DESIGN.md`.)
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
pip install kartikeya # base: shell tasks, SQLite backend
|
|
41
|
+
pip install "kartikeya[postgres]" # + Postgres backend helpers
|
|
42
|
+
pip install "kartikeya[llm]" # + LLM-workflow task type
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quickstart
|
|
46
|
+
|
|
47
|
+
_Coming with stage 2 — once the worker core lands, this section documents
|
|
48
|
+
`kartikeya worker` end to end (submit → worker runs → poll)._
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT © Sean Campbell
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Spec: Kart Lift — extract a standalone `kart` package
|
|
2
|
+
|
|
3
|
+
Status: DRAFT for review (2026-07-08). Engineering spec for the migration whose
|
|
4
|
+
direction is set in `kart-productionization.md`. Resolves B-22.
|
|
5
|
+
|
|
6
|
+
**Decisions locked (operator, 2026-07-08):**
|
|
7
|
+
1. **Shared package** — Kart becomes its own installable package; willow-mcp
|
|
8
|
+
(and later willow-2.0) *depend* on it rather than vendoring a copy. No drift.
|
|
9
|
+
2. **Product-neutral default** sandbox mount policy.
|
|
10
|
+
3. **SQLite task backend** — the queue must run with **no Postgres**; a
|
|
11
|
+
zero-infra `pip install` can execute tasks.
|
|
12
|
+
|
|
13
|
+
The big consequence: **the `kart` package must not import willow-mcp or
|
|
14
|
+
willow-2.0.** Its coupling to a host (DB access, `$WILLOW_HOME`) is inverted
|
|
15
|
+
into a small **backend interface** the host implements. Kart owns the sandbox,
|
|
16
|
+
worker loop, lanes, and scan; the host owns "where tasks live" and "where files
|
|
17
|
+
live."
|
|
18
|
+
|
|
19
|
+
## 0. Goal & acceptance
|
|
20
|
+
|
|
21
|
+
A clean `pip install willow-mcp` (which pulls in `kart`) can execute a submitted
|
|
22
|
+
task end-to-end with **no Postgres and no willow-2.0 present**:
|
|
23
|
+
```
|
|
24
|
+
pip install willow-mcp # depends on kart
|
|
25
|
+
willow-mcp worker & # drains the queue via the SQLite backend
|
|
26
|
+
# via MCP: task_submit(task="echo hi") -> task_status -> {status: completed, result: "hi"}
|
|
27
|
+
```
|
|
28
|
+
plus the same for an `allow_net=True` task with `task_net` granted (sandbox +
|
|
29
|
+
gate work standalone), and the `kart` suite green with neither willow-mcp nor
|
|
30
|
+
willow-2.0 on `sys.path`.
|
|
31
|
+
|
|
32
|
+
## 1. The `kartikeya` package
|
|
33
|
+
|
|
34
|
+
New **standalone repo** (operator decision). Distribution + import name
|
|
35
|
+
**`kartikeya`** — bringing back Kart's full name (Skanda / Murugan, the
|
|
36
|
+
six-faced commander of the divine armies; `docs/audits/KART_SANDBOX_AUDIT_2026-06-11.md`).
|
|
37
|
+
"Kart" stays the colloquial short form in prose and existing `kart:*` task ids.
|
|
38
|
+
~2,200 LOC lifted from willow-2.0 `core/kart_*`; own `pyproject`; depends only
|
|
39
|
+
on stdlib + optional extras.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
kartikeya/ # new standalone GitHub repo
|
|
43
|
+
pyproject.toml # name = "kartikeya"; deps: none (base); extras: [llm]
|
|
44
|
+
src/kartikeya/
|
|
45
|
+
__init__.py # run_worker(), execute_task_row(), TaskQueue
|
|
46
|
+
sandbox.py # from kart_sandbox.py
|
|
47
|
+
execute.py # from kart_execute.py
|
|
48
|
+
worker.py # from kart_worker.py — takes a TaskQueue
|
|
49
|
+
lanes.py # from kart_lanes.py
|
|
50
|
+
task_scan.py # from kart_task_scan.py
|
|
51
|
+
queue.py # TaskQueue ABC + SqliteTaskQueue reference impl
|
|
52
|
+
config.py # sandbox mount-policy loader + neutral default
|
|
53
|
+
data/kart-sandbox.json # product-neutral default (§5); filename kept for continuity
|
|
54
|
+
tests/ # carried test_kart_* + queue-backend tests
|
|
55
|
+
```
|
|
56
|
+
Console script: `kartikeya worker` (with a `kart` alias for muscle memory).
|
|
57
|
+
willow-mcp keeps its own `willow-mcp worker` subcommand that constructs the
|
|
58
|
+
backend and calls into the library (§3).
|
|
59
|
+
|
|
60
|
+
### 1a. Dependency dispositions
|
|
61
|
+
|
|
62
|
+
| willow-2.0 dependency | Disposition in `kart` |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `kart_sandbox/execute/worker/lanes/task_scan` | **Lifted in** as the package core |
|
|
65
|
+
| `core.pg_bridge.PgBridge` | **Inverted** → `TaskQueue` interface (§2); host supplies impl |
|
|
66
|
+
| `willow.fylgja.willow_home` | **Inverted** → host passes a work-root; `kart` falls back to `$WILLOW_HOME`/CWD |
|
|
67
|
+
| `willow.fylgja.python_env` | **Reimplemented** minimally in `kart` (stdlib `sysconfig` + venv detect) |
|
|
68
|
+
| `core.loop_heartbeat` | **Callback seam** — `on_heartbeat` hook, default no-op |
|
|
69
|
+
| `core.run_ledger` | **Callback seam** — `on_run_event` hook, default no-op |
|
|
70
|
+
| `core.grove_gate` | **Dropped** (fleet governance) |
|
|
71
|
+
| `core.outcomes` | **Dropped** (default) / host callback if wanted |
|
|
72
|
+
| `core.llm_edge` | **Optional extra** `kart[llm]`; LLM task type disabled if absent |
|
|
73
|
+
|
|
74
|
+
No `core.*` / `willow.fylgja.*` import survives in `kart`.
|
|
75
|
+
|
|
76
|
+
## 2. The `TaskQueue` backend interface
|
|
77
|
+
|
|
78
|
+
The one seam that inverts host coupling. `kart` defines the ABC; the worker
|
|
79
|
+
loop is written against it:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
class TaskQueue(ABC):
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def claim_pending(self, agent: str, limit: int) -> list[TaskRow]: ... # atomic claim → 'running'
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def mark_running(self, task_id: str) -> None: ...
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def mark_done(self, task_id: str, *, status: str, result: str) -> None: ... # completed|failed + completed_at
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def pending_count(self) -> QueueStats: ... # for liveness/fleet_health
|
|
91
|
+
```
|
|
92
|
+
`TaskRow` = `{task_id, task, agent, submitted_by, status}`.
|
|
93
|
+
|
|
94
|
+
Three implementations:
|
|
95
|
+
- **`SqliteTaskQueue`** (shipped *in* `kart`) — reference/zero-infra backend.
|
|
96
|
+
Atomic claim via a single `UPDATE tasks SET status='running' WHERE task_id IN
|
|
97
|
+
(SELECT task_id FROM tasks WHERE status='pending' AND agent=? LIMIT ?)
|
|
98
|
+
RETURNING …` inside `BEGIN IMMEDIATE` (SQLite serializes writers; safe).
|
|
99
|
+
- **willow-mcp's Postgres impl** (in willow-mcp) — maps through willow-mcp's
|
|
100
|
+
schema-adaptation (`_TASK_FIELDS`), so it works against an *adopted* `tasks`
|
|
101
|
+
table, using `FOR UPDATE SKIP LOCKED` for the claim.
|
|
102
|
+
- **willow-2.0's impl** (later) — wraps its existing `PgBridge`.
|
|
103
|
+
|
|
104
|
+
This is also exactly what makes the **SQLite backend** (decision 3) fall out
|
|
105
|
+
naturally rather than being bolted on.
|
|
106
|
+
|
|
107
|
+
## 3. willow-mcp integration
|
|
108
|
+
|
|
109
|
+
willow-mcp stays thin — it does **not** contain sandbox/worker code:
|
|
110
|
+
- Adds `kart` to `dependencies`.
|
|
111
|
+
- Implements `WillowMcpTaskQueue(TaskQueue)` over its adopted `tasks` table
|
|
112
|
+
(Postgres via schema-adaptation) **and** wires the shipped `SqliteTaskQueue`
|
|
113
|
+
for the no-Postgres path; backend chosen by config (Postgres if `WILLOW_PG_*`
|
|
114
|
+
present, else SQLite under `WILLOW_STORE_ROOT`).
|
|
115
|
+
- Adds `willow-mcp worker` (argparse subcommand, consistent with `--serve`):
|
|
116
|
+
`--lane fast|batch`, `--slots N`, `--once`. Constructs the backend + calls
|
|
117
|
+
`kart.run_worker(queue=…, lane=…)`.
|
|
118
|
+
- Ships the `CREATE TABLE tasks` DDL the review flagged missing
|
|
119
|
+
(`docs/schema/`), for both SQLite and Postgres, matching `_TASK_FIELDS`.
|
|
120
|
+
|
|
121
|
+
## 4. Security posture (preserve + test)
|
|
122
|
+
|
|
123
|
+
- **bwrap isolation** and **credential-prefix gating** carry over unchanged.
|
|
124
|
+
- **Net-directive contract stays split (B-21 ↔ B-22):** the worker grants
|
|
125
|
+
egress iff the *stored task text* has a `# allow_net` line; willow-mcp's
|
|
126
|
+
`task_submit` is the only writer of that line, only under `task_net`,
|
|
127
|
+
stripping caller copies. Ship an **end-to-end test in willow-mcp**: a
|
|
128
|
+
`task_queue`-only app submitting `task="curl …\n# allow_net"` with
|
|
129
|
+
`allow_net=False` runs **network-isolated**; a `task_net` app with
|
|
130
|
+
`allow_net=True` runs with egress. Most likely seam to regress in the lift.
|
|
131
|
+
- `kart` also gets a unit test that `task_allows_network()` still keys on
|
|
132
|
+
`line.strip() == "# allow_net"` exactly (the contract willow-mcp's strip
|
|
133
|
+
depends on).
|
|
134
|
+
|
|
135
|
+
## 5. Sandbox config — product-neutral default
|
|
136
|
+
|
|
137
|
+
Vendored `kart/data/kart-sandbox.json`: keep `WILLOW_`, `PG`, `POSTGRES`,
|
|
138
|
+
`OLLAMA_`, `GIT_` prefixes and generic bind paths; **drop** fleet-only
|
|
139
|
+
`GROVE_`/`SAFE_` prefixes and fleet bind paths from the *default*. Resolution:
|
|
140
|
+
`$KART_SANDBOX_CONFIG` → `$WILLOW_HOME/kart-sandbox.json` → vendored default.
|
|
141
|
+
Templating (`{{HOME}}`, `{{WILLOW_ROOT}}`) preserved. The fleet keeps its richer
|
|
142
|
+
policy via the override file — parity without shipping fleet surface.
|
|
143
|
+
|
|
144
|
+
## 6. Test migration
|
|
145
|
+
|
|
146
|
+
Carry `tests/test_kart_*.py` into `kart/tests/`, replacing fleet imports with a
|
|
147
|
+
fake `TaskQueue`, a tmp `kart-sandbox.json`, and no-op heartbeat/run callbacks.
|
|
148
|
+
Add `SqliteTaskQueue` backend tests (concurrent claim, no double-execution).
|
|
149
|
+
Gate bwrap tests on `bwrap_available()` so CI without bwrap skips, not fails.
|
|
150
|
+
|
|
151
|
+
## 7. Optional LLM task type
|
|
152
|
+
|
|
153
|
+
`kart[llm]` extra. Base worker runs shell tasks with zero LLM deps; an LLM task
|
|
154
|
+
submitted without the extra fails cleanly (`{"error": "llm task type requires
|
|
155
|
+
kart[llm]"}`), never an import crash. (willow-2.0 supplies its own `llm_edge`
|
|
156
|
+
adapter behind the extra's hook.)
|
|
157
|
+
|
|
158
|
+
## 8. Staged PRs / milestones
|
|
159
|
+
|
|
160
|
+
Spans two (maybe three) repos; sequence willow-mcp value first.
|
|
161
|
+
|
|
162
|
+
1. **Extract `kart` (new package):** lift the 5 files, sever fleet imports per
|
|
163
|
+
§1, define `TaskQueue` + `SqliteTaskQueue`, callback seams, neutral config.
|
|
164
|
+
*Acceptance: `kart`'s own suite green standalone; `pip install kart` +
|
|
165
|
+
SqliteTaskQueue executes a shell task in a sandbox.*
|
|
166
|
+
2. **willow-mcp integration:** depend on `kart`, add `WillowMcpTaskQueue`
|
|
167
|
+
(Pg + SQLite), `willow-mcp worker`, DDL. *Acceptance: §0 end-to-end on a
|
|
168
|
+
clean venv, no Postgres.*
|
|
169
|
+
3. **Security e2e + docs → B-22 Fixed:** the §4 net-gate e2e test, README
|
|
170
|
+
quickstart, fill `skills/kart-tasks.md` §0 worker-run section, honest
|
|
171
|
+
`pyproject`.
|
|
172
|
+
4. **Liveness:** worker heartbeat via the `on_heartbeat` seam surfaced in
|
|
173
|
+
`fleet_health`/`diagnostic_summary` (review §1).
|
|
174
|
+
5. **willow-2.0 migration (separate, later):** point willow-2.0 at the `kart`
|
|
175
|
+
package, delete its `core/kart_*`. Ends drift. Can trail well behind stage 3.
|
|
176
|
+
6. **Optional:** `kart[llm]`, systemd templates, batch-lane polish.
|
|
177
|
+
|
|
178
|
+
## 9. Decisions & remaining questions
|
|
179
|
+
|
|
180
|
+
**Locked:**
|
|
181
|
+
- Repo: **new standalone GitHub repo** (operator).
|
|
182
|
+
- Name: **`kartikeya`** (operator — "bring back Kart's full name").
|
|
183
|
+
- Shared package · product-neutral default · SQLite backend (top of doc).
|
|
184
|
+
|
|
185
|
+
**Working assumptions (operator may override):**
|
|
186
|
+
- **willow-2.0 migration is deferred to stage 5** — accepting a temporary
|
|
187
|
+
window where willow-2.0 keeps its `core/kart_*` copy while willow-mcp depends
|
|
188
|
+
on `kartikeya`. Rationale: ship willow-mcp value (close B-22) before touching
|
|
189
|
+
the live fleet.
|
|
190
|
+
- **`kartikeya` publish stays under the operator gate**, same as willow-mcp
|
|
191
|
+
(release-state hold), until explicitly released — even though its surface is
|
|
192
|
+
lower-risk than willow-mcp's OAuth layer.
|
|
193
|
+
|
|
194
|
+
**Still open:**
|
|
195
|
+
- Confirm PyPI `kartikeya` is claimable (almost certainly; verify at extract
|
|
196
|
+
time) and that the `kart` alias console-script name doesn't collide.
|
|
197
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kartikeya"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Kartikeya — a standalone bubblewrap-sandboxed task queue and worker. Backend-agnostic (SQLite or Postgres); the execution engine formerly known as Kart."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Sean Campbell" }]
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
|
+
keywords = ["task-queue", "sandbox", "bubblewrap", "bwrap", "worker", "kart", "willow"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: POSIX :: Linux",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: System :: Distributed Computing",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
]
|
|
26
|
+
dependencies = []
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
# LLM-backed "workflow" task type. Base worker runs shell tasks with zero LLM deps.
|
|
30
|
+
llm = []
|
|
31
|
+
# Reference Postgres backend helpers (SQLite backend needs no extra).
|
|
32
|
+
postgres = ["psycopg2-binary>=2.9,<3.0"]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
kartikeya = "kartikeya.worker:main"
|
|
36
|
+
kart = "kartikeya.worker:main"
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/rudi193-cmd/kartikeya"
|
|
40
|
+
Repository = "https://github.com/rudi193-cmd/kartikeya"
|
|
41
|
+
Issues = "https://github.com/rudi193-cmd/kartikeya/issues"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/kartikeya"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Kartikeya — standalone sandboxed task queue + worker (a.k.a. Kart).
|
|
2
|
+
|
|
3
|
+
Public surface:
|
|
4
|
+
- Task-queue backend seam: `TaskQueue`, `TaskRow`, `QueueStats`, `SqliteTaskQueue`.
|
|
5
|
+
- Execution lanes: `lanes` (fast/batch constants + worker-mode helpers).
|
|
6
|
+
- Task security gate: `check_kart_task` (hybrid scan over task text).
|
|
7
|
+
- Execution: `execute_task_row`, `drain_claimed_tasks`, `run_shell_task`.
|
|
8
|
+
- Worker loop: `run_worker`.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from . import lanes
|
|
13
|
+
from .execute import drain_claimed_tasks, execute_task_row, run_shell_task
|
|
14
|
+
from .queue import QueueStats, SqliteTaskQueue, TaskQueue, TaskRow
|
|
15
|
+
from .task_scan import check_kart_task
|
|
16
|
+
from .worker import run_worker
|
|
17
|
+
|
|
18
|
+
__version__ = "0.0.1"
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"TaskQueue",
|
|
22
|
+
"TaskRow",
|
|
23
|
+
"QueueStats",
|
|
24
|
+
"SqliteTaskQueue",
|
|
25
|
+
"lanes",
|
|
26
|
+
"check_kart_task",
|
|
27
|
+
"run_shell_task",
|
|
28
|
+
"execute_task_row",
|
|
29
|
+
"drain_claimed_tasks",
|
|
30
|
+
"run_worker",
|
|
31
|
+
"__version__",
|
|
32
|
+
]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"description": "Kartikeya bwrap mount policy (product-neutral default). Override via $KART_SANDBOX_CONFIG or $WILLOW_HOME/kart-sandbox.json to add host/fleet-specific paths without editing code.",
|
|
4
|
+
"env_prefixes": [
|
|
5
|
+
"WILLOW_",
|
|
6
|
+
"PG",
|
|
7
|
+
"POSTGRES",
|
|
8
|
+
"OLLAMA_",
|
|
9
|
+
"GIT_",
|
|
10
|
+
"TWINE_",
|
|
11
|
+
"PYPI_",
|
|
12
|
+
"ANTHROPIC_",
|
|
13
|
+
"OPENROUTER_",
|
|
14
|
+
"GROQ_",
|
|
15
|
+
"GITHUB_",
|
|
16
|
+
"NPM_",
|
|
17
|
+
"NODE_",
|
|
18
|
+
"SSH_AUTH",
|
|
19
|
+
"HUGGINGFACE_",
|
|
20
|
+
"HF_",
|
|
21
|
+
"OPENAI_",
|
|
22
|
+
"AWS_",
|
|
23
|
+
"DBUS_",
|
|
24
|
+
"XDG_"
|
|
25
|
+
],
|
|
26
|
+
"credential_env_prefixes": [
|
|
27
|
+
"TWINE_",
|
|
28
|
+
"PYPI_",
|
|
29
|
+
"ANTHROPIC_",
|
|
30
|
+
"OPENROUTER_",
|
|
31
|
+
"GROQ_",
|
|
32
|
+
"GITHUB_",
|
|
33
|
+
"NPM_",
|
|
34
|
+
"HUGGINGFACE_",
|
|
35
|
+
"HF_",
|
|
36
|
+
"OPENAI_",
|
|
37
|
+
"AWS_"
|
|
38
|
+
],
|
|
39
|
+
"bind_read_only": [
|
|
40
|
+
"/usr",
|
|
41
|
+
"/etc",
|
|
42
|
+
"/sys",
|
|
43
|
+
"{{HOME}}/github",
|
|
44
|
+
"{{HOME}}/.config/git",
|
|
45
|
+
"{{HOME}}/.config/systemd"
|
|
46
|
+
],
|
|
47
|
+
"bind_read_write": [
|
|
48
|
+
"{{WILLOW_ROOT}}",
|
|
49
|
+
"{{HOME}}/.willow",
|
|
50
|
+
"{{HOME}}/.local",
|
|
51
|
+
"/var/run/postgresql"
|
|
52
|
+
],
|
|
53
|
+
"bind_try": [
|
|
54
|
+
"{{WILLOW_ROOT}}/.venv-dev",
|
|
55
|
+
"{{HOME}}/.willow-venv",
|
|
56
|
+
"{{HOME}}/.gitconfig",
|
|
57
|
+
"{{HOME}}/.npmrc",
|
|
58
|
+
"{{HOME}}/.cargo",
|
|
59
|
+
"{{HOME}}/.npm",
|
|
60
|
+
"{{HOME}}/.cache/uv",
|
|
61
|
+
"{{HOME}}/.cache/pip",
|
|
62
|
+
"{{HOME}}/.cache/pre-commit",
|
|
63
|
+
"{{HOME}}/.config/pip",
|
|
64
|
+
"{{HOME}}/.config/pypoetry",
|
|
65
|
+
"{{HOME}}/.local/share/uv",
|
|
66
|
+
"/run/user",
|
|
67
|
+
"/run/media",
|
|
68
|
+
"{{XDG_RUNTIME_DIR}}",
|
|
69
|
+
"/run/systemd/resolve"
|
|
70
|
+
],
|
|
71
|
+
"bind_try_read_only": [
|
|
72
|
+
"/lib32"
|
|
73
|
+
],
|
|
74
|
+
"worktree_scan_roots": [
|
|
75
|
+
"{{WILLOW_ROOT}}/worktrees"
|
|
76
|
+
],
|
|
77
|
+
"rtk_compress": {
|
|
78
|
+
"enabled": false
|
|
79
|
+
},
|
|
80
|
+
"_security_notes": {
|
|
81
|
+
"github_ro": "~/github is read-only by default; WILLOW_ROOT + its worktrees nest read-write on top. Hosts promote additional repos to RW via an override config.",
|
|
82
|
+
"ssh_dropped": "~/.ssh is not bound. SSH git uses SSH_AUTH_SOCK (via /run/user) + known_hosts (ro), both only on allow_net. Private keys never enter the sandbox.",
|
|
83
|
+
"creds_gated": "credential_env_prefixes (and ~/.config/gh, ~/.netrc) are present only on allow_net. A no-network task gets zero credentials.",
|
|
84
|
+
"localhost_tier": "# allow_localhost shares the host network namespace (loopback works) but still strips credentials. Weaker than --unshare-net; prefer over allow_net for local-only work.",
|
|
85
|
+
"tmp_tmpfs": "/tmp + /dev/shm are private tmpfs, not host binds."
|
|
86
|
+
}
|
|
87
|
+
}
|