runinfra-cli 0.2.0__py3-none-win_amd64.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.
- runinfra_cli/__init__.py +211 -0
- runinfra_cli/__main__.py +18 -0
- runinfra_cli/_bin/runinfra.exe +0 -0
- runinfra_cli-0.2.0.dist-info/LICENSE +48 -0
- runinfra_cli-0.2.0.dist-info/METADATA +164 -0
- runinfra_cli-0.2.0.dist-info/RECORD +8 -0
- runinfra_cli-0.2.0.dist-info/WHEEL +4 -0
- runinfra_cli-0.2.0.dist-info/entry_points.txt +2 -0
runinfra_cli/__init__.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Launcher for the RunInfra CLI.
|
|
2
|
+
|
|
3
|
+
`pip install runinfra-cli` puts a `runinfra` command on PATH. That command is
|
|
4
|
+
this module, and its whole job is to hand control to the program shipped
|
|
5
|
+
inside the wheel and then stop existing. Nothing about signing in, about
|
|
6
|
+
entitlement, or about downloading a package lives here. This file only has to
|
|
7
|
+
get out of the way cleanly.
|
|
8
|
+
|
|
9
|
+
Getting out of the way cleanly is the entire design constraint. `runinfra`
|
|
10
|
+
gets run from CI jobs and provisioning scripts that branch on its exit code:
|
|
11
|
+
2 for bad usage, 3 for a sign in problem, 4 when the workspace does not own
|
|
12
|
+
the package, 5 for a transport failure, 6 for an integrity failure, 7 for a
|
|
13
|
+
local disk problem, and 130 when someone pressed Ctrl-C. A launcher that
|
|
14
|
+
turned any of those into a generic 1 would quietly break every one of those
|
|
15
|
+
scripts, and it would break them in the worst way: the caller would retry a
|
|
16
|
+
failure that will never succeed, or give up on one that would have.
|
|
17
|
+
|
|
18
|
+
So on Unix this process calls execv and ceases to exist, which makes exit
|
|
19
|
+
code and signal fidelity not a feature to maintain but a thing that cannot go
|
|
20
|
+
wrong. Windows has no execv with those semantics, so there the launcher stays
|
|
21
|
+
alive as a thin parent, refuses to answer Ctrl-C itself, and returns exactly
|
|
22
|
+
the code its child returned.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import NoReturn
|
|
31
|
+
|
|
32
|
+
__all__ = ["__version__", "binary_path", "main"]
|
|
33
|
+
|
|
34
|
+
# One of four copies of this string. The others are cli/package.json,
|
|
35
|
+
# cli/src/version.ts and cli/pypi/pyproject.toml. build_wheels.py compares all
|
|
36
|
+
# four and refuses to build when they disagree, so this literal cannot drift
|
|
37
|
+
# on its own.
|
|
38
|
+
__version__ = "0.2.0"
|
|
39
|
+
|
|
40
|
+
# Where the wheel keeps the program, relative to this file. The leading
|
|
41
|
+
# underscore marks it as ours: it is not an import package and nothing outside
|
|
42
|
+
# this module should reach into it.
|
|
43
|
+
_PROGRAM_DIR = "_bin"
|
|
44
|
+
|
|
45
|
+
_SUPPORT_URL = "https://runinfra.ai/contact"
|
|
46
|
+
|
|
47
|
+
# Matches the CLI's own code for "this machine cannot run the command", so a
|
|
48
|
+
# script that already branches on 2 keeps working when the failure happens one
|
|
49
|
+
# layer out here instead of inside the program.
|
|
50
|
+
_EXIT_UNSUPPORTED_RUNTIME = 2
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def binary_path() -> Path:
|
|
54
|
+
"""Return the absolute path of the program this installation carries.
|
|
55
|
+
|
|
56
|
+
There is no platform detection to do at run time. pip already did it: it
|
|
57
|
+
chose one wheel out of the release using the wheel's platform tag, and
|
|
58
|
+
that wheel carries exactly one program. All that is left is the file name
|
|
59
|
+
convention of the operating system running right now.
|
|
60
|
+
"""
|
|
61
|
+
name = "runinfra.exe" if os.name == "nt" else "runinfra"
|
|
62
|
+
return Path(__file__).resolve().parent / _PROGRAM_DIR / name
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def main() -> NoReturn:
|
|
66
|
+
"""Run the CLI with this process's arguments, and never return."""
|
|
67
|
+
program = binary_path()
|
|
68
|
+
if not program.is_file():
|
|
69
|
+
_fail_no_program_for_platform(program)
|
|
70
|
+
|
|
71
|
+
argv = [str(program), *sys.argv[1:]]
|
|
72
|
+
|
|
73
|
+
# The embedded program cannot otherwise know which published channel owns
|
|
74
|
+
# its upgrade command. execv and subprocess.run both inherit this value.
|
|
75
|
+
os.environ["RUNINFRA_DIST_CHANNEL"] = "pypi"
|
|
76
|
+
|
|
77
|
+
if os.name == "nt":
|
|
78
|
+
raise SystemExit(_run_on_windows(argv))
|
|
79
|
+
_exec_on_posix(program, argv)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _exec_on_posix(program: Path, argv: list[str]) -> NoReturn:
|
|
83
|
+
"""Replace this process with the CLI.
|
|
84
|
+
|
|
85
|
+
After execv succeeds there is no launcher left, so the process id, the
|
|
86
|
+
controlling terminal, every signal and the exit code all belong to the
|
|
87
|
+
CLI itself. Ctrl-C reaches it directly, which is what lets it finish the
|
|
88
|
+
write it was in the middle of and exit 130 with a resumable download on
|
|
89
|
+
disk.
|
|
90
|
+
|
|
91
|
+
Nothing on this path may install a signal handler first. A disposition of
|
|
92
|
+
"ignore" survives execv, so ignoring Ctrl-C here, which is the correct
|
|
93
|
+
thing to do on the Windows path below, would leave the CLI permanently
|
|
94
|
+
unable to see it.
|
|
95
|
+
"""
|
|
96
|
+
for attempt in (1, 2):
|
|
97
|
+
try:
|
|
98
|
+
os.execv(str(program), argv)
|
|
99
|
+
except PermissionError:
|
|
100
|
+
# Wheels record the execute bit and pip restores it, but a wheel
|
|
101
|
+
# unpacked by hand with a tool that does not carry file modes
|
|
102
|
+
# arrives without it. That is one syscall to repair and a support
|
|
103
|
+
# round trip to diagnose, so repair it once and try again.
|
|
104
|
+
if attempt == 1 and _restore_execute_bit(program):
|
|
105
|
+
continue
|
|
106
|
+
_fail_not_executable(program)
|
|
107
|
+
except OSError as error:
|
|
108
|
+
_fail_to_launch(program, error)
|
|
109
|
+
|
|
110
|
+
_fail_not_executable(program)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _run_on_windows(argv: list[str]) -> int:
|
|
114
|
+
"""Run the CLI as a child process and return its exit code unchanged."""
|
|
115
|
+
import signal
|
|
116
|
+
import subprocess
|
|
117
|
+
|
|
118
|
+
# On Windows, Ctrl-C is delivered to every process attached to the
|
|
119
|
+
# console, this launcher included. If the launcher answered it, Python
|
|
120
|
+
# would raise KeyboardInterrupt here and the shell would see the
|
|
121
|
+
# launcher's exit code instead of the 130 the CLI returns once it has
|
|
122
|
+
# finished its current write. Declining to answer leaves the CLI as the
|
|
123
|
+
# only process that does, so its code is the one that survives.
|
|
124
|
+
try:
|
|
125
|
+
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
126
|
+
except (OSError, ValueError):
|
|
127
|
+
# Only the main thread is allowed to install a handler. Off it, the
|
|
128
|
+
# CLI still receives and handles its own Ctrl-C exactly as before; the
|
|
129
|
+
# only thing lost is the guarantee about whose exit code wins a race
|
|
130
|
+
# that almost never happens. Not worth refusing to run over.
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
completed = subprocess.run(argv)
|
|
135
|
+
except OSError as error:
|
|
136
|
+
_fail_to_launch(Path(argv[0]), error)
|
|
137
|
+
return completed.returncode
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _restore_execute_bit(program: Path) -> bool:
|
|
141
|
+
"""Put the execute bit back, reporting whether it is set afterwards.
|
|
142
|
+
|
|
143
|
+
Returns False rather than raising when the file system refuses, for
|
|
144
|
+
example on a read only installation, so the caller can say what is wrong
|
|
145
|
+
in one sentence instead of unwinding a traceback at the user.
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
program.chmod(program.stat().st_mode | 0o111)
|
|
149
|
+
return bool(program.stat().st_mode & 0o111)
|
|
150
|
+
except OSError:
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _fail_no_program_for_platform(program: Path) -> NoReturn:
|
|
155
|
+
import platform
|
|
156
|
+
|
|
157
|
+
_die(
|
|
158
|
+
_EXIT_UNSUPPORTED_RUNTIME,
|
|
159
|
+
"this installation does not include a program for your platform.",
|
|
160
|
+
"System: {0}, machine: {1}".format(platform.system(), platform.machine()),
|
|
161
|
+
"Expected it at: {0}".format(program),
|
|
162
|
+
"",
|
|
163
|
+
"Reinstall so pip can select the matching build:",
|
|
164
|
+
" pip install --force-reinstall runinfra-cli",
|
|
165
|
+
"",
|
|
166
|
+
"If pip reports no matching distribution, this platform is not",
|
|
167
|
+
"supported yet. Tell us which one you need: {0}".format(_SUPPORT_URL),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _fail_not_executable(program: Path) -> NoReturn:
|
|
172
|
+
_die(
|
|
173
|
+
_EXIT_UNSUPPORTED_RUNTIME,
|
|
174
|
+
"the program is installed but this machine will not run it.",
|
|
175
|
+
"Program: {0}".format(program),
|
|
176
|
+
"",
|
|
177
|
+
"Either the file lost its execute permission, or it sits on a volume",
|
|
178
|
+
"mounted without permission to execute anything on it.",
|
|
179
|
+
"",
|
|
180
|
+
" chmod +x {0}".format(program),
|
|
181
|
+
" pip install --force-reinstall runinfra-cli",
|
|
182
|
+
"",
|
|
183
|
+
"If neither helps: {0}".format(_SUPPORT_URL),
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _fail_to_launch(program: Path, error: OSError) -> NoReturn:
|
|
188
|
+
reason = error.strerror or str(error)
|
|
189
|
+
_die(
|
|
190
|
+
_EXIT_UNSUPPORTED_RUNTIME,
|
|
191
|
+
"could not start the program: {0}".format(reason),
|
|
192
|
+
"Program: {0}".format(program),
|
|
193
|
+
"",
|
|
194
|
+
"Reinstalling replaces the file with a known good copy:",
|
|
195
|
+
" pip install --force-reinstall runinfra-cli",
|
|
196
|
+
"",
|
|
197
|
+
"If it keeps happening: {0}".format(_SUPPORT_URL),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _die(exit_code: int, message: str, *detail: str) -> NoReturn:
|
|
202
|
+
"""Report a launcher failure the way the CLI itself reports failures.
|
|
203
|
+
|
|
204
|
+
One sentence naming what went wrong, then indented lines that name the
|
|
205
|
+
next thing the reader can actually do. Never a traceback: a traceback
|
|
206
|
+
tells the reader about this file, and this file is not their problem.
|
|
207
|
+
"""
|
|
208
|
+
sys.stderr.write("runinfra: {0}\n".format(message))
|
|
209
|
+
for line in detail:
|
|
210
|
+
sys.stderr.write(" {0}\n".format(line) if line else "\n")
|
|
211
|
+
raise SystemExit(exit_code)
|
runinfra_cli/__main__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""`python -m runinfra_cli`, which does exactly what `runinfra` does.
|
|
2
|
+
|
|
3
|
+
Worth shipping because the two most common places these packages get pulled,
|
|
4
|
+
containers and locked down GPU hosts, are also the two places where a virtual
|
|
5
|
+
environment's scripts directory is most often missing from PATH. When that
|
|
6
|
+
happens `runinfra` is "command not found" even though the install succeeded,
|
|
7
|
+
and the module form is the answer that always works.
|
|
8
|
+
|
|
9
|
+
It is a second door into the same room. All of the behaviour, including the
|
|
10
|
+
exit codes, lives in __init__.py.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from . import main
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
main()
|
|
Binary file
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
RunInfra CLI License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RightNow AI. All rights reserved.
|
|
4
|
+
|
|
5
|
+
1. LICENSE GRANT
|
|
6
|
+
|
|
7
|
+
RightNow AI grants you a non-exclusive, non-transferable, revocable license
|
|
8
|
+
to install and use this software ("the CLI") for the purpose of accessing
|
|
9
|
+
the RunInfra service and downloading model packages your workspace is
|
|
10
|
+
entitled to.
|
|
11
|
+
|
|
12
|
+
2. RESTRICTIONS
|
|
13
|
+
|
|
14
|
+
You may not redistribute, sell, sublicense or publish the CLI or any part of
|
|
15
|
+
it. You may not modify the CLI and distribute the result. You may not remove
|
|
16
|
+
or alter this notice.
|
|
17
|
+
|
|
18
|
+
These restrictions apply to the CLI itself. They do NOT apply to the model
|
|
19
|
+
packages the CLI downloads: each package carries its own license, stated on
|
|
20
|
+
its product page and included in its kit, and that license governs your use
|
|
21
|
+
of that package.
|
|
22
|
+
|
|
23
|
+
3. NO WARRANTY
|
|
24
|
+
|
|
25
|
+
THE CLI IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
28
|
+
|
|
29
|
+
The CLI writes large files to disk and transfers large amounts of data over
|
|
30
|
+
your network. You are responsible for available disk space, for any metered
|
|
31
|
+
bandwidth costs your provider charges, and for where you choose to store
|
|
32
|
+
downloaded packages.
|
|
33
|
+
|
|
34
|
+
4. LIMITATION OF LIABILITY
|
|
35
|
+
|
|
36
|
+
IN NO EVENT SHALL RIGHTNOW AI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
37
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
38
|
+
OUT OF OR IN CONNECTION WITH THE CLI OR THE USE OR OTHER DEALINGS IN THE CLI.
|
|
39
|
+
|
|
40
|
+
5. TERMINATION
|
|
41
|
+
|
|
42
|
+
This license ends if you breach it, or when your entitlement to the RunInfra
|
|
43
|
+
service ends. On termination, stop using the CLI and delete it. Packages you
|
|
44
|
+
have already lawfully downloaded remain governed by their own licenses.
|
|
45
|
+
|
|
46
|
+
6. CONTACT
|
|
47
|
+
|
|
48
|
+
https://runinfra.ai
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: runinfra-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Sign in from your terminal and pull the optimized model packages your workspace owns
|
|
5
|
+
Author: RightNow AI
|
|
6
|
+
Project-URL: Homepage, https://runinfra.ai/catalog
|
|
7
|
+
Project-URL: Support, https://runinfra.ai/contact
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: Other/Proprietary License
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: System :: Software Distribution
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Keywords: runinfra,cli,model,download,resumable,inference
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# runinfra-cli
|
|
25
|
+
|
|
26
|
+
Sign in from your terminal and pull the optimized model packages your workspace
|
|
27
|
+
owns.
|
|
28
|
+
|
|
29
|
+
```console
|
|
30
|
+
$ pip install runinfra-cli
|
|
31
|
+
$ runinfra login
|
|
32
|
+
$ runinfra pull <slug> --out ./models
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Replace `<slug>` with the slug shown on your package's page under **Optimized
|
|
36
|
+
models** on runinfra.ai.
|
|
37
|
+
|
|
38
|
+
**No Node.js required.** This distribution carries a self contained program, so
|
|
39
|
+
`pip install` is the only step. That is the point of publishing here: the hosts
|
|
40
|
+
these packages get pulled onto usually have Python and often do not have Node.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Which package do I want?
|
|
45
|
+
|
|
46
|
+
There are two, they do different jobs, and installing the wrong one is the most
|
|
47
|
+
common mistake we see.
|
|
48
|
+
|
|
49
|
+
| Install | What it gives you | Use it when |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| `pip install runinfra-cli` | The `runinfra` command | You want to sign in and download the packages your workspace bought |
|
|
52
|
+
| `pip install runinfra` | The Python SDK, `import runinfra` | You want to call an optimized endpoint from your code |
|
|
53
|
+
|
|
54
|
+
They are separate products on separate release cycles and they do not conflict.
|
|
55
|
+
Installing both in the same environment is fine and supported.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
| Command | What it does |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `runinfra login [--device]` | Connects this terminal by having you approve it in a browser |
|
|
64
|
+
| `runinfra pull <slug> [--out DIR] [--concurrency N]` | Downloads a package this workspace owns, resumable |
|
|
65
|
+
| `runinfra logout` | Deletes the local credential and tells you where to revoke the key |
|
|
66
|
+
| `runinfra whoami` | Shows what this machine has stored, and when its access expires |
|
|
67
|
+
|
|
68
|
+
Run `runinfra --help` for the full grammar.
|
|
69
|
+
|
|
70
|
+
### Signing in on a machine with no browser
|
|
71
|
+
|
|
72
|
+
That is the normal case on a GPU host, and it is handled:
|
|
73
|
+
|
|
74
|
+
```console
|
|
75
|
+
$ runinfra login --device
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The terminal prints a short code that you type into the approval page on
|
|
79
|
+
whatever device does have a browser. The code never arrives inside a link. A
|
|
80
|
+
link that shows up with the code already in it did not come from us.
|
|
81
|
+
|
|
82
|
+
### Downloads resume
|
|
83
|
+
|
|
84
|
+
Kits are large. If a transfer stops, for any reason including Ctrl-C, run the
|
|
85
|
+
same `runinfra pull` command again and it continues from where it stopped
|
|
86
|
+
rather than starting over.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Exit codes
|
|
91
|
+
|
|
92
|
+
These are a contract, not an implementation detail. Scripts branch on them, so
|
|
93
|
+
they do not change without a major version.
|
|
94
|
+
|
|
95
|
+
| Code | Meaning |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| 0 | Success |
|
|
98
|
+
| 2 | Bad usage, or this machine cannot run the command |
|
|
99
|
+
| 3 | Not signed in, denied, or expired |
|
|
100
|
+
| 4 | The workspace does not own it |
|
|
101
|
+
| 5 | Network or server failure |
|
|
102
|
+
| 6 | Integrity failure, such as a checksum mismatch |
|
|
103
|
+
| 7 | No disk space, or the destination is not writable |
|
|
104
|
+
| 130 | Interrupted |
|
|
105
|
+
|
|
106
|
+
The split matters in automation. A 5 is worth retrying. A 4 never will be.
|
|
107
|
+
|
|
108
|
+
Installing through pip does not change any of this. The `runinfra` command
|
|
109
|
+
installed here hands control straight to the program and returns its exit code
|
|
110
|
+
untouched, including 130 from Ctrl-C.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Supported platforms
|
|
115
|
+
|
|
116
|
+
| Operating system | Processor |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| Linux (glibc 2.17 and newer) | x86-64, arm64 |
|
|
119
|
+
| Linux (musl, such as Alpine) | x86-64, arm64 |
|
|
120
|
+
| macOS 11 and newer | Apple silicon, Intel |
|
|
121
|
+
| Windows 10 and newer | x86-64 |
|
|
122
|
+
|
|
123
|
+
Python 3.8 or newer. There is nothing to compile and no build step, so the
|
|
124
|
+
install is a download and an unpack.
|
|
125
|
+
|
|
126
|
+
If `pip install runinfra-cli` reports that no matching distribution was found,
|
|
127
|
+
your platform is not published yet. Tell us which one you need at
|
|
128
|
+
<https://runinfra.ai/contact>.
|
|
129
|
+
|
|
130
|
+
If the `runinfra` command is not found after a successful install, your
|
|
131
|
+
environment's scripts directory is not on PATH. `python -m runinfra_cli` does
|
|
132
|
+
the same thing and always works.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## What the CLI never does
|
|
137
|
+
|
|
138
|
+
- **It never sees your password.** Sign in happens in your browser, in your
|
|
139
|
+
existing session. The CLI only ever holds an authorization code and, after
|
|
140
|
+
the exchange, the key that code bought.
|
|
141
|
+
- **It never asks you to paste an API key.** Approving in the browser mints
|
|
142
|
+
one, the CLI writes it to a file only you can read, and revoking it in
|
|
143
|
+
Settings kills it.
|
|
144
|
+
- **It never prints the key.** `whoami` and `logout` show a redacted form.
|
|
145
|
+
- **The key it holds cannot spend money.** It opens exactly one door,
|
|
146
|
+
downloading a package your workspace already owns.
|
|
147
|
+
|
|
148
|
+
Credentials are stored in `$XDG_CONFIG_HOME/runinfra/credentials.json` on Linux
|
|
149
|
+
and macOS, and under `%LOCALAPPDATA%\runinfra\` on Windows. Set
|
|
150
|
+
`RUNINFRA_CONFIG_DIR` to put them somewhere else.
|
|
151
|
+
|
|
152
|
+
You can revoke this machine's access at any time from **Settings, API keys** on
|
|
153
|
+
runinfra.ai.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Licence and support
|
|
158
|
+
|
|
159
|
+
Licensed for use with the RunInfra service. The full text ships in the package
|
|
160
|
+
and is reproduced on the product pages. The packages this CLI downloads are
|
|
161
|
+
covered by their own licences, stated on each package's page and included in
|
|
162
|
+
each kit.
|
|
163
|
+
|
|
164
|
+
Questions, or a platform you need: <https://runinfra.ai/contact>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
runinfra_cli/__init__.py,sha256=lH4x9RQtFq5FsLGycGFqK7V7qwFdDiGKlkNcTCJdI9U,8503
|
|
2
|
+
runinfra_cli/__main__.py,sha256=0BIEeWO525qqpoKnnPPSxD3BV52YBuqjSP_x25V7Sfo,641
|
|
3
|
+
runinfra_cli/_bin/runinfra.exe,sha256=Hzzi56VWKjenH820aoHxaVeONr3LKPhyGlVPdEQ-fvY,115764736
|
|
4
|
+
runinfra_cli-0.2.0.dist-info/LICENSE,sha256=JewMbf_rFZalfeDZEHI5HgRb-fa7xGV80f5jI4WfwBU,1760
|
|
5
|
+
runinfra_cli-0.2.0.dist-info/METADATA,sha256=7lj8hWLuSIGA1StcaKHtU--UivDSKMhHQLRoZTVCPZQ,5898
|
|
6
|
+
runinfra_cli-0.2.0.dist-info/WHEEL,sha256=PkX5WTniyLpEndcRqICIkYlUb4YvTtiY1w37Yo6SWZ8,110
|
|
7
|
+
runinfra_cli-0.2.0.dist-info/entry_points.txt,sha256=QQ1_VmipCFuqtpatnAsiF6IZ5sCV1NVKBbxgqgrRGJ4,47
|
|
8
|
+
runinfra_cli-0.2.0.dist-info/RECORD,,
|