krypton-app 0.1.0__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.
- krypton_app/__init__.py +2 -0
- krypton_app/__main__.py +5 -0
- krypton_app/cli.py +61 -0
- krypton_app/interfaces/__init__.py +1 -0
- krypton_app/interfaces/krypton_launcher_interface.py +13 -0
- krypton_app/launcher.py +104 -0
- krypton_app/py.typed +0 -0
- krypton_app-0.1.0.dist-info/METADATA +46 -0
- krypton_app-0.1.0.dist-info/RECORD +11 -0
- krypton_app-0.1.0.dist-info/WHEEL +4 -0
- krypton_app-0.1.0.dist-info/entry_points.txt +2 -0
krypton_app/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
# Made with ❤️ by [Vibey](https://the-vibey-project.github.io/vibey/), Developed by [Adam Matthew Steinberger](https://vibewithadam.matthewsteinberger.com/) ([@adammatthewsteinberger](https://github.com/adammatthewsteinberger/)).
|
|
2
|
+
"""krypton-app: the apps of vibey, and the `krypton` command that launches them."""
|
krypton_app/__main__.py
ADDED
krypton_app/cli.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""The `krypton` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from krypton_app.launcher import KryptonLauncher
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _version() -> str:
|
|
16
|
+
# A module-level function because it is a one-line lookup with no state (ADR-0016).
|
|
17
|
+
try:
|
|
18
|
+
return version("krypton-app")
|
|
19
|
+
except PackageNotFoundError: # pragma: no cover - only in an uninstalled tree
|
|
20
|
+
return "0+unknown"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _vibey_beside_me() -> str | None:
|
|
24
|
+
# A module-level function because it is a one-line lookup with no state (ADR-0016).
|
|
25
|
+
# The engine installed into the same environment as krypton-app comes first: a `vibey`
|
|
26
|
+
# earlier on PATH may be an older one from another environment.
|
|
27
|
+
candidate = Path(sys.executable).parent / "vibey"
|
|
28
|
+
return str(candidate) if os.access(candidate, os.X_OK) else None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
32
|
+
# A module-level function because a console-script entry point must be one (ADR-0016);
|
|
33
|
+
# everything it does is KryptonLauncher's.
|
|
34
|
+
parser = argparse.ArgumentParser(
|
|
35
|
+
prog="krypton",
|
|
36
|
+
description=(
|
|
37
|
+
"krypton, the apps of vibey. Starts the local vibey hub (`vibey serve`) and "
|
|
38
|
+
"opens it in your browser; if the installed vibey cannot serve it yet, says so "
|
|
39
|
+
"and lists what is available instead."
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument("--version", action="version", version=f"krypton {_version()}")
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--host", default="127.0.0.1", help="address to serve on (default: 127.0.0.1)"
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument("--port", type=int, default=8765, help="port to serve on (default: 8765)")
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--no-browser", action="store_true", help="start the hub without opening it"
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--vibey",
|
|
52
|
+
default=None,
|
|
53
|
+
help="the vibey program to use (default: the one beside krypton, else vibey on PATH)",
|
|
54
|
+
)
|
|
55
|
+
args = parser.parse_args(argv)
|
|
56
|
+
launcher = KryptonLauncher(out=sys.stdout, vibey=args.vibey or _vibey_beside_me())
|
|
57
|
+
return launcher.launch(args.host, args.port, open_browser=not args.no_browser)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__": # pragma: no cover
|
|
61
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Interfaces of krypton-app (ADR-0016)."""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""The contract of the krypton launcher (ADR-0016: an interface beside every class)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KryptonLauncherInterface(ABC):
|
|
9
|
+
"""Starts the local vibey hub and opens it, or says plainly why it cannot."""
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def launch(self, host: str, port: int, open_browser: bool) -> int:
|
|
13
|
+
"""Run the hub until it exits; return the process exit status."""
|
krypton_app/launcher.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""The krypton launcher: `vibey serve`, then the browser -- or an honest refusal."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import time
|
|
7
|
+
import urllib.error
|
|
8
|
+
import urllib.request
|
|
9
|
+
import webbrowser
|
|
10
|
+
from collections.abc import Callable, Sequence
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from shutil import which
|
|
13
|
+
from typing import IO, Protocol
|
|
14
|
+
|
|
15
|
+
from krypton_app.interfaces.krypton_launcher_interface import KryptonLauncherInterface
|
|
16
|
+
|
|
17
|
+
AVAILABLE_INSTEAD = (
|
|
18
|
+
"What you can use now:\n"
|
|
19
|
+
" vibey --help the vibey command line\n"
|
|
20
|
+
" vibey doctor what is installed and what is missing\n"
|
|
21
|
+
" the VS Code extension krypton inside your editor (clients/vscode)\n"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _Process(Protocol):
|
|
26
|
+
def poll(self) -> int | None: ...
|
|
27
|
+
def wait(self) -> int: ...
|
|
28
|
+
def terminate(self) -> None: ...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _probe(url: str) -> bool:
|
|
32
|
+
"""Whether anything answers HTTP at `url`, whatever the status."""
|
|
33
|
+
try:
|
|
34
|
+
with urllib.request.urlopen(url, timeout=2): # nosec B310 - loopback URL we built
|
|
35
|
+
return True
|
|
36
|
+
except urllib.error.HTTPError:
|
|
37
|
+
return True
|
|
38
|
+
except (urllib.error.URLError, OSError):
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class KryptonLauncher(KryptonLauncherInterface):
|
|
44
|
+
"""Launches the hub through the installed `vibey`, with every side effect injectable."""
|
|
45
|
+
|
|
46
|
+
out: IO[str]
|
|
47
|
+
vibey: str | None = None
|
|
48
|
+
ready_timeout: float = 60.0
|
|
49
|
+
find: Callable[[str], str | None] = which
|
|
50
|
+
run: Callable[[Sequence[str]], int] = field(
|
|
51
|
+
default=lambda argv: (
|
|
52
|
+
subprocess.run( # nosec B603 - argv, no shell
|
|
53
|
+
list(argv), capture_output=True, timeout=30, check=False
|
|
54
|
+
).returncode
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
spawn: Callable[[Sequence[str]], _Process] = field(
|
|
58
|
+
default=lambda argv: subprocess.Popen(list(argv)) # nosec B603 - argv, no shell
|
|
59
|
+
)
|
|
60
|
+
probe: Callable[[str], bool] = _probe
|
|
61
|
+
browse: Callable[[str], object] = webbrowser.open
|
|
62
|
+
sleep: Callable[[float], None] = time.sleep
|
|
63
|
+
clock: Callable[[], float] = time.monotonic
|
|
64
|
+
|
|
65
|
+
def launch(self, host: str, port: int, open_browser: bool) -> int:
|
|
66
|
+
vibey = self.vibey or self.find("vibey")
|
|
67
|
+
if vibey is None:
|
|
68
|
+
self.out.write(
|
|
69
|
+
"krypton: the vibey command is not installed, so there is no hub to start.\n"
|
|
70
|
+
"Install the engine with: pip install vibey-engine\n"
|
|
71
|
+
)
|
|
72
|
+
return 1
|
|
73
|
+
if self.run([vibey, "serve", "--help"]) != 0:
|
|
74
|
+
self.out.write(
|
|
75
|
+
"krypton: the local web app is started by `vibey serve`, and the installed\n"
|
|
76
|
+
f"vibey ({vibey}) does not provide it yet. Nothing was started.\n"
|
|
77
|
+
+ AVAILABLE_INSTEAD
|
|
78
|
+
)
|
|
79
|
+
return 1
|
|
80
|
+
url = f"http://{host}:{port}/"
|
|
81
|
+
process = self.spawn([vibey, "serve", "--host", host, "--port", str(port)])
|
|
82
|
+
deadline = self.clock() + self.ready_timeout
|
|
83
|
+
while not self.probe(url):
|
|
84
|
+
status = process.poll()
|
|
85
|
+
if status is not None:
|
|
86
|
+
self.out.write(
|
|
87
|
+
f"krypton: vibey serve exited with status {status} before it answered.\n"
|
|
88
|
+
)
|
|
89
|
+
return status or 1
|
|
90
|
+
if self.clock() >= deadline:
|
|
91
|
+
self.out.write(
|
|
92
|
+
f"krypton: nothing answered at {url} within {self.ready_timeout:g}s.\n"
|
|
93
|
+
)
|
|
94
|
+
process.terminate()
|
|
95
|
+
return 1
|
|
96
|
+
self.sleep(0.5)
|
|
97
|
+
self.out.write(f"krypton: the vibey hub is at {url}\n")
|
|
98
|
+
if open_browser:
|
|
99
|
+
self.browse(url)
|
|
100
|
+
try:
|
|
101
|
+
return process.wait()
|
|
102
|
+
except KeyboardInterrupt:
|
|
103
|
+
process.terminate()
|
|
104
|
+
return 130
|
krypton_app/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: krypton-app
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: krypton, the apps of vibey: launches the local vibey hub and opens it.
|
|
5
|
+
Project-URL: Homepage, https://the-vibey-project.github.io/vibey/
|
|
6
|
+
Project-URL: Source, https://github.com/the-vibey-project/vibey/tree/main/clients/krypton-app
|
|
7
|
+
Project-URL: Issues, https://github.com/the-vibey-project/vibey/issues
|
|
8
|
+
License: MIT
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Requires-Dist: vibey-engine
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
15
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# krypton-app
|
|
19
|
+
|
|
20
|
+
krypton is every app and interface of [vibey](https://the-vibey-project.github.io/vibey/).
|
|
21
|
+
This package carries them and installs one command, `krypton`.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install krypton-app # brings vibey-engine with it
|
|
25
|
+
krypton # start the local vibey hub and open it in your browser
|
|
26
|
+
krypton --help
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## What `krypton` does today
|
|
30
|
+
|
|
31
|
+
`krypton` is a launcher. It finds the `vibey` command, starts `vibey serve` on
|
|
32
|
+
`127.0.0.1:8765` (change it with `--host` and `--port`), waits until the hub answers, and
|
|
33
|
+
opens it in your browser. `--no-browser` only starts it.
|
|
34
|
+
|
|
35
|
+
When the installed vibey-engine has no `vibey serve` yet, `krypton` says so plainly, lists
|
|
36
|
+
what you can use instead, and exits with status 1. It never pretends a hub is running.
|
|
37
|
+
|
|
38
|
+
## Releases
|
|
39
|
+
|
|
40
|
+
Published by `.github/workflows/krypton-app.yml` alone, by trusted publishing: a dev build
|
|
41
|
+
to TestPyPI (environment `testpypi`) on each push to `develop` that changes this tree or the
|
|
42
|
+
workflow, and a release to PyPI (environment `pypi`) on each such push to `main`. There is no
|
|
43
|
+
schedule. A push GitHub skips publishes nothing: a squash merge whose message quotes an old
|
|
44
|
+
`[skip ci]` subject skips every push workflow, which is how the 3.0.0 promotion first
|
|
45
|
+
published neither package. Merge a promotion by rebase, or edit the squash message. ADR-0069 records why vibey publishes exactly two packages, `vibey-engine` and
|
|
46
|
+
`krypton-app`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
krypton_app/__init__.py,sha256=RDf_IDp2ZN_S_behzNYyFGgBx9nsDQiVOTNHNstss3w,318
|
|
2
|
+
krypton_app/__main__.py,sha256=kXjgx3aDuOwiLYTQTF8o2g0Zg6f69Q-l7Yanf2xYNsk,88
|
|
3
|
+
krypton_app/cli.py,sha256=WCxi74-eBG3qnic0YlxRA4_7_O9j0vkpH5q4MT8UBmY,2354
|
|
4
|
+
krypton_app/launcher.py,sha256=YRqFVRXzTNATuU9S5hu1qq2SELZTGNLSNpH6j9AIuYs,3756
|
|
5
|
+
krypton_app/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
krypton_app/interfaces/__init__.py,sha256=FFuGqyvWl8qYoA51z238K2vrQgRG-qQjLfeTUzSHx1s,44
|
|
7
|
+
krypton_app/interfaces/krypton_launcher_interface.py,sha256=TUzCwsbAw1NCQM3GYbm1nEW0Ixu-lo2vNs1friE9EhQ,448
|
|
8
|
+
krypton_app-0.1.0.dist-info/METADATA,sha256=zY2ksXPpH0u2uJyiZjtNbw64p-ITPUuj2BAppp_9weU,2089
|
|
9
|
+
krypton_app-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
10
|
+
krypton_app-0.1.0.dist-info/entry_points.txt,sha256=SUniEOzAlnva7bhd4Aqm6RZ6CBQ87en-Pnpw7nY-lwE,49
|
|
11
|
+
krypton_app-0.1.0.dist-info/RECORD,,
|