rathflow-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.
- rathflow_cli-0.1.0/LICENSE +21 -0
- rathflow_cli-0.1.0/PKG-INFO +164 -0
- rathflow_cli-0.1.0/README.md +133 -0
- rathflow_cli-0.1.0/pyproject.toml +47 -0
- rathflow_cli-0.1.0/rathflow_cli/__init__.py +6 -0
- rathflow_cli-0.1.0/rathflow_cli/__main__.py +6 -0
- rathflow_cli-0.1.0/rathflow_cli/cli.py +161 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/__init__.py +0 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/_common.py +78 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/admin.py +83 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/agent.py +288 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/api_cmd.py +75 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/asset.py +449 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/auth_cmd.py +160 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/billing.py +71 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/config_cmd.py +98 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/memory.py +275 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/org.py +70 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/project.py +223 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/sandbox.py +282 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/session.py +237 -0
- rathflow_cli-0.1.0/rathflow_cli/commands/workflow.py +81 -0
- rathflow_cli-0.1.0/rathflow_cli/config.py +69 -0
- rathflow_cli-0.1.0/rathflow_cli/endpoints.py +207 -0
- rathflow_cli-0.1.0/rathflow_cli/errors.py +70 -0
- rathflow_cli-0.1.0/rathflow_cli/http.py +294 -0
- rathflow_cli-0.1.0/rathflow_cli/output.py +95 -0
- rathflow_cli-0.1.0/rathflow_cli/selftest.py +120 -0
- rathflow_cli-0.1.0/rathflow_cli/state.py +129 -0
- rathflow_cli-0.1.0/rathflow_cli.egg-info/PKG-INFO +164 -0
- rathflow_cli-0.1.0/rathflow_cli.egg-info/SOURCES.txt +34 -0
- rathflow_cli-0.1.0/rathflow_cli.egg-info/dependency_links.txt +1 -0
- rathflow_cli-0.1.0/rathflow_cli.egg-info/entry_points.txt +2 -0
- rathflow_cli-0.1.0/rathflow_cli.egg-info/requires.txt +5 -0
- rathflow_cli-0.1.0/rathflow_cli.egg-info/top_level.txt +1 -0
- rathflow_cli-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rath Team
|
|
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,164 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rathflow-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: RathFlow command line client: sessions, memory, sandboxes, assets and billing over the RathFlow REST API.
|
|
5
|
+
Author: Rath Team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://rathflow.lynwe.com/
|
|
8
|
+
Project-URL: Repository, https://github.com/Rath-Team/rathflow-cli
|
|
9
|
+
Project-URL: Issues, https://github.com/Rath-Team/rathflow-cli/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/Rath-Team/rathflow-cli/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: rathflow,cli,agent,sessions,memory,sandbox
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
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: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: httpx>=0.27
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Provides-Extra: socks
|
|
29
|
+
Requires-Dist: httpx[socks]; extra == "socks"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# rathflow-cli
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/rathflow-cli/)
|
|
35
|
+
[](https://pypi.org/project/rathflow-cli/)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
|
|
38
|
+
The RathFlow command line client. It talks to the RathFlow northbound REST API and
|
|
39
|
+
covers sessions, memory, sandboxes, agents, assets, projects, billing and
|
|
40
|
+
workflows.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv tool install rathflow-cli
|
|
46
|
+
# or
|
|
47
|
+
pipx install rathflow-cli
|
|
48
|
+
# or
|
|
49
|
+
pip install rathflow-cli
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Requires Python 3.10+.
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Log in with your email and password (the password prompt does not echo).
|
|
58
|
+
rathflow auth login -e you@example.com
|
|
59
|
+
|
|
60
|
+
# Who am I, what can I reach.
|
|
61
|
+
rathflow whoami
|
|
62
|
+
|
|
63
|
+
# Pick a project scope; every later command carries it.
|
|
64
|
+
rathflow project list
|
|
65
|
+
rathflow project use <project_id>
|
|
66
|
+
|
|
67
|
+
# Read something.
|
|
68
|
+
rathflow session list
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The default gateway is `https://rathflow.lynwe.com`. Point it elsewhere with
|
|
72
|
+
`--base-url`, `RATHFLOW_BASE_URL`, or `rathflow config set base_url <url>`.
|
|
73
|
+
|
|
74
|
+
## Commands
|
|
75
|
+
|
|
76
|
+
| Group | What it covers |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `auth` | log in / out, register, API keys (`keys`, `key-create`, `key-revoke`) |
|
|
79
|
+
| `config` | local profiles (`list`, `show`, `set`, `use`) |
|
|
80
|
+
| `org` | organizations, invitations |
|
|
81
|
+
| `project` | projects, scope switching, project config, share links |
|
|
82
|
+
| `session` | sessions, blocks, events |
|
|
83
|
+
| `memory` | memory entries, search, commit tasks |
|
|
84
|
+
| `sandbox` | sandboxes, exec, code, files, logs |
|
|
85
|
+
| `agent` | agent definitions, versions, prompts, runs |
|
|
86
|
+
| `asset` | assets, versions, credentials, publish |
|
|
87
|
+
| `billing` | subscription, usage, invoices |
|
|
88
|
+
| `workflow` | workflows |
|
|
89
|
+
| `admin` | platform administration (platform admins only) |
|
|
90
|
+
|
|
91
|
+
Two more entry points sit at the root:
|
|
92
|
+
|
|
93
|
+
- `rathflow whoami` — identity, scope and reachable projects.
|
|
94
|
+
- `rathflow api <Key>` — call any endpoint directly; `rathflow api --list` prints
|
|
95
|
+
the whole table. Streaming endpoints stream.
|
|
96
|
+
|
|
97
|
+
Every group has `--help`, e.g. `rathflow session --help`.
|
|
98
|
+
|
|
99
|
+
## Authentication
|
|
100
|
+
|
|
101
|
+
`rathflow auth login` exchanges your email and password for a JWT, stored in the
|
|
102
|
+
CLI config file (mode `0600`). The CLI sends it as a `Bearer` token and never
|
|
103
|
+
touches cookies, so it is unaffected by the browser CSRF rules.
|
|
104
|
+
|
|
105
|
+
For unattended use, issue a long-lived API key and pass it through the
|
|
106
|
+
environment:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
rathflow auth key-create # prints the key once, right here
|
|
110
|
+
export RATHFLOW_TOKEN=rf_... # or: rathflow config set token rf_...
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Configuration
|
|
114
|
+
|
|
115
|
+
Precedence, highest first: command-line flag > environment variable > active
|
|
116
|
+
profile > built-in default. Profiles live in
|
|
117
|
+
`~/.config/rathflow/config.json` (override the directory with
|
|
118
|
+
`RATHFLOW_CONFIG_DIR`).
|
|
119
|
+
|
|
120
|
+
| Variable | Meaning |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `RATHFLOW_BASE_URL` | Gateway address |
|
|
123
|
+
| `RATHFLOW_PROJECT` | Project scope |
|
|
124
|
+
| `RATHFLOW_TOKEN` | Bearer token / API key |
|
|
125
|
+
| `RATHFLOW_CONFIG_DIR` | Config directory |
|
|
126
|
+
|
|
127
|
+
## Output
|
|
128
|
+
|
|
129
|
+
- Default: human-readable tables.
|
|
130
|
+
- `--json`: one stable JSON document, for scripts.
|
|
131
|
+
- `--quiet` / `-q`: primary keys only.
|
|
132
|
+
|
|
133
|
+
Exit codes are stable: `0` success, and non-zero for usage errors, API errors and
|
|
134
|
+
remote command failures (a sandbox `exec` returns the remote exit code).
|
|
135
|
+
|
|
136
|
+
## Proxies
|
|
137
|
+
|
|
138
|
+
The CLI respects the standard proxy variables. One caveat: `httpx` cannot use a
|
|
139
|
+
SOCKS proxy unless `socksio` is installed. If your environment sets
|
|
140
|
+
`ALL_PROXY=socks://...` (some Clash setups do), install the extra or the CLI
|
|
141
|
+
fails to start with `Unknown scheme for proxy URL`:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
uv tool install 'rathflow-cli[socks]'
|
|
145
|
+
# or clear it for this command only
|
|
146
|
+
ALL_PROXY= rathflow whoami
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
uv venv --python 3.12 .venv
|
|
153
|
+
uv pip install -e .
|
|
154
|
+
.venv/bin/rathflow --help
|
|
155
|
+
.venv/bin/python -m rathflow_cli.selftest
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`selftest` checks that no command hard-codes a URL and, when the upstream
|
|
159
|
+
generated endpoint table is available (`RATHFLOW_ENDPOINTS_TS`), that
|
|
160
|
+
`rathflow_cli/endpoints.py` still matches it.
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# rathflow-cli
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/rathflow-cli/)
|
|
4
|
+
[](https://pypi.org/project/rathflow-cli/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
The RathFlow command line client. It talks to the RathFlow northbound REST API and
|
|
8
|
+
covers sessions, memory, sandboxes, agents, assets, projects, billing and
|
|
9
|
+
workflows.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv tool install rathflow-cli
|
|
15
|
+
# or
|
|
16
|
+
pipx install rathflow-cli
|
|
17
|
+
# or
|
|
18
|
+
pip install rathflow-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Python 3.10+.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Log in with your email and password (the password prompt does not echo).
|
|
27
|
+
rathflow auth login -e you@example.com
|
|
28
|
+
|
|
29
|
+
# Who am I, what can I reach.
|
|
30
|
+
rathflow whoami
|
|
31
|
+
|
|
32
|
+
# Pick a project scope; every later command carries it.
|
|
33
|
+
rathflow project list
|
|
34
|
+
rathflow project use <project_id>
|
|
35
|
+
|
|
36
|
+
# Read something.
|
|
37
|
+
rathflow session list
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The default gateway is `https://rathflow.lynwe.com`. Point it elsewhere with
|
|
41
|
+
`--base-url`, `RATHFLOW_BASE_URL`, or `rathflow config set base_url <url>`.
|
|
42
|
+
|
|
43
|
+
## Commands
|
|
44
|
+
|
|
45
|
+
| Group | What it covers |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `auth` | log in / out, register, API keys (`keys`, `key-create`, `key-revoke`) |
|
|
48
|
+
| `config` | local profiles (`list`, `show`, `set`, `use`) |
|
|
49
|
+
| `org` | organizations, invitations |
|
|
50
|
+
| `project` | projects, scope switching, project config, share links |
|
|
51
|
+
| `session` | sessions, blocks, events |
|
|
52
|
+
| `memory` | memory entries, search, commit tasks |
|
|
53
|
+
| `sandbox` | sandboxes, exec, code, files, logs |
|
|
54
|
+
| `agent` | agent definitions, versions, prompts, runs |
|
|
55
|
+
| `asset` | assets, versions, credentials, publish |
|
|
56
|
+
| `billing` | subscription, usage, invoices |
|
|
57
|
+
| `workflow` | workflows |
|
|
58
|
+
| `admin` | platform administration (platform admins only) |
|
|
59
|
+
|
|
60
|
+
Two more entry points sit at the root:
|
|
61
|
+
|
|
62
|
+
- `rathflow whoami` — identity, scope and reachable projects.
|
|
63
|
+
- `rathflow api <Key>` — call any endpoint directly; `rathflow api --list` prints
|
|
64
|
+
the whole table. Streaming endpoints stream.
|
|
65
|
+
|
|
66
|
+
Every group has `--help`, e.g. `rathflow session --help`.
|
|
67
|
+
|
|
68
|
+
## Authentication
|
|
69
|
+
|
|
70
|
+
`rathflow auth login` exchanges your email and password for a JWT, stored in the
|
|
71
|
+
CLI config file (mode `0600`). The CLI sends it as a `Bearer` token and never
|
|
72
|
+
touches cookies, so it is unaffected by the browser CSRF rules.
|
|
73
|
+
|
|
74
|
+
For unattended use, issue a long-lived API key and pass it through the
|
|
75
|
+
environment:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
rathflow auth key-create # prints the key once, right here
|
|
79
|
+
export RATHFLOW_TOKEN=rf_... # or: rathflow config set token rf_...
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
Precedence, highest first: command-line flag > environment variable > active
|
|
85
|
+
profile > built-in default. Profiles live in
|
|
86
|
+
`~/.config/rathflow/config.json` (override the directory with
|
|
87
|
+
`RATHFLOW_CONFIG_DIR`).
|
|
88
|
+
|
|
89
|
+
| Variable | Meaning |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `RATHFLOW_BASE_URL` | Gateway address |
|
|
92
|
+
| `RATHFLOW_PROJECT` | Project scope |
|
|
93
|
+
| `RATHFLOW_TOKEN` | Bearer token / API key |
|
|
94
|
+
| `RATHFLOW_CONFIG_DIR` | Config directory |
|
|
95
|
+
|
|
96
|
+
## Output
|
|
97
|
+
|
|
98
|
+
- Default: human-readable tables.
|
|
99
|
+
- `--json`: one stable JSON document, for scripts.
|
|
100
|
+
- `--quiet` / `-q`: primary keys only.
|
|
101
|
+
|
|
102
|
+
Exit codes are stable: `0` success, and non-zero for usage errors, API errors and
|
|
103
|
+
remote command failures (a sandbox `exec` returns the remote exit code).
|
|
104
|
+
|
|
105
|
+
## Proxies
|
|
106
|
+
|
|
107
|
+
The CLI respects the standard proxy variables. One caveat: `httpx` cannot use a
|
|
108
|
+
SOCKS proxy unless `socksio` is installed. If your environment sets
|
|
109
|
+
`ALL_PROXY=socks://...` (some Clash setups do), install the extra or the CLI
|
|
110
|
+
fails to start with `Unknown scheme for proxy URL`:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uv tool install 'rathflow-cli[socks]'
|
|
114
|
+
# or clear it for this command only
|
|
115
|
+
ALL_PROXY= rathflow whoami
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv venv --python 3.12 .venv
|
|
122
|
+
uv pip install -e .
|
|
123
|
+
.venv/bin/rathflow --help
|
|
124
|
+
.venv/bin/python -m rathflow_cli.selftest
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`selftest` checks that no command hard-codes a URL and, when the upstream
|
|
128
|
+
generated endpoint table is available (`RATHFLOW_ENDPOINTS_TS`), that
|
|
129
|
+
`rathflow_cli/endpoints.py` still matches it.
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rathflow-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "RathFlow command line client: sessions, memory, sandboxes, assets and billing over the RathFlow REST API."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Rath Team" }]
|
|
14
|
+
keywords = ["rathflow", "cli", "agent", "sessions", "memory", "sandbox"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Software Development",
|
|
26
|
+
"Topic :: Utilities",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"httpx>=0.27",
|
|
30
|
+
"typer>=0.12",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
# 走 SOCKS 代理(ALL_PROXY=socks5://…)时需要,见 README「代理」一节。
|
|
35
|
+
socks = ["httpx[socks]"]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://rathflow.lynwe.com/"
|
|
39
|
+
Repository = "https://github.com/Rath-Team/rathflow-cli"
|
|
40
|
+
Issues = "https://github.com/Rath-Team/rathflow-cli/issues"
|
|
41
|
+
Changelog = "https://github.com/Rath-Team/rathflow-cli/blob/main/CHANGELOG.md"
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
rathflow = "rathflow_cli.cli:main"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
include = ["rathflow_cli*"]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""typer 根应用与全局选项。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from . import __version__, errors, state as state_mod
|
|
10
|
+
from .state import State
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(
|
|
13
|
+
add_completion=False,
|
|
14
|
+
no_args_is_help=True,
|
|
15
|
+
help="RathFlow 命令行客户端(JWT 登录;命令覆盖北向 REST 全部端点)。",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _print_version(value: bool) -> None:
|
|
20
|
+
if value:
|
|
21
|
+
typer.echo(f"rathflow {__version__}")
|
|
22
|
+
raise typer.Exit()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@app.callback()
|
|
26
|
+
def root(
|
|
27
|
+
ctx: typer.Context,
|
|
28
|
+
profile: str = typer.Option(None, "--profile", "-p", help="配置档名(缺省 default)"),
|
|
29
|
+
base_url: str = typer.Option(None, "--base-url", help="gateway 地址(env RATHFLOW_BASE_URL)"),
|
|
30
|
+
project: str = typer.Option(None, "--project", help="项目作用域(env RATHFLOW_PROJECT)"),
|
|
31
|
+
json_out: bool = typer.Option(False, "--json", help="以 JSON 输出(脚本消费的稳定形态)"),
|
|
32
|
+
quiet: bool = typer.Option(False, "--quiet", "-q", help="只输出主键"),
|
|
33
|
+
version: bool = typer.Option(
|
|
34
|
+
False, "--version", callback=_print_version, is_eager=True, help="显示版本并退出"
|
|
35
|
+
),
|
|
36
|
+
) -> None:
|
|
37
|
+
ctx.obj = state_mod.install(
|
|
38
|
+
State(
|
|
39
|
+
profile_name=profile,
|
|
40
|
+
base_url=base_url,
|
|
41
|
+
project=project,
|
|
42
|
+
json_out=json_out,
|
|
43
|
+
quiet=quiet,
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _register() -> None:
|
|
49
|
+
from .commands import (
|
|
50
|
+
admin,
|
|
51
|
+
agent,
|
|
52
|
+
api_cmd,
|
|
53
|
+
asset,
|
|
54
|
+
auth_cmd,
|
|
55
|
+
billing,
|
|
56
|
+
config_cmd,
|
|
57
|
+
memory,
|
|
58
|
+
org,
|
|
59
|
+
project,
|
|
60
|
+
sandbox,
|
|
61
|
+
session,
|
|
62
|
+
workflow,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
app.add_typer(auth_cmd.app, name="auth")
|
|
66
|
+
app.add_typer(config_cmd.app, name="config")
|
|
67
|
+
app.add_typer(org.app, name="org")
|
|
68
|
+
app.add_typer(project.app, name="project")
|
|
69
|
+
app.add_typer(session.app, name="session")
|
|
70
|
+
app.add_typer(memory.app, name="memory")
|
|
71
|
+
app.add_typer(sandbox.app, name="sandbox")
|
|
72
|
+
app.add_typer(agent.app, name="agent")
|
|
73
|
+
app.add_typer(asset.app, name="asset")
|
|
74
|
+
app.add_typer(billing.app, name="billing")
|
|
75
|
+
app.add_typer(workflow.app, name="workflow")
|
|
76
|
+
app.add_typer(admin.app, name="admin")
|
|
77
|
+
|
|
78
|
+
from .commands.auth_cmd import whoami
|
|
79
|
+
|
|
80
|
+
app.command("whoami", help="显示登录身份、作用域与可访问项目")(whoami)
|
|
81
|
+
# 逃生舱直接挂根:`rathflow api <Key>` 不必多打一层 `call`
|
|
82
|
+
app.command("api", help="任意端点调用(`rathflow api --list` 看全部)")(api_cmd.call)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
_register()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# 可以挪到命令行最前的全局选项:前者是布尔旗标,后者各吃一个值。
|
|
89
|
+
_HOIST_FLAGS = frozenset({"--json", "--quiet"})
|
|
90
|
+
_HOIST_OPTS = frozenset({"--profile", "--base-url", "--project"})
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _value_opts() -> set[str]:
|
|
94
|
+
"""全 app 里「吃下一个词」的选项名 —— 用来判断挪旗标安不安全。
|
|
95
|
+
|
|
96
|
+
走 click 命令树而非 Typer 对象:Typer 只是注册表,click Group 是懒构建的。
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def walk(cmd) -> set[str]:
|
|
100
|
+
found: set[str] = set()
|
|
101
|
+
for param in getattr(cmd, "params", []):
|
|
102
|
+
if getattr(param, "is_flag", False) or getattr(param, "count", False):
|
|
103
|
+
continue
|
|
104
|
+
found.update(getattr(param, "opts", []))
|
|
105
|
+
for sub in getattr(cmd, "commands", {}).values():
|
|
106
|
+
found |= walk(sub)
|
|
107
|
+
return found
|
|
108
|
+
|
|
109
|
+
return walk(typer.main.get_command(app))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _hoist_globals(argv: list[str]) -> list[str]:
|
|
113
|
+
"""把写在命令**后面**的全局选项挪到最前。
|
|
114
|
+
|
|
115
|
+
click 只认组级选项出现在子命令之前,故 `rathflow project list --json` 会被
|
|
116
|
+
当成给 `list` 传参而报错。脚本里这个顺序太自然了,于是这里做一次改写:
|
|
117
|
+
只挪白名单里的选项,且前一个词不是吃值的选项(否则那个词是被当值用的,
|
|
118
|
+
例如 `--body --json` 里的 `--json` 是值,不能动)。
|
|
119
|
+
"""
|
|
120
|
+
if len(argv) < 2:
|
|
121
|
+
return argv
|
|
122
|
+
value_opts = _value_opts()
|
|
123
|
+
head, tail = [argv[0]], []
|
|
124
|
+
hoisted: list[str] = []
|
|
125
|
+
idx = 1
|
|
126
|
+
while idx < len(argv):
|
|
127
|
+
tok = argv[idx]
|
|
128
|
+
prev = argv[idx - 1]
|
|
129
|
+
# 前一个词若是吃值的选项,本词就是它的值(click 也这么看),别动
|
|
130
|
+
safe = prev not in value_opts
|
|
131
|
+
if safe and tok in _HOIST_FLAGS:
|
|
132
|
+
hoisted.append(tok)
|
|
133
|
+
idx += 1
|
|
134
|
+
continue
|
|
135
|
+
if safe and tok in _HOIST_OPTS and idx + 1 < len(argv) and not argv[idx + 1].startswith("-"):
|
|
136
|
+
hoisted.extend([tok, argv[idx + 1]])
|
|
137
|
+
idx += 2
|
|
138
|
+
continue
|
|
139
|
+
tail.append(tok)
|
|
140
|
+
idx += 1
|
|
141
|
+
return head + hoisted + tail
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def main() -> None:
|
|
145
|
+
sys.argv = _hoist_globals(sys.argv)
|
|
146
|
+
try:
|
|
147
|
+
app()
|
|
148
|
+
except errors.UsageError as exc:
|
|
149
|
+
print(f"错误:{exc}", file=sys.stderr)
|
|
150
|
+
sys.exit(errors.EXIT_USAGE)
|
|
151
|
+
except errors.ApiError as exc:
|
|
152
|
+
print(f"错误:{exc}", file=sys.stderr)
|
|
153
|
+
for detail in exc.details:
|
|
154
|
+
print(f" - {detail}", file=sys.stderr)
|
|
155
|
+
sys.exit(exc.exit_code)
|
|
156
|
+
except KeyboardInterrupt: # pragma: no cover
|
|
157
|
+
sys.exit(130)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__": # pragma: no cover
|
|
161
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""命令间共用的小工具(参数解析与请求体装配)。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from ..errors import UsageError
|
|
11
|
+
|
|
12
|
+
PATH_OPT = typer.Option(None, "--path", help="路径参数 k=v,可重复(如 --path session_id=abc)")
|
|
13
|
+
QUERY_OPT = typer.Option(None, "--query", help="查询参数 k=v,可重复")
|
|
14
|
+
BODY_OPT = typer.Option(None, "--body", help="请求体 JSON 字面量")
|
|
15
|
+
BODY_FILE_OPT = typer.Option(None, "--body-file", help="从文件读请求体 JSON(- 读 stdin)")
|
|
16
|
+
PAGE_SIZE_OPT = typer.Option(None, "--page-size", help="分页大小")
|
|
17
|
+
PAGE_TOKEN_OPT = typer.Option(None, "--page-token", help="分页游标")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def pairs(items: list[str] | None) -> dict:
|
|
21
|
+
"""['k=v', ...] → {k: v};值里的 = 保留(只切第一个)。"""
|
|
22
|
+
out: dict[str, str] = {}
|
|
23
|
+
for item in items or []:
|
|
24
|
+
if "=" not in item:
|
|
25
|
+
raise UsageError(f"参数需形如 k=v,收到 {item!r}")
|
|
26
|
+
key, _, value = item.partition("=")
|
|
27
|
+
out[key.strip()] = value
|
|
28
|
+
return out
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def body_of(body: str | None, body_file: str | None) -> dict | None:
|
|
32
|
+
"""装配请求体:字面量或文件(`-` = stdin)。两者互斥。"""
|
|
33
|
+
if body and body_file:
|
|
34
|
+
raise UsageError("--body 与 --body-file 不能同时用")
|
|
35
|
+
raw: str | None = None
|
|
36
|
+
if body_file:
|
|
37
|
+
raw = sys.stdin.read() if body_file == "-" else _read(body_file)
|
|
38
|
+
elif body:
|
|
39
|
+
raw = body
|
|
40
|
+
else:
|
|
41
|
+
return None
|
|
42
|
+
raw = raw.strip()
|
|
43
|
+
if not raw:
|
|
44
|
+
return {}
|
|
45
|
+
try:
|
|
46
|
+
parsed = json.loads(raw)
|
|
47
|
+
except json.JSONDecodeError as exc:
|
|
48
|
+
raise UsageError(f"请求体不是合法 JSON:{exc}") from None
|
|
49
|
+
if not isinstance(parsed, dict):
|
|
50
|
+
raise UsageError("请求体必须是 JSON 对象")
|
|
51
|
+
return parsed
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _read(path: str) -> str:
|
|
55
|
+
try:
|
|
56
|
+
with open(path, encoding="utf-8") as fh:
|
|
57
|
+
return fh.read()
|
|
58
|
+
except OSError as exc:
|
|
59
|
+
raise UsageError(f"读取请求体文件失败:{exc}") from None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def pagination(page_size: int | None, page_token: str | None, extra: dict | None = None) -> dict:
|
|
63
|
+
"""装配分页查询串(protojson 查询名用 proto 字段名 snake_case)。"""
|
|
64
|
+
query = dict(extra or {})
|
|
65
|
+
if page_size is not None:
|
|
66
|
+
query["page_size"] = page_size
|
|
67
|
+
if page_token:
|
|
68
|
+
query["page_token"] = page_token
|
|
69
|
+
return query
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def id_of(payload: dict, *keys: str) -> str:
|
|
73
|
+
"""从响应里挑主键用于 -q 输出。"""
|
|
74
|
+
for key in keys:
|
|
75
|
+
value = (payload or {}).get(key)
|
|
76
|
+
if value:
|
|
77
|
+
return str(value)
|
|
78
|
+
return ""
|