mantos 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.
- mantos-0.1.0/.gitignore +1 -0
- mantos-0.1.0/PKG-INFO +99 -0
- mantos-0.1.0/README.md +77 -0
- mantos-0.1.0/pyproject.toml +107 -0
- mantos-0.1.0/src/mantos_cli/__init__.py +26 -0
- mantos-0.1.0/src/mantos_cli/__main__.py +20 -0
- mantos-0.1.0/src/mantos_cli/cli.py +388 -0
- mantos-0.1.0/src/mantos_cli/client.py +144 -0
- mantos-0.1.0/src/mantos_cli/config.py +250 -0
- mantos-0.1.0/src/mantos_cli/configfile.py +142 -0
- mantos-0.1.0/src/mantos_cli/exits.py +55 -0
- mantos-0.1.0/src/mantos_cli/keyform.py +64 -0
- mantos-0.1.0/src/mantos_cli/streams.py +53 -0
mantos-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dist/
|
mantos-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mantos
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The Mantos CLI — reach your Mantos tenant's API from a terminal
|
|
5
|
+
Project-URL: Homepage, https://mantos.cloud
|
|
6
|
+
Author: Systematic Labs LLC
|
|
7
|
+
License-Expression: LicenseRef-Proprietary
|
|
8
|
+
Keywords: agents,api,cli,mantos
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: Other/Proprietary License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: click>=8.2
|
|
20
|
+
Requires-Dist: httpx>=0.25
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# mantos
|
|
24
|
+
|
|
25
|
+
The command-line client for [Mantos](https://mantos.cloud) — reach your tenant's
|
|
26
|
+
API from a terminal, a CI job, a container, or an agent session.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pipx install mantos # or: pip install mantos
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## One environment variable
|
|
33
|
+
|
|
34
|
+
A Mantos API key carries its own tenant slug, so the key is the whole
|
|
35
|
+
configuration:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
export MANTOS_API_KEY=mtk_<tenant>_<keyid>_<secret>
|
|
39
|
+
|
|
40
|
+
mantos api GET api/tenant/v1/envs/dev/agents
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
There is nothing else to set — no host, no tenant, no profile. Mint a key in
|
|
44
|
+
the Mantos console (**Settings → API keys**); the secret is shown once, at mint,
|
|
45
|
+
and is not recoverable afterwards.
|
|
46
|
+
|
|
47
|
+
## Commands
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
mantos api METHOD PATH [--field K=V] [--raw-field K=V] [--json] [--timeout S]
|
|
51
|
+
mantos config set-host HOST
|
|
52
|
+
mantos config show
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`mantos api` reaches any tenant API path directly. `PATH` includes any query
|
|
56
|
+
string, and a leading slash is optional — on Git Bash, omitting it avoids the
|
|
57
|
+
shell rewriting the argument into a Windows filesystem path before the CLI sees
|
|
58
|
+
it.
|
|
59
|
+
|
|
60
|
+
`--field K=V` builds a JSON body, parsing `V` as JSON when it parses and sending
|
|
61
|
+
it as a string when it does not; `--raw-field` always sends a string. Neither is
|
|
62
|
+
accepted on `GET` or `HEAD`, where the API takes no body — silently dropping a
|
|
63
|
+
field would let you believe a filter was applied that never was.
|
|
64
|
+
|
|
65
|
+
## Output, so a parser and a person can both read it
|
|
66
|
+
|
|
67
|
+
The response body is the only thing written to **stdout**. The status line for
|
|
68
|
+
a failure goes to **stderr**. Pipe stdout straight into `jq` without stripping
|
|
69
|
+
anything, and still see why a call went red.
|
|
70
|
+
|
|
71
|
+
## Exit codes
|
|
72
|
+
|
|
73
|
+
| Code | Meaning |
|
|
74
|
+
|------|---------|
|
|
75
|
+
| `0` | The request succeeded (2xx). |
|
|
76
|
+
| `1` | The request was made and did not succeed — any non-2xx other than 401, plus a transport failure. |
|
|
77
|
+
| `2` | The invocation was wrong, or `MANTOS_API_KEY` is not set. Nothing was sent. |
|
|
78
|
+
| `3` | The credential was not accepted (401). |
|
|
79
|
+
|
|
80
|
+
A **403 is `1`, not `3`**. Authenticated-but-refused is a different fact from
|
|
81
|
+
credential-rejected: a key whose grants are narrower than its owner's authority
|
|
82
|
+
is *supposed* to be refused inside a scope it only partly holds, and that is the
|
|
83
|
+
authorization gate working rather than a broken key.
|
|
84
|
+
|
|
85
|
+
## Host resolution
|
|
86
|
+
|
|
87
|
+
`MANTOS_HOST` › the tenant named by the key › a host stored with
|
|
88
|
+
`mantos config set-host`.
|
|
89
|
+
|
|
90
|
+
The environment variable wins so a support session, a smoke test, or a
|
|
91
|
+
one-off against another deployment needs no stored state and leaves none behind.
|
|
92
|
+
A stored host is the last resort precisely because it is invisible: a pin left
|
|
93
|
+
behind by an earlier session must not silently outrank the credential in front
|
|
94
|
+
of it.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
Mantos is a product of Systematic Labs LLC. This client talks to a deployed
|
|
99
|
+
Mantos tenant over HTTPS and contains no server code.
|
mantos-0.1.0/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# mantos
|
|
2
|
+
|
|
3
|
+
The command-line client for [Mantos](https://mantos.cloud) — reach your tenant's
|
|
4
|
+
API from a terminal, a CI job, a container, or an agent session.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pipx install mantos # or: pip install mantos
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## One environment variable
|
|
11
|
+
|
|
12
|
+
A Mantos API key carries its own tenant slug, so the key is the whole
|
|
13
|
+
configuration:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
export MANTOS_API_KEY=mtk_<tenant>_<keyid>_<secret>
|
|
17
|
+
|
|
18
|
+
mantos api GET api/tenant/v1/envs/dev/agents
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
There is nothing else to set — no host, no tenant, no profile. Mint a key in
|
|
22
|
+
the Mantos console (**Settings → API keys**); the secret is shown once, at mint,
|
|
23
|
+
and is not recoverable afterwards.
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
mantos api METHOD PATH [--field K=V] [--raw-field K=V] [--json] [--timeout S]
|
|
29
|
+
mantos config set-host HOST
|
|
30
|
+
mantos config show
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`mantos api` reaches any tenant API path directly. `PATH` includes any query
|
|
34
|
+
string, and a leading slash is optional — on Git Bash, omitting it avoids the
|
|
35
|
+
shell rewriting the argument into a Windows filesystem path before the CLI sees
|
|
36
|
+
it.
|
|
37
|
+
|
|
38
|
+
`--field K=V` builds a JSON body, parsing `V` as JSON when it parses and sending
|
|
39
|
+
it as a string when it does not; `--raw-field` always sends a string. Neither is
|
|
40
|
+
accepted on `GET` or `HEAD`, where the API takes no body — silently dropping a
|
|
41
|
+
field would let you believe a filter was applied that never was.
|
|
42
|
+
|
|
43
|
+
## Output, so a parser and a person can both read it
|
|
44
|
+
|
|
45
|
+
The response body is the only thing written to **stdout**. The status line for
|
|
46
|
+
a failure goes to **stderr**. Pipe stdout straight into `jq` without stripping
|
|
47
|
+
anything, and still see why a call went red.
|
|
48
|
+
|
|
49
|
+
## Exit codes
|
|
50
|
+
|
|
51
|
+
| Code | Meaning |
|
|
52
|
+
|------|---------|
|
|
53
|
+
| `0` | The request succeeded (2xx). |
|
|
54
|
+
| `1` | The request was made and did not succeed — any non-2xx other than 401, plus a transport failure. |
|
|
55
|
+
| `2` | The invocation was wrong, or `MANTOS_API_KEY` is not set. Nothing was sent. |
|
|
56
|
+
| `3` | The credential was not accepted (401). |
|
|
57
|
+
|
|
58
|
+
A **403 is `1`, not `3`**. Authenticated-but-refused is a different fact from
|
|
59
|
+
credential-rejected: a key whose grants are narrower than its owner's authority
|
|
60
|
+
is *supposed* to be refused inside a scope it only partly holds, and that is the
|
|
61
|
+
authorization gate working rather than a broken key.
|
|
62
|
+
|
|
63
|
+
## Host resolution
|
|
64
|
+
|
|
65
|
+
`MANTOS_HOST` › the tenant named by the key › a host stored with
|
|
66
|
+
`mantos config set-host`.
|
|
67
|
+
|
|
68
|
+
The environment variable wins so a support session, a smoke test, or a
|
|
69
|
+
one-off against another deployment needs no stored state and leaves none behind.
|
|
70
|
+
A stored host is the last resort precisely because it is invisible: a pin left
|
|
71
|
+
behind by an earlier session must not silently outrank the credential in front
|
|
72
|
+
of it.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
Mantos is a product of Systematic Labs LLC. This client talks to a deployed
|
|
77
|
+
Mantos tenant over HTTPS and contains no server code.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# The `mantos` CLI — its own distribution, and the reason this directory exists.
|
|
2
|
+
#
|
|
3
|
+
# 🔴 **Why the source MOVED rather than getting a pyproject alongside it.**
|
|
4
|
+
# Hatchling refuses any package path outside the project root. Measured, not
|
|
5
|
+
# assumed: `packages = ["../src/mantos_cli"]` fails the build with
|
|
6
|
+
# `Path #1 in field 'tool.hatch.build.only-include' must be relative`. A second
|
|
7
|
+
# distribution therefore needs a second project root with its sources *under*
|
|
8
|
+
# it, which is why `src/mantos_cli/` became `cli/src/mantos_cli/`. No build
|
|
9
|
+
# backend we would want takes the parent-relative shortcut — setuptools tolerates
|
|
10
|
+
# it for a wheel and then breaks sdists and editable installs, which is a worse
|
|
11
|
+
# place to discover the same limit.
|
|
12
|
+
#
|
|
13
|
+
# 🔴 **The distribution name `mantos` is the CLIENT** — the thing a tenant
|
|
14
|
+
# installs gets the guessable name. The server distribution is `mantos-server`
|
|
15
|
+
# (repo root), and it was renamed *for this*: two distributions sharing a name
|
|
16
|
+
# means `pip install -e ./cli` in a venv holding the editable server install
|
|
17
|
+
# silently uninstalls the server, leaving `import mantos` resolving to nothing
|
|
18
|
+
# while every CLI command still works. The root pyproject carries the detail.
|
|
19
|
+
#
|
|
20
|
+
# The PACKAGE names never collided and still do not: `mantos` (server) and
|
|
21
|
+
# `mantos_cli` (this). Only the distribution names did.
|
|
22
|
+
[project]
|
|
23
|
+
name = "mantos"
|
|
24
|
+
dynamic = ["version"]
|
|
25
|
+
description = "The Mantos CLI — reach your Mantos tenant's API from a terminal"
|
|
26
|
+
readme = "README.md"
|
|
27
|
+
requires-python = ">=3.11"
|
|
28
|
+
license = "LicenseRef-Proprietary"
|
|
29
|
+
authors = [{ name = "Systematic Labs LLC" }]
|
|
30
|
+
keywords = ["mantos", "cli", "agents", "api"]
|
|
31
|
+
classifiers = [
|
|
32
|
+
"Development Status :: 3 - Alpha",
|
|
33
|
+
"Environment :: Console",
|
|
34
|
+
"Intended Audience :: Developers",
|
|
35
|
+
"License :: Other/Proprietary License",
|
|
36
|
+
"Programming Language :: Python :: 3",
|
|
37
|
+
"Programming Language :: Python :: 3.11",
|
|
38
|
+
"Programming Language :: Python :: 3.12",
|
|
39
|
+
"Programming Language :: Python :: 3.13",
|
|
40
|
+
"Topic :: Utilities",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
# The CLI's whole runtime dependency set, and it is meant to stay this short —
|
|
44
|
+
# a client a tenant installs onto a laptop, a CI runner or an agent session
|
|
45
|
+
# should not drag a stack behind it.
|
|
46
|
+
#
|
|
47
|
+
# 🔴 The click floor is >=8.2 for a behavioural reason, not a feature one:
|
|
48
|
+
# `tests/cli` asserts the response body lands on stdout and the failure line on
|
|
49
|
+
# stderr, which is what lets an agent pipe stdout straight into a parser.
|
|
50
|
+
# Click 8.1's `CliRunner` merges the two and `result.stderr` raises, so a
|
|
51
|
+
# resolver that picked 8.1 would fail those tests in a way that reads like a CLI
|
|
52
|
+
# defect rather than a pin.
|
|
53
|
+
#
|
|
54
|
+
# `tests/cli/test_packaging.py` asserts this list actually covers every
|
|
55
|
+
# third-party module `mantos_cli` imports — declaring one and importing two is
|
|
56
|
+
# invisible in a dev venv, where the server distribution has already installed
|
|
57
|
+
# everything.
|
|
58
|
+
dependencies = [
|
|
59
|
+
"click>=8.2",
|
|
60
|
+
"httpx>=0.25",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[project.urls]
|
|
64
|
+
Homepage = "https://mantos.cloud"
|
|
65
|
+
|
|
66
|
+
# The command name is `mantos` and it is independent of the distribution name —
|
|
67
|
+
# `httpie` installs `http`. Both happen to coincide here; the settled decision
|
|
68
|
+
# is the *command*, recorded in specifications/handoff-programmatic-access.md.
|
|
69
|
+
#
|
|
70
|
+
# This is also the line that could not exist in the root pyproject: a console
|
|
71
|
+
# script in the SERVER wheel pointing at `mantos_cli` would install a command
|
|
72
|
+
# that only fails when someone runs it, on a deployed image, during an incident.
|
|
73
|
+
# `tests/cli/test_packaging.py` still asserts the root declares no such script.
|
|
74
|
+
[project.scripts]
|
|
75
|
+
mantos = "mantos_cli.cli:main"
|
|
76
|
+
|
|
77
|
+
[build-system]
|
|
78
|
+
# >=1.27 for PEP 639 license expressions (`license = "LicenseRef-…"`). The root
|
|
79
|
+
# distribution's floor is >=1.21 and does not need raising for that.
|
|
80
|
+
requires = ["hatchling>=1.27"]
|
|
81
|
+
build-backend = "hatchling.build"
|
|
82
|
+
|
|
83
|
+
# One source of truth for the version: the literal in the package. The CLI
|
|
84
|
+
# deliberately does NOT read `mantos.__version__` — that import would cross the
|
|
85
|
+
# very boundary that makes this distribution separable (`.importlinter` contract
|
|
86
|
+
# `cli-independent-of-server`), so the two version literals are independent by
|
|
87
|
+
# design and are allowed to diverge.
|
|
88
|
+
[tool.hatch.version]
|
|
89
|
+
path = "src/mantos_cli/__init__.py"
|
|
90
|
+
|
|
91
|
+
[tool.hatch.build.targets.wheel]
|
|
92
|
+
packages = ["src/mantos_cli"]
|
|
93
|
+
|
|
94
|
+
[tool.hatch.build.targets.sdist]
|
|
95
|
+
# An explicit list rather than hatchling's default sweep of the project root:
|
|
96
|
+
# the default would ship whatever else ends up in this directory (a `dist/`, a
|
|
97
|
+
# stray venv) into the artifact that gets uploaded to PyPI.
|
|
98
|
+
include = ["src/mantos_cli", "README.md"]
|
|
99
|
+
# ⚠️ `include` does NOT bound the sdist on its own. Hatchling force-includes the
|
|
100
|
+
# `.gitignore` it used for exclusions, and with none in this directory it walked
|
|
101
|
+
# up and shipped the REPO ROOT's — publishing our internal path layout inside a
|
|
102
|
+
# public artifact. `exclude = [".gitignore"]` does not stop it (measured). The
|
|
103
|
+
# fix is the local `cli/.gitignore`, which is why that two-line file exists and
|
|
104
|
+
# must not be deleted as redundant with the root one.
|
|
105
|
+
#
|
|
106
|
+
# Read the built tarball rather than trusting this block —
|
|
107
|
+
# `tests/cli/test_packaging.py` does exactly that.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""The ``mantos`` CLI — a client for the Mantos tenant REST API.
|
|
2
|
+
|
|
3
|
+
🔴 **This package is its own distribution, not part of the server wheel.** Same
|
|
4
|
+
precedent, and the same reason, as ``sandbox_runner`` and ``egress_authz``: a
|
|
5
|
+
tenant should not install the control plane to get a client. Since P4d that is
|
|
6
|
+
structural rather than an exclusion — the package lives under ``cli/``, which
|
|
7
|
+
has its own ``pyproject.toml`` (published as ``mantos``, against the server's
|
|
8
|
+
``mantos-server``). ``tests/cli/test_packaging.py`` asserts it against both
|
|
9
|
+
built artifacts rather than trusting this comment.
|
|
10
|
+
|
|
11
|
+
The corollary is the import boundary: **``mantos_cli`` must never import
|
|
12
|
+
``mantos``.** It talks to the deployed control plane over HTTP and knows
|
|
13
|
+
nothing of its internals — that is what makes it separable at all. The
|
|
14
|
+
``.importlinter`` contract ``cli-independent-of-server`` enforces it, because
|
|
15
|
+
one ``from mantos.control...`` import would undo the separation silently and
|
|
16
|
+
still pass every test.
|
|
17
|
+
|
|
18
|
+
Version is a standalone literal for the same reason: reading
|
|
19
|
+
``mantos.__version__`` would be an import across that boundary.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
|
25
|
+
|
|
26
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""``python -m mantos_cli`` entry point.
|
|
2
|
+
|
|
3
|
+
⚠️ **This module holds nothing but the call.** ``python -m pkg`` imports the
|
|
4
|
+
module a *second* time under the name ``__main__``, so anything defined here —
|
|
5
|
+
a class, an exception, a sentinel — exists twice under two identities, and
|
|
6
|
+
``except`` clauses silently stop matching (memory
|
|
7
|
+
``reference_dash_m_double_import``; #2380). Keeping the file to one import and
|
|
8
|
+
one call makes that unrepresentable.
|
|
9
|
+
|
|
10
|
+
A tenant runs ``mantos``, not this: the console script ships with the CLI's own
|
|
11
|
+
distribution (``cli/pyproject.toml``, P4d). This module stays because
|
|
12
|
+
``python -m mantos_cli`` is what works from a source checkout, before anything
|
|
13
|
+
is installed.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from mantos_cli.cli import main
|
|
19
|
+
|
|
20
|
+
raise SystemExit(main())
|