onesearch-cli 0.12.1__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.
- onesearch/__init__.py +6 -0
- onesearch/api.py +247 -0
- onesearch/banner.py +84 -0
- onesearch/commands/__init__.py +4 -0
- onesearch/commands/auth.py +64 -0
- onesearch/commands/config.py +166 -0
- onesearch/commands/search.py +142 -0
- onesearch/commands/source.py +232 -0
- onesearch/commands/status.py +221 -0
- onesearch/config.py +185 -0
- onesearch/context.py +51 -0
- onesearch/main.py +153 -0
- onesearch_cli-0.12.1.dist-info/METADATA +222 -0
- onesearch_cli-0.12.1.dist-info/RECORD +16 -0
- onesearch_cli-0.12.1.dist-info/WHEEL +4 -0
- onesearch_cli-0.12.1.dist-info/entry_points.txt +2 -0
onesearch/config.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Copyright (C) 2025 demigodmode
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
"""Configuration management for OneSearch CLI."""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_config_dir() -> Path:
|
|
14
|
+
"""Get the configuration directory path."""
|
|
15
|
+
# Use XDG_CONFIG_HOME on Linux, or appropriate dir on Windows/Mac
|
|
16
|
+
if os.name == "nt": # Windows
|
|
17
|
+
config_base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
|
|
18
|
+
else: # Linux/Mac
|
|
19
|
+
config_base = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
|
|
20
|
+
return config_base / "onesearch"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_config_path() -> Path:
|
|
24
|
+
"""Get the configuration file path."""
|
|
25
|
+
return get_config_dir() / "config.yml"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_config() -> dict:
|
|
29
|
+
"""Load configuration from file.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Configuration dictionary, or empty dict if file doesn't exist.
|
|
33
|
+
"""
|
|
34
|
+
config_path = get_config_path()
|
|
35
|
+
if not config_path.exists():
|
|
36
|
+
return {}
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
with open(config_path) as f:
|
|
40
|
+
config = yaml.safe_load(f) or {}
|
|
41
|
+
return config
|
|
42
|
+
except Exception:
|
|
43
|
+
return {}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def save_config(config: dict) -> None:
|
|
47
|
+
"""Save configuration to file.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
config: Configuration dictionary to save.
|
|
51
|
+
"""
|
|
52
|
+
config_dir = get_config_dir()
|
|
53
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
|
|
55
|
+
config_path = get_config_path()
|
|
56
|
+
with open(config_path, "w") as f:
|
|
57
|
+
yaml.safe_dump(config, f, default_flow_style=False, sort_keys=False)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_config_value(key: str, default: Any = None) -> Any:
|
|
61
|
+
"""Get a configuration value.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
key: Dot-separated key path (e.g., "output.colors").
|
|
65
|
+
default: Default value if key not found.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Configuration value or default.
|
|
69
|
+
"""
|
|
70
|
+
config = load_config()
|
|
71
|
+
keys = key.split(".")
|
|
72
|
+
value = config
|
|
73
|
+
|
|
74
|
+
for k in keys:
|
|
75
|
+
if isinstance(value, dict) and k in value:
|
|
76
|
+
value = value[k]
|
|
77
|
+
else:
|
|
78
|
+
return default
|
|
79
|
+
|
|
80
|
+
return value
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def set_config_value(key: str, value: Any) -> None:
|
|
84
|
+
"""Set a configuration value.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
key: Dot-separated key path (e.g., "output.colors").
|
|
88
|
+
value: Value to set.
|
|
89
|
+
"""
|
|
90
|
+
config = load_config()
|
|
91
|
+
keys = key.split(".")
|
|
92
|
+
|
|
93
|
+
# Navigate to the parent dict
|
|
94
|
+
current = config
|
|
95
|
+
for k in keys[:-1]:
|
|
96
|
+
if k not in current or not isinstance(current[k], dict):
|
|
97
|
+
current[k] = {}
|
|
98
|
+
current = current[k]
|
|
99
|
+
|
|
100
|
+
# Set the value
|
|
101
|
+
current[keys[-1]] = value
|
|
102
|
+
save_config(config)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def delete_config_value(key: str) -> bool:
|
|
106
|
+
"""Delete a configuration value.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
key: Dot-separated key path.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
True if value was deleted, False if not found.
|
|
113
|
+
"""
|
|
114
|
+
config = load_config()
|
|
115
|
+
keys = key.split(".")
|
|
116
|
+
|
|
117
|
+
# Navigate to the parent dict
|
|
118
|
+
current = config
|
|
119
|
+
for k in keys[:-1]:
|
|
120
|
+
if k not in current or not isinstance(current[k], dict):
|
|
121
|
+
return False
|
|
122
|
+
current = current[k]
|
|
123
|
+
|
|
124
|
+
# Delete the key
|
|
125
|
+
if keys[-1] in current:
|
|
126
|
+
del current[keys[-1]]
|
|
127
|
+
save_config(config)
|
|
128
|
+
return True
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_backend_url() -> str:
|
|
133
|
+
"""Get the backend URL from config/env/default.
|
|
134
|
+
|
|
135
|
+
Priority:
|
|
136
|
+
1. Environment variable ONESEARCH_URL
|
|
137
|
+
2. Config file backend_url
|
|
138
|
+
3. Default http://localhost:8000
|
|
139
|
+
"""
|
|
140
|
+
env_url = os.environ.get("ONESEARCH_URL")
|
|
141
|
+
if env_url:
|
|
142
|
+
return env_url
|
|
143
|
+
|
|
144
|
+
config_url = get_config_value("backend_url")
|
|
145
|
+
if config_url:
|
|
146
|
+
return config_url
|
|
147
|
+
|
|
148
|
+
return "http://localhost:8000"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_auth_token() -> str | None:
|
|
152
|
+
"""Get auth token from env/config.
|
|
153
|
+
|
|
154
|
+
Priority:
|
|
155
|
+
1. Environment variable ONESEARCH_TOKEN
|
|
156
|
+
2. Config file auth.token
|
|
157
|
+
"""
|
|
158
|
+
env_token = os.environ.get("ONESEARCH_TOKEN")
|
|
159
|
+
if env_token:
|
|
160
|
+
return env_token
|
|
161
|
+
|
|
162
|
+
return get_config_value("auth.token")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# Default configuration template
|
|
166
|
+
DEFAULT_CONFIG = """\
|
|
167
|
+
# OneSearch CLI Configuration
|
|
168
|
+
# Location: {config_path}
|
|
169
|
+
|
|
170
|
+
# Backend API URL
|
|
171
|
+
backend_url: http://localhost:8000
|
|
172
|
+
|
|
173
|
+
# Authentication
|
|
174
|
+
auth:
|
|
175
|
+
token: null
|
|
176
|
+
|
|
177
|
+
# Output settings
|
|
178
|
+
output:
|
|
179
|
+
colors: true
|
|
180
|
+
format: table # table or json
|
|
181
|
+
|
|
182
|
+
# Default values for commands
|
|
183
|
+
defaults:
|
|
184
|
+
search_limit: 20
|
|
185
|
+
"""
|
onesearch/context.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Copyright (C) 2025 demigodmode
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
"""Shared CLI context and utilities."""
|
|
5
|
+
|
|
6
|
+
import io
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from onesearch.api import OneSearchAPI
|
|
12
|
+
from onesearch.config import get_auth_token
|
|
13
|
+
|
|
14
|
+
# Rich console for output
|
|
15
|
+
console = Console()
|
|
16
|
+
err_console = Console(stderr=True)
|
|
17
|
+
|
|
18
|
+
# Quiet console that discards output
|
|
19
|
+
_quiet_console = Console(file=io.StringIO(), force_terminal=False)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Context:
|
|
23
|
+
"""CLI context object passed to commands."""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self.api: OneSearchAPI | None = None
|
|
27
|
+
self.verbose: bool = False
|
|
28
|
+
self.quiet: bool = False
|
|
29
|
+
self.url: str = "http://localhost:8000"
|
|
30
|
+
self.token: str | None = None
|
|
31
|
+
|
|
32
|
+
def get_api(self) -> OneSearchAPI:
|
|
33
|
+
"""Get or create the API client."""
|
|
34
|
+
if self.api is None:
|
|
35
|
+
self.token = get_auth_token()
|
|
36
|
+
self.api = OneSearchAPI(base_url=self.url, token=self.token)
|
|
37
|
+
return self.api
|
|
38
|
+
|
|
39
|
+
def reset_api(self) -> None:
|
|
40
|
+
"""Clear cached API client after config/auth changes."""
|
|
41
|
+
self.api = None
|
|
42
|
+
self.token = None
|
|
43
|
+
|
|
44
|
+
def get_console(self) -> Console:
|
|
45
|
+
"""Get the appropriate console based on quiet mode."""
|
|
46
|
+
if self.quiet:
|
|
47
|
+
return _quiet_console
|
|
48
|
+
return console
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
pass_context = click.make_pass_decorator(Context, ensure=True)
|
onesearch/main.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Copyright (C) 2025 demigodmode
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
"""OneSearch CLI entry point."""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from onesearch import __version__
|
|
12
|
+
from onesearch.api import APIError
|
|
13
|
+
from onesearch.banner import build_startup_panel
|
|
14
|
+
from onesearch.config import load_config
|
|
15
|
+
from onesearch.context import Context, console, err_console
|
|
16
|
+
|
|
17
|
+
# Context settings for all commands
|
|
18
|
+
CONTEXT_SETTINGS = {
|
|
19
|
+
"help_option_names": ["-h", "--help"],
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_default_url() -> str:
|
|
24
|
+
"""Get the default backend URL from config/env/default."""
|
|
25
|
+
from onesearch.config import get_backend_url
|
|
26
|
+
return get_backend_url()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _has_configured_backend(resolved_url: str | None = None) -> bool:
|
|
30
|
+
config_data = load_config()
|
|
31
|
+
return bool(resolved_url or os.environ.get("ONESEARCH_URL") or config_data.get("backend_url"))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _render_startup_panel(ctx: Context) -> None:
|
|
35
|
+
configured = _has_configured_backend(ctx.url)
|
|
36
|
+
if not configured:
|
|
37
|
+
console.print(
|
|
38
|
+
build_startup_panel(
|
|
39
|
+
configured=False,
|
|
40
|
+
backend_url=None,
|
|
41
|
+
cli_version=__version__,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
api = ctx.get_api()
|
|
47
|
+
try:
|
|
48
|
+
health = api.health(allow_degraded=True)
|
|
49
|
+
server_version = health.get("version")
|
|
50
|
+
server_status = health.get("status", "unknown")
|
|
51
|
+
auth_state = "not logged in"
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
user = api.whoami()
|
|
55
|
+
auth_state = f"logged in as {user.get('username', 'unknown')}"
|
|
56
|
+
except APIError as auth_error:
|
|
57
|
+
if auth_error.status_code not in (401, 403):
|
|
58
|
+
console.print(
|
|
59
|
+
build_startup_panel(
|
|
60
|
+
configured=True,
|
|
61
|
+
backend_url=ctx.url,
|
|
62
|
+
cli_version=__version__,
|
|
63
|
+
server_version=server_version,
|
|
64
|
+
server_status=server_status,
|
|
65
|
+
error_message=auth_error.message,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
console.print(
|
|
71
|
+
build_startup_panel(
|
|
72
|
+
configured=True,
|
|
73
|
+
backend_url=ctx.url,
|
|
74
|
+
cli_version=__version__,
|
|
75
|
+
server_version=server_version,
|
|
76
|
+
server_status=server_status,
|
|
77
|
+
auth_state=auth_state,
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
except APIError as e:
|
|
81
|
+
console.print(
|
|
82
|
+
build_startup_panel(
|
|
83
|
+
configured=True,
|
|
84
|
+
backend_url=ctx.url,
|
|
85
|
+
cli_version=__version__,
|
|
86
|
+
error_message=e.message,
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@click.group(context_settings=CONTEXT_SETTINGS, invoke_without_command=True)
|
|
92
|
+
@click.version_option(version=__version__, prog_name="onesearch")
|
|
93
|
+
@click.option(
|
|
94
|
+
"--url",
|
|
95
|
+
envvar="ONESEARCH_URL",
|
|
96
|
+
default=get_default_url,
|
|
97
|
+
help="Backend API URL.",
|
|
98
|
+
show_default=True,
|
|
99
|
+
)
|
|
100
|
+
@click.option(
|
|
101
|
+
"-v", "--verbose",
|
|
102
|
+
is_flag=True,
|
|
103
|
+
help="Enable verbose output.",
|
|
104
|
+
)
|
|
105
|
+
@click.option(
|
|
106
|
+
"-q", "--quiet",
|
|
107
|
+
is_flag=True,
|
|
108
|
+
help="Suppress non-essential output. Only show results/errors.",
|
|
109
|
+
)
|
|
110
|
+
@click.pass_context
|
|
111
|
+
def cli(click_ctx: click.Context, url: str, verbose: bool, quiet: bool):
|
|
112
|
+
"""OneSearch - Self-hosted, privacy-focused search for your homelab.
|
|
113
|
+
|
|
114
|
+
Search across all your files, documents, and notes from a single,
|
|
115
|
+
unified command-line interface.
|
|
116
|
+
|
|
117
|
+
\b
|
|
118
|
+
Examples:
|
|
119
|
+
onesearch search "kubernetes deployment"
|
|
120
|
+
onesearch source list
|
|
121
|
+
onesearch status
|
|
122
|
+
onesearch health
|
|
123
|
+
|
|
124
|
+
Use 'onesearch COMMAND --help' for more information on a command.
|
|
125
|
+
"""
|
|
126
|
+
ctx = click_ctx.ensure_object(Context)
|
|
127
|
+
ctx.url = url
|
|
128
|
+
ctx.verbose = verbose
|
|
129
|
+
ctx.quiet = quiet
|
|
130
|
+
|
|
131
|
+
if click_ctx.invoked_subcommand is None:
|
|
132
|
+
_render_startup_panel(ctx)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# Import and register command groups after cli is defined
|
|
136
|
+
from onesearch.commands import auth, config, search, source, status # noqa: E402, F401
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def main():
|
|
140
|
+
"""Main entry point."""
|
|
141
|
+
try:
|
|
142
|
+
cli()
|
|
143
|
+
except KeyboardInterrupt:
|
|
144
|
+
err_console.print("\n[yellow]Aborted.[/yellow]")
|
|
145
|
+
sys.exit(130)
|
|
146
|
+
except Exception as e:
|
|
147
|
+
# Show concise error; users can use -v for more details
|
|
148
|
+
err_console.print(f"[red]Error:[/red] {type(e).__name__}: {e}")
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
main()
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: onesearch-cli
|
|
3
|
+
Version: 0.12.1
|
|
4
|
+
Summary: Standalone CLI client for a running OneSearch server
|
|
5
|
+
License: AGPL-3.0-only
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: click>=8.1.0
|
|
8
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
10
|
+
Requires-Dist: requests>=2.31.0
|
|
11
|
+
Requires-Dist: rich>=13.0.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# OneSearch CLI
|
|
15
|
+
|
|
16
|
+
Command-line interface for OneSearch - Self-hosted, privacy-focused search for your homelab.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
The CLI is a standalone client for a running OneSearch server. It does not include the backend, indexer, or search data. Tagged OneSearch releases publish the Docker image and the `onesearch-cli` package together on the same shared version.
|
|
21
|
+
|
|
22
|
+
### Recommended: pipx
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pipx install onesearch-cli
|
|
26
|
+
onesearch config set backend_url http://localhost:8000
|
|
27
|
+
onesearch login
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### From Source (Development)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd cli
|
|
34
|
+
python -m venv .venv
|
|
35
|
+
|
|
36
|
+
# Windows
|
|
37
|
+
.venv\Scripts\activate
|
|
38
|
+
|
|
39
|
+
# Linux/Mac
|
|
40
|
+
source .venv/bin/activate
|
|
41
|
+
|
|
42
|
+
pip install -e .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Verify Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
onesearch --version
|
|
49
|
+
onesearch --help
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Check system health
|
|
56
|
+
onesearch health
|
|
57
|
+
|
|
58
|
+
# List configured sources
|
|
59
|
+
onesearch source list
|
|
60
|
+
|
|
61
|
+
# Add a source (ID is auto-generated from name as slug, e.g., "documents")
|
|
62
|
+
onesearch source add "Documents" /data/docs --include "**/*.pdf,**/*.md"
|
|
63
|
+
|
|
64
|
+
# Trigger indexing (use source ID from 'source list')
|
|
65
|
+
onesearch source reindex documents
|
|
66
|
+
|
|
67
|
+
# Search
|
|
68
|
+
onesearch search "kubernetes deployment"
|
|
69
|
+
|
|
70
|
+
# Check indexing status
|
|
71
|
+
onesearch status
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> **Note:** Source IDs are slugified from the name (e.g., "My Documents" → "my-documents").
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
### Source Management
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# List all sources
|
|
82
|
+
onesearch source list
|
|
83
|
+
|
|
84
|
+
# Add a new source
|
|
85
|
+
onesearch source add <name> <path> [--include PATTERNS] [--exclude PATTERNS]
|
|
86
|
+
|
|
87
|
+
# Show source details
|
|
88
|
+
onesearch source show <source_id>
|
|
89
|
+
|
|
90
|
+
# Reindex a source
|
|
91
|
+
onesearch source reindex <source_id>
|
|
92
|
+
|
|
93
|
+
# Delete a source
|
|
94
|
+
onesearch source delete <source_id> [--yes]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Search
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Basic search
|
|
101
|
+
onesearch search "query"
|
|
102
|
+
|
|
103
|
+
# With filters (use source ID from 'source list')
|
|
104
|
+
onesearch search "python" --source documents --type pdf --limit 10
|
|
105
|
+
|
|
106
|
+
# JSON output for scripting
|
|
107
|
+
onesearch search "error" --json | jq '.results[].path'
|
|
108
|
+
|
|
109
|
+
# Pagination
|
|
110
|
+
onesearch search "docker" --offset 20 --limit 10
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Status & Health
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Overall status
|
|
117
|
+
onesearch status
|
|
118
|
+
|
|
119
|
+
# Specific source status
|
|
120
|
+
onesearch status <source_id>
|
|
121
|
+
|
|
122
|
+
# System health check
|
|
123
|
+
onesearch health
|
|
124
|
+
|
|
125
|
+
# JSON output for monitoring
|
|
126
|
+
onesearch health --json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Configuration
|
|
130
|
+
|
|
131
|
+
### Environment Variables
|
|
132
|
+
|
|
133
|
+
- `ONESEARCH_URL` - Backend API URL (default: `http://localhost:8000`)
|
|
134
|
+
- `ONESEARCH_TOKEN` - Bearer token for non-interactive auth
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Use a custom backend URL
|
|
138
|
+
export ONESEARCH_URL=http://onesearch.local:8000
|
|
139
|
+
onesearch search "test"
|
|
140
|
+
|
|
141
|
+
# Non-interactive auth for scripts
|
|
142
|
+
export ONESEARCH_TOKEN=xxxxx
|
|
143
|
+
onesearch search "test" --json
|
|
144
|
+
|
|
145
|
+
# Or pass the URL directly
|
|
146
|
+
onesearch --url http://onesearch.local:8000 search "test"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Global Options
|
|
150
|
+
|
|
151
|
+
- `--url URL` - Override backend API URL
|
|
152
|
+
- `-v, --verbose` - Enable verbose output
|
|
153
|
+
- `-q, --quiet` - Suppress non-essential output (headers, hints, decorations)
|
|
154
|
+
- `-h, --help` - Show help message
|
|
155
|
+
- `--version` - Show version
|
|
156
|
+
|
|
157
|
+
## Output Formats
|
|
158
|
+
|
|
159
|
+
Most commands support `--json` for machine-readable output:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# JSON for scripting
|
|
163
|
+
onesearch health --json
|
|
164
|
+
onesearch status --json
|
|
165
|
+
onesearch search "test" --json
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Examples
|
|
169
|
+
|
|
170
|
+
### Add and Index a Source
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Add a documents source (creates ID "my-documents")
|
|
174
|
+
onesearch source add "My Documents" /mnt/nas/documents \
|
|
175
|
+
--include "**/*.pdf,**/*.md,**/*.txt" \
|
|
176
|
+
--exclude "**/archive/**"
|
|
177
|
+
|
|
178
|
+
# Start indexing (use the source ID)
|
|
179
|
+
onesearch source reindex my-documents
|
|
180
|
+
|
|
181
|
+
# Monitor progress
|
|
182
|
+
onesearch status my-documents
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> **Docker users:** If the path only exists inside the container, use `--no-validate` to skip local path validation:
|
|
186
|
+
> ```bash
|
|
187
|
+
> onesearch source add "NAS Docs" /data/nas --no-validate
|
|
188
|
+
> ```
|
|
189
|
+
|
|
190
|
+
### Search with Filters
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Search PDFs only
|
|
194
|
+
onesearch search "quarterly report" --type pdf
|
|
195
|
+
|
|
196
|
+
# Search specific source (use source ID)
|
|
197
|
+
onesearch search "meeting notes" --source my-documents
|
|
198
|
+
|
|
199
|
+
# Get more results
|
|
200
|
+
onesearch search "python" --limit 50
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Health Monitoring
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Quick health check (returns non-zero on failure)
|
|
207
|
+
onesearch health || echo "OneSearch is down!"
|
|
208
|
+
|
|
209
|
+
# JSON for monitoring systems
|
|
210
|
+
onesearch health --json | jq '.status'
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Development
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Install CLI in development mode
|
|
217
|
+
pip install -e .
|
|
218
|
+
|
|
219
|
+
# Run tests (install pytest first)
|
|
220
|
+
pip install pytest
|
|
221
|
+
pytest
|
|
222
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
onesearch/__init__.py,sha256=CNzQ2BVpOiKjVj9EVq-K42qipFQTcmqJyouwfP5QR2A,159
|
|
2
|
+
onesearch/api.py,sha256=rLqWMcBU8fcI4wrCu-BSdk_I9zMqRu5EHz5YKh03jkE,7819
|
|
3
|
+
onesearch/banner.py,sha256=bBUaJ57kJuLtVYHogc628A8dvA_YdygvpFzFdCzr3XE,2244
|
|
4
|
+
onesearch/config.py,sha256=_0lMbXk3Hy6chW1pmPMVkK8kSBPfmqTsl6PTsbSh98s,4252
|
|
5
|
+
onesearch/context.py,sha256=kMCRiR7_fTbISktPevZDmhQqQ8J6tdBMc1pKRyYOxcc,1382
|
|
6
|
+
onesearch/main.py,sha256=ZmRglroIaAjrgTFhSY1p6EMuYVSJx3ZLzmLIT5RhzSM,4360
|
|
7
|
+
onesearch/commands/__init__.py,sha256=VvkpGc4aV5zn_mUTyW06pKm_wlcCkSIvevmyM5O_v3M,102
|
|
8
|
+
onesearch/commands/auth.py,sha256=Jh_VKaSJtHcqboh-jHNA9yzkTVVW8ysE6c4nTaFwuho,2040
|
|
9
|
+
onesearch/commands/config.py,sha256=kixtrN5aFi8_JfXb9nhcjDgjk7v7RCZmoQn29wqQcTM,4761
|
|
10
|
+
onesearch/commands/search.py,sha256=Oxn6y8tQnXY_Msf22B08PfB5taijZkwGtXKaRtMMlFI,4833
|
|
11
|
+
onesearch/commands/source.py,sha256=snNnCtoAuQOw1Bh9J6SYCrhE8Uw-_fYyMupnE-043tc,8256
|
|
12
|
+
onesearch/commands/status.py,sha256=2kqJAPMlD467QRn0z480T1MFOQmG1cnir62rE4s1Xb4,8191
|
|
13
|
+
onesearch_cli-0.12.1.dist-info/METADATA,sha256=zNg24frjkscREjp7dpcKO-lXDRzc-JXdpobCQj_OV3s,4623
|
|
14
|
+
onesearch_cli-0.12.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
15
|
+
onesearch_cli-0.12.1.dist-info/entry_points.txt,sha256=WdfuKdCaH4rLJIwtZZBmC3zZ_l0PyGNuDvvg5m2UJiE,49
|
|
16
|
+
onesearch_cli-0.12.1.dist-info/RECORD,,
|