obris-cli 0.1.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.
@@ -0,0 +1,4 @@
1
+ .venv/
2
+ *.egg-info/
3
+ __pycache__/
4
+ .idea/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Obris
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,6 @@
1
+ .PHONY: publish
2
+
3
+ publish:
4
+ rm -rf dist/
5
+ uv build
6
+ uv publish
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: obris-cli
3
+ Version: 0.1.0
4
+ Summary: CLI for capturing screenshots and managing knowledge in Obris
5
+ Project-URL: Homepage, https://obris.ai
6
+ Project-URL: Repository, https://github.com/obris-dev/obris-cli
7
+ Project-URL: Documentation, https://docs.obris.ai
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai,capture,cli,knowledge-management,obris,screenshot,second-brain
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: click>=8.0
13
+ Requires-Dist: requests>=2.28
14
+ Description-Content-Type: text/markdown
15
+
16
+ # Obris CLI
17
+
18
+ A command-line tool for capturing screenshots and managing knowledge in [Obris](https://obris.ai), your personal knowledge layer for AI. Save content to organized topics so you never start another AI chat from zero again.
19
+
20
+ ## Install
21
+
22
+ Requires Python 3.10+.
23
+
24
+ ```bash
25
+ pip install obris-cli
26
+ ```
27
+
28
+ Or run directly with [uv](https://docs.astral.sh/uv/):
29
+
30
+ ```bash
31
+ uvx obris-cli --help
32
+ ```
33
+
34
+ ### Optional: macOS notifications
35
+
36
+ For desktop notifications with deep linking to your uploaded content:
37
+
38
+ ```bash
39
+ brew install terminal-notifier
40
+ ```
41
+
42
+ ## Setup
43
+
44
+ ### 1. Get your API key
45
+
46
+ Generate an API key from your [Obris dashboard](https://app.obris.ai/api-keys). Don't have an account? [Sign up](https://app.obris.ai/signup).
47
+
48
+ ### 2. Authenticate
49
+
50
+ ```bash
51
+ obris auth --key <your-api-key>
52
+ ```
53
+
54
+ This saves your key locally to `~/.obris/config.json` and detects your Scratch topic (the default destination for captures).
55
+
56
+ ## Usage
57
+
58
+ ### Capture a screenshot
59
+
60
+ ```bash
61
+ obris capture # screenshot + upload to Scratch
62
+ obris capture --name "my pic" # explicit name
63
+ obris capture --prompt # prompt for a name via dialog
64
+ obris capture --topic <id> # upload to a specific topic
65
+ ```
66
+
67
+ ### Upload a file
68
+
69
+ ```bash
70
+ obris upload photo.png # upload to Scratch
71
+ obris upload photo.png --topic <id> # upload to a specific topic
72
+ ```
73
+
74
+ ### Manage topics
75
+
76
+ ```bash
77
+ obris topic list # list all topics
78
+ obris topic list <topic_id> # list knowledge in a topic
79
+ ```
80
+
81
+ ### Manage knowledge
82
+
83
+ ```bash
84
+ obris knowledge move <id> --topic <id> # move to another topic
85
+ obris knowledge delete <id> # delete a knowledge item
86
+ ```
87
+
88
+ ## Hotkeys
89
+
90
+ Bind keyboard shortcuts to capture commands using any automation tool — [Alfred](https://www.alfredapp.com/), [Raycast](https://www.raycast.com/), [Keyboard Maestro](https://www.keyboardmaestro.com/), macOS Shortcuts, etc.
91
+
92
+ ### Example: Alfred
93
+
94
+ Create a workflow with a **Hotkey** trigger connected to a **Run Script** action (language: `/bin/zsh`):
95
+
96
+ **Quick capture:**
97
+
98
+ ```zsh
99
+ /full/path/to/obris capture
100
+ ```
101
+
102
+ **Capture with name prompt:**
103
+
104
+ ```zsh
105
+ /full/path/to/obris capture --prompt
106
+ ```
107
+
108
+ > **Tip:** Use the full path to the `obris` binary since Alfred doesn't load your shell profile. Run `which obris` to find it.
109
+
110
+ ## Platform support
111
+
112
+ | Platform | Capture | Upload / Topics / Knowledge |
113
+ |----------|---------|----------------------------|
114
+ | macOS | Yes | Yes |
115
+ | Linux | Yes | Yes |
116
+ | Windows | No | Yes |
117
+
118
+ ## Development
119
+
120
+ ```bash
121
+ git clone https://github.com/obris-dev/obris-cli.git
122
+ cd obris-cli
123
+ uv sync
124
+ ```
125
+
126
+ Run commands locally without installing:
127
+
128
+ ```bash
129
+ uv run obris auth --key <your-api-key> --base http://localhost:8000
130
+ uv run obris capture
131
+ uv run obris topic list
132
+ ```
133
+
134
+ ### Publishing
135
+
136
+ ```bash
137
+ make publish # build and publish to PyPI
138
+ ```
139
+
140
+ ## Privacy Policy
141
+
142
+ This CLI sends your Obris API key to the Obris API (`api.obris.ai`) to authenticate requests. It uploads files and retrieves topic and knowledge data from your account. No data is stored beyond the local config file at `~/.obris/config.json`.
143
+
144
+ For the full privacy policy, see [obris.ai/privacy](https://obris.ai/privacy).
145
+
146
+ ## Support
147
+
148
+ For issues or questions, contact [support@obris.ai](mailto:support@obris.ai) or open an issue on [GitHub](https://github.com/obris-dev/obris-cli/issues).
149
+
150
+ ## License
151
+
152
+ MIT
@@ -0,0 +1,137 @@
1
+ # Obris CLI
2
+
3
+ A command-line tool for capturing screenshots and managing knowledge in [Obris](https://obris.ai), your personal knowledge layer for AI. Save content to organized topics so you never start another AI chat from zero again.
4
+
5
+ ## Install
6
+
7
+ Requires Python 3.10+.
8
+
9
+ ```bash
10
+ pip install obris-cli
11
+ ```
12
+
13
+ Or run directly with [uv](https://docs.astral.sh/uv/):
14
+
15
+ ```bash
16
+ uvx obris-cli --help
17
+ ```
18
+
19
+ ### Optional: macOS notifications
20
+
21
+ For desktop notifications with deep linking to your uploaded content:
22
+
23
+ ```bash
24
+ brew install terminal-notifier
25
+ ```
26
+
27
+ ## Setup
28
+
29
+ ### 1. Get your API key
30
+
31
+ Generate an API key from your [Obris dashboard](https://app.obris.ai/api-keys). Don't have an account? [Sign up](https://app.obris.ai/signup).
32
+
33
+ ### 2. Authenticate
34
+
35
+ ```bash
36
+ obris auth --key <your-api-key>
37
+ ```
38
+
39
+ This saves your key locally to `~/.obris/config.json` and detects your Scratch topic (the default destination for captures).
40
+
41
+ ## Usage
42
+
43
+ ### Capture a screenshot
44
+
45
+ ```bash
46
+ obris capture # screenshot + upload to Scratch
47
+ obris capture --name "my pic" # explicit name
48
+ obris capture --prompt # prompt for a name via dialog
49
+ obris capture --topic <id> # upload to a specific topic
50
+ ```
51
+
52
+ ### Upload a file
53
+
54
+ ```bash
55
+ obris upload photo.png # upload to Scratch
56
+ obris upload photo.png --topic <id> # upload to a specific topic
57
+ ```
58
+
59
+ ### Manage topics
60
+
61
+ ```bash
62
+ obris topic list # list all topics
63
+ obris topic list <topic_id> # list knowledge in a topic
64
+ ```
65
+
66
+ ### Manage knowledge
67
+
68
+ ```bash
69
+ obris knowledge move <id> --topic <id> # move to another topic
70
+ obris knowledge delete <id> # delete a knowledge item
71
+ ```
72
+
73
+ ## Hotkeys
74
+
75
+ Bind keyboard shortcuts to capture commands using any automation tool — [Alfred](https://www.alfredapp.com/), [Raycast](https://www.raycast.com/), [Keyboard Maestro](https://www.keyboardmaestro.com/), macOS Shortcuts, etc.
76
+
77
+ ### Example: Alfred
78
+
79
+ Create a workflow with a **Hotkey** trigger connected to a **Run Script** action (language: `/bin/zsh`):
80
+
81
+ **Quick capture:**
82
+
83
+ ```zsh
84
+ /full/path/to/obris capture
85
+ ```
86
+
87
+ **Capture with name prompt:**
88
+
89
+ ```zsh
90
+ /full/path/to/obris capture --prompt
91
+ ```
92
+
93
+ > **Tip:** Use the full path to the `obris` binary since Alfred doesn't load your shell profile. Run `which obris` to find it.
94
+
95
+ ## Platform support
96
+
97
+ | Platform | Capture | Upload / Topics / Knowledge |
98
+ |----------|---------|----------------------------|
99
+ | macOS | Yes | Yes |
100
+ | Linux | Yes | Yes |
101
+ | Windows | No | Yes |
102
+
103
+ ## Development
104
+
105
+ ```bash
106
+ git clone https://github.com/obris-dev/obris-cli.git
107
+ cd obris-cli
108
+ uv sync
109
+ ```
110
+
111
+ Run commands locally without installing:
112
+
113
+ ```bash
114
+ uv run obris auth --key <your-api-key> --base http://localhost:8000
115
+ uv run obris capture
116
+ uv run obris topic list
117
+ ```
118
+
119
+ ### Publishing
120
+
121
+ ```bash
122
+ make publish # build and publish to PyPI
123
+ ```
124
+
125
+ ## Privacy Policy
126
+
127
+ This CLI sends your Obris API key to the Obris API (`api.obris.ai`) to authenticate requests. It uploads files and retrieves topic and knowledge data from your account. No data is stored beyond the local config file at `~/.obris/config.json`.
128
+
129
+ For the full privacy policy, see [obris.ai/privacy](https://obris.ai/privacy).
130
+
131
+ ## Support
132
+
133
+ For issues or questions, contact [support@obris.ai](mailto:support@obris.ai) or open an issue on [GitHub](https://github.com/obris-dev/obris-cli/issues).
134
+
135
+ ## License
136
+
137
+ MIT
File without changes
Binary file
@@ -0,0 +1,49 @@
1
+ import subprocess
2
+ import sys
3
+ import tempfile
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+
7
+
8
+ def take_screenshot():
9
+ timestamp = datetime.now().strftime("%Y-%m-%d at %H.%M.%S")
10
+ filename = f"capture {timestamp}.png"
11
+ path = Path(tempfile.gettempdir()) / filename
12
+
13
+ if sys.platform == "darwin":
14
+ cmd = ["screencapture", "-i", str(path)]
15
+ elif sys.platform.startswith("linux"):
16
+ cmd = ["scrot", "-s", str(path)]
17
+ else:
18
+ raise SystemExit(f"Unsupported platform: {sys.platform}")
19
+
20
+ result = subprocess.run(cmd)
21
+ if result.returncode != 0:
22
+ raise SystemExit("Screenshot cancelled.")
23
+
24
+ if not path.exists() or path.stat().st_size == 0:
25
+ raise SystemExit("Screenshot cancelled.")
26
+
27
+ return path
28
+
29
+
30
+ def prompt_name():
31
+ if sys.platform == "darwin":
32
+ result = subprocess.run(
33
+ [
34
+ "osascript",
35
+ "-e",
36
+ 'display dialog "Name this capture:" default answer ""',
37
+ "-e",
38
+ "text returned of result",
39
+ ],
40
+ capture_output=True,
41
+ text=True,
42
+ )
43
+ if result.returncode != 0:
44
+ raise SystemExit("Cancelled.")
45
+ return result.stdout.strip()
46
+ else:
47
+ import click
48
+
49
+ return click.prompt("Name this capture")
@@ -0,0 +1,137 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from obris import capture, config, notify, topics, uploader
6
+
7
+
8
+ @click.group()
9
+ def cli():
10
+ """Obris CLI — capture and upload to your personal context layer."""
11
+
12
+
13
+ @cli.command()
14
+ @click.option("--key", required=True, help="Your Obris API key")
15
+ @click.option("--base", default=None, help="API base URL (e.g. http://localhost:8000)")
16
+ def auth(key, base):
17
+ """Save API key and detect scratch topic."""
18
+ cfg = config.load()
19
+ cfg["api_key"] = key
20
+ if base:
21
+ cfg["api_base"] = base.rstrip("/")
22
+ config.save(cfg)
23
+
24
+ # Find the Scratch topic
25
+ try:
26
+ all_topics = topics.list_topics()
27
+ except SystemExit:
28
+ click.echo("API key saved, but failed to fetch topics.")
29
+ return
30
+
31
+ scratch = next((t for t in all_topics if t.get("name") == "Scratch" and t.get("is_system")), None)
32
+ if scratch:
33
+ cfg["scratch_topic_id"] = scratch["id"]
34
+ config.save(cfg)
35
+ click.echo(f"Authenticated. Scratch topic: {scratch['id']}")
36
+ else:
37
+ click.echo("Authenticated. No 'Scratch' topic found — create one in the app.")
38
+
39
+
40
+ @cli.command("capture")
41
+ @click.option("--name", "cap_name", default=None, help="Display name for the capture")
42
+ @click.option("--prompt", "prompt_name", is_flag=True, help="Prompt for a name via dialog")
43
+ @click.option("--topic", default=None, help="Topic ID (defaults to Scratch)")
44
+ def capture_cmd(cap_name, prompt_name, topic):
45
+ """Take a screenshot and upload it."""
46
+ topic_id = topic or config.get_scratch_topic_id()
47
+
48
+ try:
49
+ path = capture.take_screenshot()
50
+ except SystemExit:
51
+ notify.send("Obris", "Screenshot cancelled")
52
+ raise
53
+
54
+ if cap_name:
55
+ name = cap_name
56
+ elif prompt_name:
57
+ name = capture.prompt_name()
58
+ if not name:
59
+ raise SystemExit("Name is required.")
60
+ else:
61
+ name = path.stem
62
+ notify.send_quiet("Obris", "Uploading...")
63
+
64
+ try:
65
+ result = uploader.upload_file(topic_id, path, name)
66
+ except SystemExit as e:
67
+ notify.send("Obris", f"Upload failed")
68
+ raise
69
+
70
+ notify.send("Obris", f"Uploaded '{result.get('title', name)}'", url=notify.topic_url(topic_id))
71
+ click.echo(f"Uploaded '{result.get('title', name)}'")
72
+ click.echo(f" ID: {result['id']}")
73
+
74
+ path.unlink(missing_ok=True)
75
+
76
+
77
+ @cli.command()
78
+ @click.argument("filepath", type=click.Path(exists=True, path_type=Path))
79
+ @click.option("--topic", default=None, help="Topic ID (defaults to Scratch)")
80
+ @click.option("--name", default=None, help="Display name (defaults to filename)")
81
+ def upload(filepath, topic, name):
82
+ """Upload a file to a topic."""
83
+ topic_id = topic or config.get_scratch_topic_id()
84
+ name = name or filepath.name
85
+ result = uploader.upload_file(topic_id, filepath, name)
86
+ click.echo(f"Uploaded '{result.get('title', name)}'")
87
+ click.echo(f" ID: {result['id']}")
88
+
89
+
90
+ @cli.group("topic")
91
+ def topic_group():
92
+ """Manage topics."""
93
+
94
+
95
+ @topic_group.command("list")
96
+ @click.argument("topic_id", required=False)
97
+ def topic_list(topic_id):
98
+ """List all topics, or knowledge items in a specific topic."""
99
+ if topic_id:
100
+ items = topics.list_knowledge(topic_id)
101
+ if not items:
102
+ click.echo("No items found.")
103
+ return
104
+ click.echo(f"{'ID':<28}{'TITLE':<38}CREATED")
105
+ for item in items:
106
+ created = item.get("created_at", "")[:16].replace("T", " ")
107
+ click.echo(f"{item['id']:<28}{item.get('title', ''):<38}{created}")
108
+ else:
109
+ all_topics = topics.list_topics()
110
+ if not all_topics:
111
+ click.echo("No topics found.")
112
+ return
113
+ click.echo(f"{'ID':<28}{'NAME':<22}ITEMS")
114
+ for t in all_topics:
115
+ click.echo(f"{t['id']:<28}{t['name']:<22}{t.get('item_count', 0)}")
116
+
117
+
118
+ @cli.group("knowledge")
119
+ def knowledge_group():
120
+ """Manage knowledge items."""
121
+
122
+
123
+ @knowledge_group.command("move")
124
+ @click.argument("knowledge_id")
125
+ @click.option("--topic", required=True, help="Destination topic ID")
126
+ def knowledge_move(knowledge_id, topic):
127
+ """Move a knowledge item to another topic."""
128
+ result = topics.move_knowledge(knowledge_id, topic)
129
+ click.echo(f"Moved to {result.get('topic_name', topic)}")
130
+
131
+
132
+ @knowledge_group.command("delete")
133
+ @click.argument("knowledge_id")
134
+ def knowledge_delete(knowledge_id):
135
+ """Delete a knowledge item."""
136
+ topics.delete_knowledge(knowledge_id)
137
+ click.echo(f"Deleted {knowledge_id}")
@@ -0,0 +1,42 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+ CONFIG_DIR = Path.home() / ".obris"
5
+ CONFIG_FILE = CONFIG_DIR / "config.json"
6
+
7
+ DEFAULT_CONFIG = {
8
+ "api_key": "",
9
+ "api_base": "https://api.obris.ai",
10
+ "scratch_topic_id": "",
11
+ }
12
+
13
+
14
+ def load():
15
+ if not CONFIG_FILE.exists():
16
+ return dict(DEFAULT_CONFIG)
17
+ return {**DEFAULT_CONFIG, **json.loads(CONFIG_FILE.read_text())}
18
+
19
+
20
+ def save(config):
21
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
22
+ CONFIG_FILE.write_text(json.dumps(config, indent=2) + "\n")
23
+
24
+
25
+ def get_api_key():
26
+ cfg = load()
27
+ key = cfg.get("api_key")
28
+ if not key:
29
+ raise SystemExit("Not authenticated. Run: obris auth --key <key>")
30
+ return key
31
+
32
+
33
+ def get_api_base():
34
+ return load().get("api_base", DEFAULT_CONFIG["api_base"])
35
+
36
+
37
+ def get_scratch_topic_id():
38
+ cfg = load()
39
+ tid = cfg.get("scratch_topic_id")
40
+ if not tid:
41
+ raise SystemExit("No scratch topic configured. Run: obris auth --key <key>")
42
+ return tid
@@ -0,0 +1,70 @@
1
+ import shutil
2
+ import subprocess
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ ICON_PATH = str(Path(__file__).parent / "assets" / "icon.png")
7
+
8
+ # api.obris.ai -> app.obris.ai, localhost:8000 -> localhost:3001
9
+ APP_BASE_MAP = {
10
+ "https://api.obris.ai": "https://app.obris.ai",
11
+ "http://localhost:8000": "http://localhost:3001",
12
+ }
13
+
14
+ _has_notifier = sys.platform == "darwin" and shutil.which("terminal-notifier") is not None
15
+
16
+
17
+ def _get_app_base():
18
+ from obris.config import get_api_base
19
+ api_base = get_api_base()
20
+ return APP_BASE_MAP.get(api_base, api_base.replace("api.", "app."))
21
+
22
+
23
+ def _osascript_notify(title, message):
24
+ subprocess.run([
25
+ "osascript", "-e",
26
+ f'display notification "{message}" with title "{title}"',
27
+ ])
28
+
29
+
30
+ def send(title, message, url=None):
31
+ if sys.platform != "darwin":
32
+ import click
33
+ click.echo(f"{title}: {message}")
34
+ return
35
+
36
+ if _has_notifier:
37
+ cmd = [
38
+ "terminal-notifier",
39
+ "-title", title,
40
+ "-message", message,
41
+ "-contentImage", ICON_PATH,
42
+ "-sound", "default",
43
+ ]
44
+ if url:
45
+ cmd += ["-open", url]
46
+ subprocess.run(cmd)
47
+ else:
48
+ _osascript_notify(title, message)
49
+
50
+
51
+ def send_quiet(title, message):
52
+ """Notification without sound (for transient states like 'Uploading...')."""
53
+ if sys.platform != "darwin":
54
+ import click
55
+ click.echo(f"{title}: {message}")
56
+ return
57
+
58
+ if _has_notifier:
59
+ subprocess.run([
60
+ "terminal-notifier",
61
+ "-title", title,
62
+ "-message", message,
63
+ "-contentImage", ICON_PATH,
64
+ ])
65
+ else:
66
+ _osascript_notify(title, message)
67
+
68
+
69
+ def topic_url(topic_id):
70
+ return f"{_get_app_base()}/topics/{topic_id}"
@@ -0,0 +1,49 @@
1
+ import requests
2
+
3
+ from obris.config import get_api_base, get_api_key
4
+
5
+
6
+ def _headers():
7
+ return {"X-API-Key": get_api_key()}
8
+
9
+
10
+ def _unwrap(data):
11
+ """Handle paginated ({"results": [...]}) or plain list responses."""
12
+ if isinstance(data, dict) and "results" in data:
13
+ return data["results"]
14
+ return data
15
+
16
+
17
+ def list_topics():
18
+ resp = requests.get(f"{get_api_base()}/v1/topics", headers=_headers())
19
+ if not resp.ok:
20
+ raise SystemExit(f"Failed to list topics ({resp.status_code}): {resp.text}")
21
+ return _unwrap(resp.json())
22
+
23
+
24
+ def list_knowledge(topic_id):
25
+ resp = requests.get(
26
+ f"{get_api_base()}/v1/topics/{topic_id}/knowledge", headers=_headers()
27
+ )
28
+ if not resp.ok:
29
+ raise SystemExit(f"Failed to list knowledge ({resp.status_code}): {resp.text}")
30
+ return _unwrap(resp.json())
31
+
32
+
33
+ def delete_knowledge(knowledge_id):
34
+ resp = requests.delete(
35
+ f"{get_api_base()}/v1/knowledge/detail/{knowledge_id}", headers=_headers()
36
+ )
37
+ if not resp.ok:
38
+ raise SystemExit(f"Delete failed ({resp.status_code}): {resp.text}")
39
+
40
+
41
+ def move_knowledge(knowledge_id, topic_id):
42
+ resp = requests.post(
43
+ f"{get_api_base()}/v1/knowledge/detail/{knowledge_id}/move",
44
+ headers=_headers(),
45
+ json={"topic_id": topic_id},
46
+ )
47
+ if not resp.ok:
48
+ raise SystemExit(f"Move failed ({resp.status_code}): {resp.text}")
49
+ return resp.json()
@@ -0,0 +1,25 @@
1
+ import mimetypes
2
+
3
+ import requests
4
+
5
+ from obris.config import get_api_base, get_api_key
6
+
7
+
8
+ def upload_file(topic_id, filepath, name):
9
+ url = f"{get_api_base()}/v1/knowledge/upload"
10
+ headers = {"X-API-Key": get_api_key()}
11
+ filename = filepath.name if hasattr(filepath, "name") else str(filepath)
12
+ mime_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
13
+
14
+ with open(filepath, "rb") as f:
15
+ resp = requests.post(
16
+ url,
17
+ headers=headers,
18
+ files={"file": (filename, f, mime_type)},
19
+ data={"topic_id": topic_id, "title": name},
20
+ )
21
+
22
+ if not resp.ok:
23
+ raise SystemExit(f"Upload failed ({resp.status_code}): {resp.text}")
24
+
25
+ return resp.json()
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "obris-cli"
7
+ version = "0.1.0"
8
+ description = "CLI for capturing screenshots and managing knowledge in Obris"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ keywords = ["obris", "cli", "screenshot", "knowledge-management", "ai", "second-brain", "capture"]
13
+ dependencies = [
14
+ "click>=8.0",
15
+ "requests>=2.28",
16
+ ]
17
+
18
+ [project.scripts]
19
+ obris = "obris.cli:cli"
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["obris"]
23
+
24
+ [project.urls]
25
+ Homepage = "https://obris.ai"
26
+ Repository = "https://github.com/obris-dev/obris-cli"
27
+ Documentation = "https://docs.obris.ai"
@@ -0,0 +1,170 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.10"
4
+
5
+ [[package]]
6
+ name = "certifi"
7
+ version = "2026.2.25"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
12
+ ]
13
+
14
+ [[package]]
15
+ name = "charset-normalizer"
16
+ version = "3.4.4"
17
+ source = { registry = "https://pypi.org/simple" }
18
+ sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
19
+ wheels = [
20
+ { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" },
21
+ { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" },
22
+ { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" },
23
+ { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" },
24
+ { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" },
25
+ { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" },
26
+ { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" },
27
+ { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" },
28
+ { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" },
29
+ { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" },
30
+ { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" },
31
+ { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" },
32
+ { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" },
33
+ { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" },
34
+ { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" },
35
+ { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" },
36
+ { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
37
+ { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
38
+ { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
39
+ { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
40
+ { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
41
+ { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
42
+ { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
43
+ { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
44
+ { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
45
+ { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
46
+ { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
47
+ { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
48
+ { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
49
+ { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
50
+ { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
51
+ { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
52
+ { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
53
+ { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
54
+ { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
55
+ { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
56
+ { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
57
+ { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
58
+ { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
59
+ { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
60
+ { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
61
+ { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
62
+ { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
63
+ { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
64
+ { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
65
+ { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
66
+ { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
67
+ { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
68
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
69
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
70
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
71
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
72
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
73
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
74
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
75
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
76
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
77
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
78
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
79
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
80
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
81
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
82
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
83
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
84
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
85
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
86
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
87
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
88
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
89
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
90
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
91
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
92
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
93
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
94
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
95
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
96
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
97
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
98
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
99
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
100
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
101
+ ]
102
+
103
+ [[package]]
104
+ name = "click"
105
+ version = "8.3.1"
106
+ source = { registry = "https://pypi.org/simple" }
107
+ dependencies = [
108
+ { name = "colorama", marker = "sys_platform == 'win32'" },
109
+ ]
110
+ sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
111
+ wheels = [
112
+ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
113
+ ]
114
+
115
+ [[package]]
116
+ name = "colorama"
117
+ version = "0.4.6"
118
+ source = { registry = "https://pypi.org/simple" }
119
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
120
+ wheels = [
121
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
122
+ ]
123
+
124
+ [[package]]
125
+ name = "idna"
126
+ version = "3.11"
127
+ source = { registry = "https://pypi.org/simple" }
128
+ sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
129
+ wheels = [
130
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
131
+ ]
132
+
133
+ [[package]]
134
+ name = "obris-cli"
135
+ version = "0.1.0"
136
+ source = { editable = "." }
137
+ dependencies = [
138
+ { name = "click" },
139
+ { name = "requests" },
140
+ ]
141
+
142
+ [package.metadata]
143
+ requires-dist = [
144
+ { name = "click", specifier = ">=8.0" },
145
+ { name = "requests", specifier = ">=2.28" },
146
+ ]
147
+
148
+ [[package]]
149
+ name = "requests"
150
+ version = "2.32.5"
151
+ source = { registry = "https://pypi.org/simple" }
152
+ dependencies = [
153
+ { name = "certifi" },
154
+ { name = "charset-normalizer" },
155
+ { name = "idna" },
156
+ { name = "urllib3" },
157
+ ]
158
+ sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
159
+ wheels = [
160
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
161
+ ]
162
+
163
+ [[package]]
164
+ name = "urllib3"
165
+ version = "2.6.3"
166
+ source = { registry = "https://pypi.org/simple" }
167
+ sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
168
+ wheels = [
169
+ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
170
+ ]