yoru-cli 0.1.0__tar.gz → 0.1.2__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.
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/.gitignore +63 -62
- yoru_cli-0.1.2/PKG-INFO +76 -0
- yoru_cli-0.1.2/README.md +50 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/pyproject.toml +44 -44
- yoru_cli-0.1.2/src/yoru_cli/__init__.py +6 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/api.py +40 -40
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/cli.py +84 -84
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/doctor_cmd.py +92 -92
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/hook_template.py +119 -119
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/init_cmd.py +183 -183
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/tests/test_doctor.py +18 -18
- yoru_cli-0.1.0/.claude/settings.local.json +0 -6
- yoru_cli-0.1.0/PKG-INFO +0 -61
- yoru_cli-0.1.0/README.md +0 -35
- yoru_cli-0.1.0/src/yoru_cli/__init__.py +0 -1
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/LICENSE +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/__main__.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/config.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/tail_cmd.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/src/yoru_cli/transcript_tailer.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/tests/__init__.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/tests/test_cli.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/tests/test_hook_template.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/tests/test_init.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/tests/test_init_settings_merge.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/tests/test_tail.py +0 -0
- {yoru_cli-0.1.0 → yoru_cli-0.1.2}/uv.lock +0 -0
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
# overnight-saas root .gitignore
|
|
2
|
-
|
|
3
|
-
# Python
|
|
4
|
-
__pycache__/
|
|
5
|
-
*.py[cod]
|
|
6
|
-
*.pyc
|
|
7
|
-
*.pyo
|
|
8
|
-
*.egg-info/
|
|
9
|
-
.Python
|
|
10
|
-
.venv/
|
|
11
|
-
venv/
|
|
12
|
-
env/
|
|
13
|
-
.pytest_cache/
|
|
14
|
-
.ruff_cache/
|
|
15
|
-
.mypy_cache/
|
|
16
|
-
.coverage
|
|
17
|
-
htmlcov/
|
|
18
|
-
*.cover
|
|
19
|
-
|
|
20
|
-
# Node / frontend
|
|
21
|
-
node_modules/
|
|
22
|
-
dist/
|
|
23
|
-
build/
|
|
24
|
-
.vite/
|
|
25
|
-
*.log
|
|
26
|
-
npm-debug.log*
|
|
27
|
-
yarn-debug.log*
|
|
28
|
-
yarn-error.log*
|
|
29
|
-
|
|
30
|
-
# uv / pip
|
|
31
|
-
uv.lock.tmp
|
|
32
|
-
pip-wheel-metadata/
|
|
33
|
-
|
|
34
|
-
# Env / secrets (NEVER commit)
|
|
35
|
-
.env
|
|
36
|
-
.env.*
|
|
37
|
-
!.env.example
|
|
38
|
-
!.env.template
|
|
39
|
-
|
|
40
|
-
# Editors / OS
|
|
41
|
-
.DS_Store
|
|
42
|
-
Thumbs.db
|
|
43
|
-
.idea/
|
|
44
|
-
.vscode/
|
|
45
|
-
*.swp
|
|
46
|
-
*.swo
|
|
47
|
-
|
|
48
|
-
# Docker local data
|
|
49
|
-
docker/.data/
|
|
50
|
-
|
|
51
|
-
# Receipt v0 local SQLite
|
|
52
|
-
backend/data/*.db
|
|
53
|
-
backend/data/*.db-*
|
|
54
|
-
backend/data/backups/*.db
|
|
55
|
-
backend/data/backups/*.db-*
|
|
56
|
-
|
|
57
|
-
# Agent / vault ephemeral
|
|
58
|
-
vault/STOP_NIGHT.off
|
|
59
|
-
vault/*.off
|
|
60
|
-
|
|
61
|
-
# Agent worktrees
|
|
62
|
-
.worktrees/
|
|
1
|
+
# overnight-saas root .gitignore
|
|
2
|
+
|
|
3
|
+
# Python
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*.pyc
|
|
7
|
+
*.pyo
|
|
8
|
+
*.egg-info/
|
|
9
|
+
.Python
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
*.cover
|
|
19
|
+
|
|
20
|
+
# Node / frontend
|
|
21
|
+
node_modules/
|
|
22
|
+
dist/
|
|
23
|
+
build/
|
|
24
|
+
.vite/
|
|
25
|
+
*.log
|
|
26
|
+
npm-debug.log*
|
|
27
|
+
yarn-debug.log*
|
|
28
|
+
yarn-error.log*
|
|
29
|
+
|
|
30
|
+
# uv / pip
|
|
31
|
+
uv.lock.tmp
|
|
32
|
+
pip-wheel-metadata/
|
|
33
|
+
|
|
34
|
+
# Env / secrets (NEVER commit)
|
|
35
|
+
.env
|
|
36
|
+
.env.*
|
|
37
|
+
!.env.example
|
|
38
|
+
!.env.template
|
|
39
|
+
|
|
40
|
+
# Editors / OS
|
|
41
|
+
.DS_Store
|
|
42
|
+
Thumbs.db
|
|
43
|
+
.idea/
|
|
44
|
+
.vscode/
|
|
45
|
+
*.swp
|
|
46
|
+
*.swo
|
|
47
|
+
|
|
48
|
+
# Docker local data
|
|
49
|
+
docker/.data/
|
|
50
|
+
|
|
51
|
+
# Receipt v0 local SQLite
|
|
52
|
+
backend/data/*.db
|
|
53
|
+
backend/data/*.db-*
|
|
54
|
+
backend/data/backups/*.db
|
|
55
|
+
backend/data/backups/*.db-*
|
|
56
|
+
|
|
57
|
+
# Agent / vault ephemeral
|
|
58
|
+
vault/STOP_NIGHT.off
|
|
59
|
+
vault/*.off
|
|
60
|
+
|
|
61
|
+
# Agent worktrees
|
|
62
|
+
.worktrees/
|
|
63
63
|
|
|
64
64
|
# Skill / agent working files — not code
|
|
65
65
|
.agents/
|
|
@@ -69,3 +69,4 @@ backend/receipt.db
|
|
|
69
69
|
|
|
70
70
|
# Runtime caches populated by pricing warmup etc.
|
|
71
71
|
backend/data/*.json
|
|
72
|
+
.saasforge
|
yoru_cli-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yoru-cli
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Yoru — audit-grade session receipts for autonomous AI coding agents.
|
|
5
|
+
Project-URL: Homepage, https://yoru.sh
|
|
6
|
+
Project-URL: Documentation, https://yoru.sh/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/yoru-sh/cli
|
|
8
|
+
Project-URL: Issues, https://github.com/yoru-sh/cli/issues
|
|
9
|
+
Author-email: Yoru authors <hello@yoru.sh>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai-agents,aider,audit,claude-code,cursor,observability
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: httpx>=0.27
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# yoru-cli
|
|
28
|
+
|
|
29
|
+
The Claude Code hook installer for Yoru. One command installs a `PostToolUse` hook that forwards every tool call, file edit, and red-flag event to a Yoru backend. Your next session shows up in the dashboard automatically.
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/yoru-cli/)
|
|
32
|
+
[](./LICENSE)
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install yoru-cli
|
|
38
|
+
yoru init
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`yoru init` opens a pairing URL in your browser, writes `~/.claude/hooks/yoru.sh`, and registers the hook in `~/.claude/settings.json`. By default it points at Yoru Cloud ([yoru.sh](https://yoru.sh)) — free forever for one developer.
|
|
42
|
+
|
|
43
|
+
Requires Python 3.10+. One runtime dep: `httpx`.
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
yoru init # pair against https://api.yoru.sh
|
|
49
|
+
yoru init --server https://yoru.acme # pair against your self-hosted server
|
|
50
|
+
yoru init --token yoru_xxx --force # non-interactive (CI / scripted rotation)
|
|
51
|
+
|
|
52
|
+
yoru tail < events.jsonl # post a batch of events (debug)
|
|
53
|
+
|
|
54
|
+
yoru --version
|
|
55
|
+
yoru --help
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Exit codes: `0` ok · `1` already installed (use `--force`) · `2` auth failed · `3` 4xx · `4` 5xx/network.
|
|
59
|
+
|
|
60
|
+
## How the hook works
|
|
61
|
+
|
|
62
|
+
The installed script is ~20 lines of Bash that `POST`s one JSON event per Claude Code tool call:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{"session_id": "…", "user": "…", "kind": "tool_use", "tool": "Bash", "content": "…"}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
It uses `curl --max-time 2 || true` so a Yoru outage never stalls your terminal — Claude Code doesn't notice the hook is even there.
|
|
69
|
+
|
|
70
|
+
## Self-host the server
|
|
71
|
+
|
|
72
|
+
The CLI is MIT. The server (backend + dashboard) is AGPL-3.0 and lives at **[github.com/yoru-sh/yoru](https://github.com/yoru-sh/yoru)**. `docker-compose up`, then `yoru init --server https://your-host` — done.
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
[MIT](./LICENSE) · Copyright (c) 2026 Yoru authors. Issues and PRs welcome at [github.com/yoru-sh/cli](https://github.com/yoru-sh/cli).
|
yoru_cli-0.1.2/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# yoru-cli
|
|
2
|
+
|
|
3
|
+
The Claude Code hook installer for Yoru. One command installs a `PostToolUse` hook that forwards every tool call, file edit, and red-flag event to a Yoru backend. Your next session shows up in the dashboard automatically.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/yoru-cli/)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install yoru-cli
|
|
12
|
+
yoru init
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`yoru init` opens a pairing URL in your browser, writes `~/.claude/hooks/yoru.sh`, and registers the hook in `~/.claude/settings.json`. By default it points at Yoru Cloud ([yoru.sh](https://yoru.sh)) — free forever for one developer.
|
|
16
|
+
|
|
17
|
+
Requires Python 3.10+. One runtime dep: `httpx`.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yoru init # pair against https://api.yoru.sh
|
|
23
|
+
yoru init --server https://yoru.acme # pair against your self-hosted server
|
|
24
|
+
yoru init --token yoru_xxx --force # non-interactive (CI / scripted rotation)
|
|
25
|
+
|
|
26
|
+
yoru tail < events.jsonl # post a batch of events (debug)
|
|
27
|
+
|
|
28
|
+
yoru --version
|
|
29
|
+
yoru --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Exit codes: `0` ok · `1` already installed (use `--force`) · `2` auth failed · `3` 4xx · `4` 5xx/network.
|
|
33
|
+
|
|
34
|
+
## How the hook works
|
|
35
|
+
|
|
36
|
+
The installed script is ~20 lines of Bash that `POST`s one JSON event per Claude Code tool call:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{"session_id": "…", "user": "…", "kind": "tool_use", "tool": "Bash", "content": "…"}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
It uses `curl --max-time 2 || true` so a Yoru outage never stalls your terminal — Claude Code doesn't notice the hook is even there.
|
|
43
|
+
|
|
44
|
+
## Self-host the server
|
|
45
|
+
|
|
46
|
+
The CLI is MIT. The server (backend + dashboard) is AGPL-3.0 and lives at **[github.com/yoru-sh/yoru](https://github.com/yoru-sh/yoru)**. `docker-compose up`, then `yoru init --server https://your-host` — done.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
[MIT](./LICENSE) · Copyright (c) 2026 Yoru authors. Issues and PRs welcome at [github.com/yoru-sh/cli](https://github.com/yoru-sh/cli).
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "yoru-cli"
|
|
3
|
-
version = "0.1.
|
|
4
|
-
description = "Yoru — audit-grade session receipts for autonomous AI coding agents."
|
|
5
|
-
license = { text = "MIT" }
|
|
6
|
-
license-files = ["LICENSE"]
|
|
7
|
-
readme = "README.md"
|
|
8
|
-
authors = [{ name = "Yoru authors", email = "hello@
|
|
9
|
-
keywords = ["claude-code", "cursor", "aider", "ai-agents", "observability", "audit"]
|
|
10
|
-
classifiers = [
|
|
11
|
-
"License :: OSI Approved :: MIT License",
|
|
12
|
-
"Programming Language :: Python :: 3",
|
|
13
|
-
"Programming Language :: Python :: 3.10",
|
|
14
|
-
"Programming Language :: Python :: 3.11",
|
|
15
|
-
"Programming Language :: Python :: 3.12",
|
|
16
|
-
"Environment :: Console",
|
|
17
|
-
"Intended Audience :: Developers",
|
|
18
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
19
|
-
]
|
|
20
|
-
requires-python = ">=3.10"
|
|
21
|
-
dependencies = ["httpx>=0.27"]
|
|
22
|
-
|
|
23
|
-
[project.urls]
|
|
24
|
-
Homepage = "https://yoru.sh"
|
|
25
|
-
Documentation = "https://yoru.sh/docs"
|
|
26
|
-
Repository = "https://github.com/
|
|
27
|
-
Issues = "https://github.com/
|
|
28
|
-
|
|
29
|
-
[project.scripts]
|
|
30
|
-
yoru = "yoru_cli.cli:main"
|
|
31
|
-
|
|
32
|
-
[project.optional-dependencies]
|
|
33
|
-
dev = ["pytest>=7"]
|
|
34
|
-
|
|
35
|
-
[build-system]
|
|
36
|
-
requires = ["hatchling"]
|
|
37
|
-
build-backend = "hatchling.build"
|
|
38
|
-
|
|
39
|
-
[tool.hatch.build.targets.wheel]
|
|
40
|
-
packages = ["src/yoru_cli"]
|
|
41
|
-
|
|
42
|
-
[tool.pytest.ini_options]
|
|
43
|
-
pythonpath = ["src"]
|
|
44
|
-
testpaths = ["tests"]
|
|
1
|
+
[project]
|
|
2
|
+
name = "yoru-cli"
|
|
3
|
+
version = "0.1.2"
|
|
4
|
+
description = "Yoru — audit-grade session receipts for autonomous AI coding agents."
|
|
5
|
+
license = { text = "MIT" }
|
|
6
|
+
license-files = ["LICENSE"]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
authors = [{ name = "Yoru authors", email = "hello@yoru.sh" }]
|
|
9
|
+
keywords = ["claude-code", "cursor", "aider", "ai-agents", "observability", "audit"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.10",
|
|
14
|
+
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
19
|
+
]
|
|
20
|
+
requires-python = ">=3.10"
|
|
21
|
+
dependencies = ["httpx>=0.27"]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://yoru.sh"
|
|
25
|
+
Documentation = "https://yoru.sh/docs"
|
|
26
|
+
Repository = "https://github.com/yoru-sh/cli"
|
|
27
|
+
Issues = "https://github.com/yoru-sh/cli/issues"
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
yoru = "yoru_cli.cli:main"
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
dev = ["pytest>=7"]
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["hatchling"]
|
|
37
|
+
build-backend = "hatchling.build"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/yoru_cli"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
pythonpath = ["src"]
|
|
44
|
+
testpaths = ["tests"]
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
import httpx
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ReceiptClient:
|
|
9
|
-
def __init__(self, base_url: str, token: str | None = None) -> None:
|
|
10
|
-
self.base_url = base_url.rstrip("/")
|
|
11
|
-
self.token = token
|
|
12
|
-
|
|
13
|
-
def start_device_code(self, label: str | None = None) -> dict[str, Any]:
|
|
14
|
-
"""Begin the device-pairing handshake — no auth needed."""
|
|
15
|
-
r = httpx.post(
|
|
16
|
-
f"{self.base_url}/api/v1/auth/device-code",
|
|
17
|
-
json={"label": label} if label else {},
|
|
18
|
-
timeout=5.0,
|
|
19
|
-
)
|
|
20
|
-
r.raise_for_status()
|
|
21
|
-
return r.json()
|
|
22
|
-
|
|
23
|
-
def poll_device_code(self, device_code: str) -> dict[str, Any]:
|
|
24
|
-
"""Poll for approval — returns {status, token?}."""
|
|
25
|
-
r = httpx.post(
|
|
26
|
-
f"{self.base_url}/api/v1/auth/device-code/poll",
|
|
27
|
-
json={"device_code": device_code},
|
|
28
|
-
timeout=10.0,
|
|
29
|
-
)
|
|
30
|
-
r.raise_for_status()
|
|
31
|
-
return r.json()
|
|
32
|
-
|
|
33
|
-
def post_events(self, events: list[dict[str, Any]]) -> httpx.Response:
|
|
34
|
-
headers = {"Authorization": f"Bearer {self.token}"} if self.token else {}
|
|
35
|
-
return httpx.post(
|
|
36
|
-
f"{self.base_url}/api/v1/sessions/events",
|
|
37
|
-
json={"events": events},
|
|
38
|
-
headers=headers,
|
|
39
|
-
timeout=5.0,
|
|
40
|
-
)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReceiptClient:
|
|
9
|
+
def __init__(self, base_url: str, token: str | None = None) -> None:
|
|
10
|
+
self.base_url = base_url.rstrip("/")
|
|
11
|
+
self.token = token
|
|
12
|
+
|
|
13
|
+
def start_device_code(self, label: str | None = None) -> dict[str, Any]:
|
|
14
|
+
"""Begin the device-pairing handshake — no auth needed."""
|
|
15
|
+
r = httpx.post(
|
|
16
|
+
f"{self.base_url}/api/v1/auth/device-code",
|
|
17
|
+
json={"label": label} if label else {},
|
|
18
|
+
timeout=5.0,
|
|
19
|
+
)
|
|
20
|
+
r.raise_for_status()
|
|
21
|
+
return r.json()
|
|
22
|
+
|
|
23
|
+
def poll_device_code(self, device_code: str) -> dict[str, Any]:
|
|
24
|
+
"""Poll for approval — returns {status, token?}."""
|
|
25
|
+
r = httpx.post(
|
|
26
|
+
f"{self.base_url}/api/v1/auth/device-code/poll",
|
|
27
|
+
json={"device_code": device_code},
|
|
28
|
+
timeout=10.0,
|
|
29
|
+
)
|
|
30
|
+
r.raise_for_status()
|
|
31
|
+
return r.json()
|
|
32
|
+
|
|
33
|
+
def post_events(self, events: list[dict[str, Any]]) -> httpx.Response:
|
|
34
|
+
headers = {"Authorization": f"Bearer {self.token}"} if self.token else {}
|
|
35
|
+
return httpx.post(
|
|
36
|
+
f"{self.base_url}/api/v1/sessions/events",
|
|
37
|
+
json={"events": events},
|
|
38
|
+
headers=headers,
|
|
39
|
+
timeout=5.0,
|
|
40
|
+
)
|
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import argparse
|
|
4
|
-
|
|
5
|
-
from . import __version__
|
|
6
|
-
from . import config, doctor_cmd, init_cmd, tail_cmd
|
|
7
|
-
|
|
8
|
-
DEFAULT_SERVER = "https://api.yoru.sh"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _build_parser() -> argparse.ArgumentParser:
|
|
12
|
-
parser = argparse.ArgumentParser(
|
|
13
|
-
prog="yoru",
|
|
14
|
-
description="Yoru — audit-grade session receipts for autonomous AI coding agents.",
|
|
15
|
-
)
|
|
16
|
-
parser.add_argument(
|
|
17
|
-
"--version",
|
|
18
|
-
action="version",
|
|
19
|
-
version=f"yoru {__version__}",
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
subparsers = parser.add_subparsers(dest="cmd", required=True, metavar="{init,tail,doctor}")
|
|
23
|
-
|
|
24
|
-
p_init = subparsers.add_parser(
|
|
25
|
-
"init",
|
|
26
|
-
help="Install the Claude Code hook and write ~/.config/yoru/config.json.",
|
|
27
|
-
)
|
|
28
|
-
p_init.add_argument("--server", default=DEFAULT_SERVER, help=f"Backend URL (default: {DEFAULT_SERVER})")
|
|
29
|
-
p_init.add_argument(
|
|
30
|
-
"--token",
|
|
31
|
-
default=None,
|
|
32
|
-
help="Pre-minted hook token (rcpt_...) — for headless/CI/server setups. "
|
|
33
|
-
"Also read from $YORU_TOKEN. Without this, yoru init launches "
|
|
34
|
-
"interactive device pairing.",
|
|
35
|
-
)
|
|
36
|
-
p_init.add_argument(
|
|
37
|
-
"--label",
|
|
38
|
-
default=None,
|
|
39
|
-
help="Human-readable machine label shown in the dashboard "
|
|
40
|
-
"(default: <hostname> · <os>).",
|
|
41
|
-
)
|
|
42
|
-
p_init.add_argument(
|
|
43
|
-
"--no-browser",
|
|
44
|
-
action="store_true",
|
|
45
|
-
help="Don't try to auto-open the pairing URL in a browser.",
|
|
46
|
-
)
|
|
47
|
-
p_init.add_argument("--force", action="store_true", help="Overwrite an existing install.")
|
|
48
|
-
|
|
49
|
-
p_tail = subparsers.add_parser(
|
|
50
|
-
"tail",
|
|
51
|
-
help="Read JSON events from stdin and POST them as a batch (dev/debug).",
|
|
52
|
-
)
|
|
53
|
-
p_tail.add_argument(
|
|
54
|
-
"--server",
|
|
55
|
-
default=None,
|
|
56
|
-
help=f"Backend URL (default: value from config, else {DEFAULT_SERVER}).",
|
|
57
|
-
)
|
|
58
|
-
p_tail.add_argument("--session-id", default=None, help="Session id to stamp on events missing one.")
|
|
59
|
-
|
|
60
|
-
subparsers.add_parser(
|
|
61
|
-
"doctor",
|
|
62
|
-
help="Diagnose the install: config, backend, token, hook.",
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
return parser
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def main(argv: list[str] | None = None) -> int:
|
|
69
|
-
parser = _build_parser()
|
|
70
|
-
args = parser.parse_args(argv)
|
|
71
|
-
|
|
72
|
-
if args.cmd == "tail" and args.server is None:
|
|
73
|
-
cfg = config.load() or {}
|
|
74
|
-
args.server = cfg.get("server", DEFAULT_SERVER)
|
|
75
|
-
|
|
76
|
-
if args.cmd == "init":
|
|
77
|
-
return init_cmd.run(args)
|
|
78
|
-
if args.cmd == "tail":
|
|
79
|
-
return tail_cmd.run(args)
|
|
80
|
-
if args.cmd == "doctor":
|
|
81
|
-
return doctor_cmd.run(args)
|
|
82
|
-
|
|
83
|
-
parser.error(f"unknown command: {args.cmd!r}")
|
|
84
|
-
return 2
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from . import __version__
|
|
6
|
+
from . import config, doctor_cmd, init_cmd, tail_cmd
|
|
7
|
+
|
|
8
|
+
DEFAULT_SERVER = "https://api.yoru.sh"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
12
|
+
parser = argparse.ArgumentParser(
|
|
13
|
+
prog="yoru",
|
|
14
|
+
description="Yoru — audit-grade session receipts for autonomous AI coding agents.",
|
|
15
|
+
)
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"--version",
|
|
18
|
+
action="version",
|
|
19
|
+
version=f"yoru {__version__}",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
subparsers = parser.add_subparsers(dest="cmd", required=True, metavar="{init,tail,doctor}")
|
|
23
|
+
|
|
24
|
+
p_init = subparsers.add_parser(
|
|
25
|
+
"init",
|
|
26
|
+
help="Install the Claude Code hook and write ~/.config/yoru/config.json.",
|
|
27
|
+
)
|
|
28
|
+
p_init.add_argument("--server", default=DEFAULT_SERVER, help=f"Backend URL (default: {DEFAULT_SERVER})")
|
|
29
|
+
p_init.add_argument(
|
|
30
|
+
"--token",
|
|
31
|
+
default=None,
|
|
32
|
+
help="Pre-minted hook token (rcpt_...) — for headless/CI/server setups. "
|
|
33
|
+
"Also read from $YORU_TOKEN. Without this, yoru init launches "
|
|
34
|
+
"interactive device pairing.",
|
|
35
|
+
)
|
|
36
|
+
p_init.add_argument(
|
|
37
|
+
"--label",
|
|
38
|
+
default=None,
|
|
39
|
+
help="Human-readable machine label shown in the dashboard "
|
|
40
|
+
"(default: <hostname> · <os>).",
|
|
41
|
+
)
|
|
42
|
+
p_init.add_argument(
|
|
43
|
+
"--no-browser",
|
|
44
|
+
action="store_true",
|
|
45
|
+
help="Don't try to auto-open the pairing URL in a browser.",
|
|
46
|
+
)
|
|
47
|
+
p_init.add_argument("--force", action="store_true", help="Overwrite an existing install.")
|
|
48
|
+
|
|
49
|
+
p_tail = subparsers.add_parser(
|
|
50
|
+
"tail",
|
|
51
|
+
help="Read JSON events from stdin and POST them as a batch (dev/debug).",
|
|
52
|
+
)
|
|
53
|
+
p_tail.add_argument(
|
|
54
|
+
"--server",
|
|
55
|
+
default=None,
|
|
56
|
+
help=f"Backend URL (default: value from config, else {DEFAULT_SERVER}).",
|
|
57
|
+
)
|
|
58
|
+
p_tail.add_argument("--session-id", default=None, help="Session id to stamp on events missing one.")
|
|
59
|
+
|
|
60
|
+
subparsers.add_parser(
|
|
61
|
+
"doctor",
|
|
62
|
+
help="Diagnose the install: config, backend, token, hook.",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return parser
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def main(argv: list[str] | None = None) -> int:
|
|
69
|
+
parser = _build_parser()
|
|
70
|
+
args = parser.parse_args(argv)
|
|
71
|
+
|
|
72
|
+
if args.cmd == "tail" and args.server is None:
|
|
73
|
+
cfg = config.load() or {}
|
|
74
|
+
args.server = cfg.get("server", DEFAULT_SERVER)
|
|
75
|
+
|
|
76
|
+
if args.cmd == "init":
|
|
77
|
+
return init_cmd.run(args)
|
|
78
|
+
if args.cmd == "tail":
|
|
79
|
+
return tail_cmd.run(args)
|
|
80
|
+
if args.cmd == "doctor":
|
|
81
|
+
return doctor_cmd.run(args)
|
|
82
|
+
|
|
83
|
+
parser.error(f"unknown command: {args.cmd!r}")
|
|
84
|
+
return 2
|