orc-hestrator-server 2.0.0__py3-none-any.whl

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.
Files changed (43) hide show
  1. orc_hestrator_server-2.0.0.dist-info/METADATA +302 -0
  2. orc_hestrator_server-2.0.0.dist-info/RECORD +43 -0
  3. orc_hestrator_server-2.0.0.dist-info/WHEEL +4 -0
  4. orc_hestrator_server-2.0.0.dist-info/entry_points.txt +9 -0
  5. orc_hestrator_server-2.0.0.dist-info/licenses/LICENSE +21 -0
  6. task_orchestrator/__init__.py +3 -0
  7. task_orchestrator/auth.py +92 -0
  8. task_orchestrator/checkpoints.py +63 -0
  9. task_orchestrator/db.py +324 -0
  10. task_orchestrator/engine/__init__.py +188 -0
  11. task_orchestrator/engine/archive.py +74 -0
  12. task_orchestrator/engine/base.py +163 -0
  13. task_orchestrator/engine/claims.py +189 -0
  14. task_orchestrator/engine/dependencies.py +195 -0
  15. task_orchestrator/engine/items.py +391 -0
  16. task_orchestrator/engine/metrics.py +296 -0
  17. task_orchestrator/engine/notes.py +75 -0
  18. task_orchestrator/engine/portability.py +224 -0
  19. task_orchestrator/engine/presence.py +145 -0
  20. task_orchestrator/engine/queries.py +233 -0
  21. task_orchestrator/engine/transitions.py +292 -0
  22. task_orchestrator/engine/trees.py +106 -0
  23. task_orchestrator/engine/users.py +334 -0
  24. task_orchestrator/engine/workspace_view.py +122 -0
  25. task_orchestrator/onboarding.py +476 -0
  26. task_orchestrator/prompts.py +146 -0
  27. task_orchestrator/schemas.py +184 -0
  28. task_orchestrator/server.py +1135 -0
  29. task_orchestrator/ui/__init__.py +1 -0
  30. task_orchestrator/ui/app.py +567 -0
  31. task_orchestrator/ui/templates/arc_detail.html +179 -0
  32. task_orchestrator/ui/templates/arcs.html +56 -0
  33. task_orchestrator/ui/templates/base.html +18 -0
  34. task_orchestrator/ui/templates/board.html +186 -0
  35. task_orchestrator/ui/templates/partials/card.html +16 -0
  36. task_orchestrator/ui/templates/partials/column.html +8 -0
  37. task_orchestrator/ui/templates/partials/item_detail.html +41 -0
  38. task_orchestrator/ui/templates/partials/tree_node.html +41 -0
  39. task_orchestrator/ui/templates/timeline.html +161 -0
  40. task_orchestrator/web/__init__.py +4 -0
  41. task_orchestrator/web/api.py +410 -0
  42. task_orchestrator/web/graph.py +109 -0
  43. task_orchestrator/workspace.py +111 -0
@@ -0,0 +1,302 @@
1
+ Metadata-Version: 2.5
2
+ Name: orc-hestrator-server
3
+ Version: 2.0.0
4
+ Summary: MCP server for AI agent work management — persistent work item graph for AI agents
5
+ Project-URL: Homepage, https://github.com/filhocf/orc-hestrator
6
+ Project-URL: Repository, https://github.com/filhocf/orc-hestrator
7
+ Author: Claudio Ferreira Filho
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai-agents,mcp,task-management,workflow
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: croniter>=2.0.0
18
+ Requires-Dist: mcp<2,>=1.0.0
19
+ Requires-Dist: pyyaml>=6.0
20
+ Provides-Extra: test
21
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
22
+ Requires-Dist: pytest>=8.0.0; extra == 'test'
23
+ Provides-Extra: ui
24
+ Requires-Dist: fastapi>=0.115.0; extra == 'ui'
25
+ Requires-Dist: jinja2>=3.1.0; extra == 'ui'
26
+ Requires-Dist: python-multipart>=0.0.9; extra == 'ui'
27
+ Requires-Dist: uvicorn[standard]>=0.30.0; extra == 'ui'
28
+ Description-Content-Type: text/markdown
29
+
30
+ <p align="center">
31
+ <img src="docs/assets/orc-logo.png" alt="Orc[hestrator]" width="260">
32
+ </p>
33
+
34
+ <h1 align="center">Orc[hestrator]</h1>
35
+
36
+ <p align="center"><strong>The shared work board for humans and AI agents.</strong><br>
37
+ One source of truth for what is being worked on, by whom, right now — enforced by the server,
38
+ not by the operator's discipline.</p>
39
+
40
+ [![PyPI](https://img.shields.io/pypi/v/orc-hestrator-server)](https://pypi.org/project/orc-hestrator-server/)
41
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
43
+ [![CI](https://github.com/filhocf/orc-hestrator/actions/workflows/ci.yml/badge.svg)](https://github.com/filhocf/orc-hestrator/actions/workflows/ci.yml)
44
+
45
+ Orc[hestrator] is an MCP server that keeps the work of a team of AI agents and their human
46
+ in one place. Agents read and write it over MCP; the human reads and intervenes through a web
47
+ UI. The state lives in a single SQLite database on one host, so switching machines, restarting
48
+ a session, or running three agents in parallel does not fork reality.
49
+
50
+ Distribution: `orc-hestrator-server` on PyPI, commands `orc`, `orc-mcp`, `orc-serve` and `orc-ui`, site [orc-hestrator.dev](https://orc-hestrator.dev).
51
+ Formerly published as `task-orchestrator-py`.
52
+
53
+ ## The problem
54
+
55
+ An AI agent forgets. Context compacts, the session ends, you open a different machine, and the
56
+ agent no longer knows what was done, what is blocked, or what comes next. The usual answers do
57
+ not survive contact with real work:
58
+
59
+ - **Prompt-based reminders** disappear with the context window.
60
+ - **A markdown or JSON task file** has no rules: nothing stops an agent from marking work done
61
+ that never ran, and two agents will happily pick the same task.
62
+ - **A hosted issue tracker** is built for humans typing tickets. Its API can be called by an
63
+ agent, but nothing in it is designed for agents as first-class actors.
64
+
65
+ So you end up being the router: retyping state, reconciling boards, and noticing by accident
66
+ that two machines implemented the same feature.
67
+
68
+ ## What Orc does
69
+
70
+ - **Server-enforced workflow.** Items move `queue → work → review → done` through explicit
71
+ triggers. The server rejects an invalid transition; the client cannot skip a step.
72
+ - **Dependencies that actually block.** Advancement waits for prerequisites, with cycle
73
+ detection and a configurable `unblock_at` threshold (unblock at review, not only at done).
74
+ - **Gates as required notes.** A YAML schema declares which notes a phase requires. Missing
75
+ documentation blocks the transition, so "done" means something.
76
+ - **Presence and claims across machines.** Every session registers as a triple
77
+ `hostname / agent_id / session_id`, heartbeats, and is reaped when it dies. Claims answer
78
+ "who is on this item or file right now" before two agents duplicate the work.
79
+ - **Attribution per request.** `agent_id` is resolved per call
80
+ (argument → `X-Agent-ID` header → `MCP_AGENT_ID` → OAuth client id) and recorded on writes,
81
+ so the board can say who did what.
82
+ - **One database, many clients.** Run it locally over stdio, or as a central HTTP service that
83
+ every machine and every agent points at.
84
+
85
+ ## Who it is for
86
+
87
+ - **A developer running agent CLIs on more than one machine.** Continuity is the whole point:
88
+ the task you started on the laptop is the task the desktop agent picks up.
89
+ - **A small team putting agents into the loop.** Two to ten people, several agents, and a need
90
+ to see who is doing what without a daily sync to rebuild the picture.
91
+ - **Anyone who tried a task file and watched it drift.** If you have ever found two divergent
92
+ copies of the same plan, this is the fix.
93
+
94
+ It is not a runtime orchestrator: Orc does not spawn or supervise agent processes. It is not a
95
+ memory system either: memory stores facts, Orc stores the structure of work. They compose.
96
+
97
+ ## Quick start
98
+
99
+ ### Single machine, stdio
100
+
101
+ ```bash
102
+ uvx orc-hestrator-server
103
+ ```
104
+
105
+ Register it with any MCP client:
106
+
107
+ ```json
108
+ {
109
+ "mcpServers": {
110
+ "orc": {
111
+ "command": "uvx",
112
+ "args": ["orc-hestrator-server"],
113
+ "env": { "ORC_DB": "/path/to/tasks.db" }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ Without `ORC_DB` the database defaults to `~/.orc/tasks.db`. The previous name,
120
+ `TASK_ORCHESTRATOR_DB`, still works as a fallback with a deprecation notice.
121
+
122
+ ### One server, many machines
123
+
124
+ ```bash
125
+ # on the host that owns the database
126
+ MCP_HOST=127.0.0.1 MCP_PORT=8005 \
127
+ ORC_DB=~/.local/share/orc/tasks.db \
128
+ uvx --from 'orc-hestrator-server[ui]' orc-serve
129
+ ```
130
+
131
+ Then point every client at `http://<host>:8005/mcp`. The web UI is served from the same app
132
+ (`/ui/`), and the REST API from `/api`. Put a TLS proxy in front: the UI and API use cookie
133
+ sessions, and an initial admin is seeded from `ORC_ADMIN_USER` / `ORC_ADMIN_PASSWORD` on first
134
+ boot when no user exists.
135
+
136
+ The `[ui]` extra carries `fastapi` + `uvicorn`, which the HTTP transport needs; a plain install
137
+ does not pull it in — hence `--from 'orc-hestrator-server[ui]'`. From a checkout the equivalent
138
+ is `uv sync --extra ui` then `uv run orc-serve`.
139
+
140
+ ## Screens
141
+
142
+ The management UI is a React app (Vite, TypeScript, Tailwind) with nine screens: login,
143
+ overview, live team presence, kanban board, arc tree, dependency graph, task detail,
144
+ users/RBAC, and profile.
145
+
146
+ ![Orc dashboard](frontend/screenshots/orc-dashboard.png)
147
+
148
+ The screen that matters most is **live team presence**: which host, which agent, which session,
149
+ on which item, right now.
150
+
151
+ ## How it compares
152
+
153
+ | | Hosted issue trackers | Agent memory servers | Task file in the repo | Orc |
154
+ |---|---|---|---|---|
155
+ | Designed for agents as primary actors | No | Yes, for facts | No | Yes |
156
+ | Workflow enforced by the server | Partly, configurable | Not applicable | No | Yes |
157
+ | Blocks advancement on unmet dependencies | Sometimes | No | No | Yes |
158
+ | Cross-machine single source of truth | Yes | Usually per host | No, it forks | Yes |
159
+ | Presence and claims ("who is on this") | Human assignee only | No | No | Yes, with heartbeat |
160
+ | Gates that require evidence | Paid tiers, human-driven | No | No | Yes |
161
+ | Runs on your own box, one SQLite file | No | Yes | Yes | Yes |
162
+
163
+ ## The tools you will actually use
164
+
165
+ Thirty-one tools exist; a normal session touches eight of them.
166
+
167
+ | Tool | What it is for |
168
+ |---|---|
169
+ | `get_context` | Dashboard of the current state, or the detail of one item |
170
+ | `get_next_item` | Highest-priority item with no unsatisfied dependency |
171
+ | `manage_items` | Create, update, delete; batch and hierarchy |
172
+ | `create_work_tree` | Root plus children plus dependency wiring in one call |
173
+ | `advance_item` | Move an item through a trigger, with enforcement |
174
+ | `manage_notes` | Phase notes, including the ones gates require |
175
+ | `agent_register` / `agent_heartbeat` / `agent_end` | Presence lifecycle |
176
+ | `claim` / `release` / `conflicts` | Take and free an item or a free-form resource |
177
+
178
+ Full reference: [docs/TOOLS.md](docs/TOOLS.md). Seven MCP prompts ship with the server
179
+ (`session_start`, `work_summary`, `quick_start`, `create_item_from_context`,
180
+ `status_progression`, `dependency_manager`, `batch_complete`).
181
+
182
+ ## Workflow
183
+
184
+ ```
185
+ start start start
186
+ queue ────────→ work ────────→ review ────────→ done
187
+ │ │ │
188
+ │ block │ block │ block
189
+ └──────→ blocked ←──────────────┘
190
+ │
191
+ │ resume
192
+ └──────→ (previous status)
193
+
194
+ done/cancelled ──reopen──→ queue
195
+ ```
196
+
197
+ Triggers: `start`, `complete`, `block`/`hold`, `resume`, `cancel`, `reopen`.
198
+
199
+ Enforcement: dependencies must be satisfied before an item leaves `queue`; note-gate schemas are
200
+ checked on every `start` and `complete`; reopening cascades to the parent when the parent is
201
+ terminal.
202
+
203
+ ## Example
204
+
205
+ ```python
206
+ create_work_tree(
207
+ root_title="User authentication",
208
+ children_json='[
209
+ {"ref": "schema", "title": "Design auth schema", "priority": "high"},
210
+ {"ref": "api", "title": "Implement auth endpoints", "priority": "high"},
211
+ {"ref": "tests", "title": "Write auth tests", "priority": "medium"}
212
+ ]',
213
+ deps_json='[
214
+ {"from": "schema", "to": "api"},
215
+ {"from": "api", "to": "tests"}
216
+ ]'
217
+ )
218
+
219
+ agent_register(cli="hermes") # presence: host/agent/session
220
+ get_next_item() # -> "Design auth schema"
221
+ claim(subject_type="work_item", subject_ref="a1b2", session_id="...")
222
+ advance_item(item_id="a1b2", trigger="start")
223
+ manage_notes(operation="upsert", item_id="a1b2", key="requirements", role="queue",
224
+ body="RS256, refresh tokens, 15 min access expiry")
225
+ advance_item(item_id="a1b2", trigger="complete")
226
+ # -> unblocked_items: ["Implement auth endpoints"]
227
+ agent_end(session_id="...") # releases the claim
228
+ ```
229
+
230
+ ## Architecture
231
+
232
+ A thin MCP layer over a modular domain engine, SQLite with WAL, plus an optional web layer.
233
+
234
+ ```
235
+ src/task_orchestrator/
236
+ ├── server.py MCP tools (FastMCP) and per-request agent identity
237
+ ├── engine/ domain logic, one module per concern:
238
+ │ ├── base, items, transitions, dependencies, notes, queries, trees
239
+ │ ├── metrics, archive, portability, workspace_view
240
+ │ └── presence, claims, users coordination and access
241
+ ├── db.py schema, additive migrations, FTS5
242
+ ├── schemas.py note-gate schemas
243
+ ├── workspace.py workspace configs
244
+ ├── web/ REST API (/api) and graph endpoints
245
+ └── ui/ server-rendered board (FastAPI + HTMX)
246
+ frontend/ React management UI (isolated from the Python tree)
247
+ ```
248
+
249
+ Modules form a strict import DAG and `engine/` re-exports its public surface, so clients keep a
250
+ stable `engine.X` interface. Design decisions are recorded in [docs/adr/](docs/adr/):
251
+ central service, session registry incorporation, single-source-of-truth before hybrid,
252
+ edge security, per-request agent identity.
253
+
254
+ ## Security
255
+
256
+ - `/api/*` requires an authenticated user session except login, logout and `/health`.
257
+ - Passwords use scrypt with a per-user salt; sessions are opaque tokens in an HTTP-only cookie
258
+ with server-side revocation; roles are `admin` and `user`.
259
+ - MCP clients can add a second barrier: OAuth2 resource-server mode (`MCP_OAUTH_ENABLED`) verifies
260
+ opaque pre-shared tokens, behind the reverse proxy's basic auth.
261
+ - The admin user is seeded from `ORC_ADMIN_USER` / `ORC_ADMIN_PASSWORD` on first boot.
262
+
263
+ ## Roadmap
264
+
265
+ Shipped through 1.5.0: workflow engine, dependency graph with cycle detection, note-gate
266
+ schemas, workspaces, metrics and graph analytics, scheduling, archive and checkpoints,
267
+ export/import, central presence and claims, per-request attribution, REST API, user auth with
268
+ RBAC, and the React management UI.
269
+
270
+ Next, in rough order:
271
+
272
+ | Area | Focus |
273
+ |---|---|
274
+ | Event ingestion | Git, CI and webhooks move items instead of a human remembering to |
275
+ | Typed gates | Blocking predicates: CI status, coverage, approver role, no bypass |
276
+ | Work-item types | First-class incident (`detect → mitigate → resolve → postmortem`) and spike flows |
277
+ | Claim leases | TTL on claims so a dead agent does not hold an item forever |
278
+ | Authoring UI | Create and edit items from the browser, not only from MCP |
279
+ | Hybrid mode | Local primary with a central secondary, for offline work |
280
+
281
+ ## Origins
282
+
283
+ Orc started as a Python port of [jpicklyk/task-orchestrator](https://github.com/jpicklyk/task-orchestrator)
284
+ (Kotlin). It has since diverged: tags, batch operations, work trees, short hex ids, stale
285
+ detection, structured errors, graph analytics, central presence, claims, attribution, and the
286
+ web layer are Python-only. The Kotlin project deserves credit for the original design.
287
+
288
+ ## Contributing
289
+
290
+ ```bash
291
+ git clone https://github.com/filhocf/orc-hestrator
292
+ cd orc-hestrator
293
+ uv sync --extra test --extra ui
294
+ uv run pytest tests/ -q
295
+ ```
296
+
297
+ Changes go to a branch and a PR against `main`. Product requirements live in
298
+ [docs/PRD.md](docs/PRD.md); architecture in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
299
+
300
+ ## License
301
+
302
+ [MIT](LICENSE)
@@ -0,0 +1,43 @@
1
+ task_orchestrator/__init__.py,sha256=jPs0aALQshSz4KFQCcu2aTl0J4GurPh3s4J-52P36zM,95
2
+ task_orchestrator/auth.py,sha256=e1Q9CaZ8pFx4wD1i2mcGpf9xiMxFB-hkvHOTxNt7gmo,2967
3
+ task_orchestrator/checkpoints.py,sha256=dq1NaMbyomWeBAgEOlSspaU-m5XB7WHU77Eu5gfoegY,1981
4
+ task_orchestrator/db.py,sha256=8E2Xm9PoD7lAgDnGrjuYX02nY19lYYKt4j6y2XFzGKQ,12219
5
+ task_orchestrator/onboarding.py,sha256=p3Om-aCvGHEwmfeb8YBiw6lmxDyo-RHpmKtnnbx9k3A,19671
6
+ task_orchestrator/prompts.py,sha256=H5kAM4S-UWhUaKeQ7AjdmNqez-jGuMIVLlTWgtfJV-g,5983
7
+ task_orchestrator/schemas.py,sha256=w9v3MIAQ9dVHsxjvULXP6Qcf6ppOutJzTLvfoloDBow,6199
8
+ task_orchestrator/server.py,sha256=o3RNMOKBtgv0KZl5jBWXf0Qaj35bHzD8TLNqdWTjiFE,39790
9
+ task_orchestrator/workspace.py,sha256=Rv8CpN9N9i3rgvKCd7BpKB_Dxr9L-Igek_5-LdtbiIg,2937
10
+ task_orchestrator/engine/__init__.py,sha256=3dESUZCZJKK_EOQrHIiQtMuesaOAP2wDEA-Q1sDKD3g,4156
11
+ task_orchestrator/engine/archive.py,sha256=gk0-YHA-gE2yzotQ_yaiZhsv-meRXsfUa2x0tlDYSGY,2648
12
+ task_orchestrator/engine/base.py,sha256=hoqqiQjNwoincD40XQBfuboiQMBCsA7z17BCli6gAfs,4914
13
+ task_orchestrator/engine/claims.py,sha256=7h6HQWjoQ0LBW4SEGdtaewWg01F1lhZZ25GsSatr3e4,6749
14
+ task_orchestrator/engine/dependencies.py,sha256=E3q1Xm-n_v5lbvFtuWuocwbt1-dl6Of5v_J1f6lEu-A,7059
15
+ task_orchestrator/engine/items.py,sha256=P22fr4Y1ZNT0jvJfRJAlA3dUrXyznMByogCmBwCP_RA,12930
16
+ task_orchestrator/engine/metrics.py,sha256=uxmMVmIBo0r-4bXDGbhG7699xnJ_Jx5n6pJ8aK-cxa8,11588
17
+ task_orchestrator/engine/notes.py,sha256=sQdpjw_rMtI5sICyEk69Chrwm12v_aD-F65cqTwcyCY,2706
18
+ task_orchestrator/engine/portability.py,sha256=nE52VCxH-vRmXSOQht7wo7RujnDpGFbMHEw2wfqFm-I,8169
19
+ task_orchestrator/engine/presence.py,sha256=ZldTXqOD2U7wh1WOH3HTLp6Dwk1oFHt_kEtZnYScyEY,4995
20
+ task_orchestrator/engine/queries.py,sha256=KuT-dgm0dVreYRjA0sKfZ3Ape3j9dpVKwwbDpRLnCIA,8693
21
+ task_orchestrator/engine/transitions.py,sha256=OXCkxRV1GxO_1mO6Nqa_-LcdFioGO0xVCuR8J4ortt4,10927
22
+ task_orchestrator/engine/trees.py,sha256=6uF_QgNHKtig15NeYTlOShcBEPwoa6CPOK8ZJ5PR_fI,3526
23
+ task_orchestrator/engine/users.py,sha256=aufkO2uWhAgwFnsqUfrS5n8CQ8WXl0B0OnXqVowJn_0,11227
24
+ task_orchestrator/engine/workspace_view.py,sha256=v8mO38dEWFfKJxUvxdNuHz8sOCGD5Ctj2YLkq_SkpZ0,4557
25
+ task_orchestrator/ui/__init__.py,sha256=-_32roXmM7or5RlP-MPfTlT51608bsDMlUJavBeJmPU,43
26
+ task_orchestrator/ui/app.py,sha256=kPT7PTbQY9CwMXCSJD-YVrWO943_pL-rj0in0aeoEB8,18943
27
+ task_orchestrator/ui/templates/arc_detail.html,sha256=AIxwcRObQnKi1aaDxBlDV-mcoGWrZvFN4F_7ynQA_KE,8232
28
+ task_orchestrator/ui/templates/arcs.html,sha256=nCV4dyehcSFvN3lrECarTTx1f3JDoCsIK5_8JnORl90,2631
29
+ task_orchestrator/ui/templates/base.html,sha256=QC9lWoM5tO1t7w36nlfgBrFvksgFYuC-yjxoSodc5No,657
30
+ task_orchestrator/ui/templates/board.html,sha256=yK4ut5CzFrxWuYX4GVJH4-JpWEPHzM2-3E9mZNEjOE0,9013
31
+ task_orchestrator/ui/templates/timeline.html,sha256=8-RFcMCzwCvN34KDX12AGi4CVSUIJsD9W8gd0ZjtjGw,6085
32
+ task_orchestrator/ui/templates/partials/card.html,sha256=koHu3nP44x32Z45LH5TIVymWUeg71lgPtLGFcCBg5qo,801
33
+ task_orchestrator/ui/templates/partials/column.html,sha256=-8mA1dZRJl1E8K3UyQFklNmi2HG8BDGpYtDMZCdzgdQ,300
34
+ task_orchestrator/ui/templates/partials/item_detail.html,sha256=kai5Vgy2z6blqks14WOgEDy1A_qVkP5I_S9ByeEMMX4,1727
35
+ task_orchestrator/ui/templates/partials/tree_node.html,sha256=HikdrkpvjecjBx3oCmgEfWwgDgn-rbRUlXKrDryA6Fw,2160
36
+ task_orchestrator/web/__init__.py,sha256=fmXEU1h-eE8A9uhJorfHAF9oS8t2bBhisQTVzkipuXQ,178
37
+ task_orchestrator/web/api.py,sha256=0Onpmewsq3Yj93Nkxyj1UGzO2-UtrEcJI2bEAwRftDw,16039
38
+ task_orchestrator/web/graph.py,sha256=VVEx1uxKVeGmQo__doykthI0l5HkQY7xhRNhfxl7HoM,3981
39
+ orc_hestrator_server-2.0.0.dist-info/METADATA,sha256=NbDSZXc4eSc3DuZ2NN2l7HKqgxnWLVr1xIl23LE2IcU,13387
40
+ orc_hestrator_server-2.0.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
41
+ orc_hestrator_server-2.0.0.dist-info/entry_points.txt,sha256=hUC7tIDTOCkKwfnop5qCKrtyz3nvPYYkoPwVDBeIfPs,399
42
+ orc_hestrator_server-2.0.0.dist-info/licenses/LICENSE,sha256=zYn23Qf0BnHSrScYg5N_5UKnPZ_3niJaq5eGAE7TKXo,1079
43
+ orc_hestrator_server-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,9 @@
1
+ [console_scripts]
2
+ orc = task_orchestrator.server:main
3
+ orc-hestrator = task_orchestrator.server:main
4
+ orc-hestrator-server = task_orchestrator.server:main
5
+ orc-mcp = task_orchestrator.server:main
6
+ orc-serve = task_orchestrator.server:main_http
7
+ orc-ui = task_orchestrator.ui.app:main
8
+ task-orchestrator-py = task_orchestrator.server:main_legacy
9
+ task-orchestrator-ui = task_orchestrator.ui.app:main_legacy
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Claudio Ferreira Filho
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.
@@ -0,0 +1,3 @@
1
+ """orc-hestrator-server — MCP server for AI agent work management."""
2
+
3
+ __version__ = "0.8.0"
@@ -0,0 +1,92 @@
1
+ """OAuth2 resource-server authentication with opaque pre-shared tokens.
2
+
3
+ Implements TokenVerifier interface for MCP FastMCP auth.
4
+ """
5
+
6
+ import hmac
7
+ import os
8
+
9
+ from mcp.server.auth.provider import AccessToken, TokenVerifier
10
+ from mcp.server.auth.settings import AuthSettings
11
+
12
+
13
+ class OpaqueTokenVerifier:
14
+ """Token verifier for opaque pre-shared tokens."""
15
+
16
+ def __init__(self, persona_secrets: dict[str, str] | None = None) -> None:
17
+ """Initialize with persona->secret mapping.
18
+
19
+ Args:
20
+ persona_secrets: Dict of persona names to their secrets.
21
+ If None, will read from MCP_OAUTH_TOKENS env var.
22
+ """
23
+ if persona_secrets is None:
24
+ self.persona_secrets = self._load_tokens_from_env()
25
+ else:
26
+ self.persona_secrets = persona_secrets
27
+
28
+ def _load_tokens_from_env(self) -> dict[str, str]:
29
+ """Load persona->secret mapping from MCP_OAUTH_TOKENS environment variable.
30
+
31
+ Expected format: "persona1:secret1,persona2:secret2"
32
+ """
33
+ tokens_env = os.environ.get("MCP_OAUTH_TOKENS", "")
34
+ if not tokens_env:
35
+ return {}
36
+
37
+ result = {}
38
+ for pair in tokens_env.split(","):
39
+ if ":" not in pair:
40
+ continue
41
+ persona, secret = pair.split(":", 1)
42
+ result[persona.strip()] = secret.strip()
43
+ return result
44
+
45
+ async def verify_token(self, token: str) -> AccessToken | None:
46
+ """Verify bearer token against configured secrets.
47
+
48
+ Args:
49
+ token: Bearer token to verify
50
+
51
+ Returns:
52
+ AccessToken if valid, None if invalid/empty
53
+ """
54
+ if not token:
55
+ return None
56
+
57
+ # Check against all configured secrets using constant-time comparison
58
+ for persona, secret in self.persona_secrets.items():
59
+ if hmac.compare_digest(token, secret):
60
+ return AccessToken(
61
+ token=token, client_id=persona, scopes=["orc"], expires_at=None
62
+ )
63
+
64
+ return None
65
+
66
+
67
+ def build_auth() -> tuple[TokenVerifier | None, AuthSettings | None]:
68
+ """Build auth configuration from environment variables.
69
+
70
+ Returns:
71
+ Tuple of (TokenVerifier, AuthSettings) if OAuth is enabled,
72
+ (None, None) if disabled.
73
+ """
74
+ # Check if OAuth is enabled
75
+ oauth_enabled = os.environ.get("MCP_OAUTH_ENABLED", "").lower()
76
+ if oauth_enabled not in ("true", "1", "yes"):
77
+ return (None, None)
78
+
79
+ # Create token verifier
80
+ verifier = OpaqueTokenVerifier()
81
+
82
+ # Build AuthSettings with required fields
83
+ issuer_url = os.environ.get("MCP_OAUTH_ISSUER", "https://cfnarede.dev/orc")
84
+ resource_server_url = os.environ.get("MCP_OAUTH_RESOURCE_SERVER_URL", issuer_url)
85
+
86
+ auth_settings = AuthSettings(
87
+ issuer_url=issuer_url,
88
+ resource_server_url=resource_server_url,
89
+ required_scopes=["orc"],
90
+ )
91
+
92
+ return (verifier, auth_settings)
@@ -0,0 +1,63 @@
1
+ """Checkpoint management — export snapshots and DB integrity verification."""
2
+
3
+ import json
4
+ import os
5
+ import sqlite3
6
+ from datetime import UTC, datetime
7
+ from pathlib import Path
8
+
9
+ from . import db as _db
10
+ from . import engine
11
+
12
+
13
+ def _default_output_dir() -> str:
14
+ """Default checkpoint dir: same directory as the SQLite DB file."""
15
+ return str(Path(_db.DB_PATH).parent)
16
+
17
+
18
+ def create_checkpoint(output_dir: str | None = None) -> str:
19
+ """Export graph and save to checkpoint-{ISO-timestamp}.json. Returns file path."""
20
+ out = output_dir or _default_output_dir()
21
+ os.makedirs(out, exist_ok=True)
22
+ data = engine.export_graph()
23
+ ts = datetime.now(UTC).strftime("%Y%m%dT%H%M%S%fZ")
24
+ filename = f"checkpoint-{ts}.json"
25
+ filepath = os.path.join(out, filename)
26
+ with open(filepath, "w") as f:
27
+ json.dump(data, f, default=str)
28
+ return filepath
29
+
30
+
31
+ def list_checkpoints(output_dir: str | None = None) -> list[str]:
32
+ """List .json checkpoint files in output dir sorted by date (newest first)."""
33
+ out = output_dir or _default_output_dir()
34
+ if not os.path.isdir(out):
35
+ return []
36
+ files = sorted(
37
+ (
38
+ os.path.join(out, name)
39
+ for name in os.listdir(out)
40
+ if name.startswith("checkpoint-") and name.endswith(".json")
41
+ ),
42
+ reverse=True,
43
+ )
44
+ return files
45
+
46
+
47
+ def restore_checkpoint(path: str) -> bool:
48
+ """Restore graph from a checkpoint file using import_graph(replace). Returns True on success."""
49
+ with open(path) as f:
50
+ data = json.load(f)
51
+ engine.import_graph(data, mode="replace")
52
+ return True
53
+
54
+
55
+ def verify_db_integrity() -> bool:
56
+ """Run PRAGMA integrity_check on the DB. Returns True if healthy."""
57
+ try:
58
+ conn = sqlite3.connect(_db.DB_PATH)
59
+ result = conn.execute("PRAGMA integrity_check").fetchone()
60
+ conn.close()
61
+ return result[0] == "ok"
62
+ except (sqlite3.DatabaseError, sqlite3.OperationalError):
63
+ return False