codeaway 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.
- codeaway/__init__.py +4 -0
- codeaway/__main__.py +5 -0
- codeaway/agents.py +427 -0
- codeaway/cli.py +221 -0
- codeaway/config.py +157 -0
- codeaway/desktop.py +644 -0
- codeaway/server.py +673 -0
- codeaway/web/__init__.py +1 -0
- codeaway/web/app.js +747 -0
- codeaway/web/index.html +41 -0
- codeaway/web/setup.html +57 -0
- codeaway/web/style.css +280 -0
- codeaway-0.1.0.dist-info/METADATA +113 -0
- codeaway-0.1.0.dist-info/RECORD +17 -0
- codeaway-0.1.0.dist-info/WHEEL +4 -0
- codeaway-0.1.0.dist-info/entry_points.txt +2 -0
- codeaway-0.1.0.dist-info/licenses/LICENSE +21 -0
codeaway/config.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .agents import SurfaceMap
|
|
10
|
+
from .desktop import FractionalRegion
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class WindowHint:
|
|
15
|
+
process_path: str
|
|
16
|
+
title_hint: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class AppConfig:
|
|
21
|
+
bind_ip: str = "127.0.0.1"
|
|
22
|
+
port: int = 8765
|
|
23
|
+
selected_agent: str | None = None
|
|
24
|
+
selected_window: WindowHint | None = None
|
|
25
|
+
surfaces: SurfaceMap | None = None
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def setup_complete(self) -> bool:
|
|
29
|
+
return (
|
|
30
|
+
self.selected_agent is not None
|
|
31
|
+
and self.selected_window is not None
|
|
32
|
+
and self.surfaces is not None
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class ConfigLoad:
|
|
38
|
+
config: AppConfig
|
|
39
|
+
warnings: tuple[str, ...]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def default_config_path() -> Path:
|
|
43
|
+
if os.name == "nt":
|
|
44
|
+
return Path(os.environ["LOCALAPPDATA"]) / "CodeAway" / "config.json"
|
|
45
|
+
return Path.home() / ".config" / "codeaway" / "config.json"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _surface_values(surface: FractionalRegion) -> list[float]:
|
|
49
|
+
return [surface.x, surface.y, surface.width, surface.height]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _config_values(config: AppConfig) -> dict[str, Any]:
|
|
53
|
+
selected_window = None
|
|
54
|
+
if config.selected_window is not None:
|
|
55
|
+
selected_window = {
|
|
56
|
+
"process_path": config.selected_window.process_path,
|
|
57
|
+
"title_hint": config.selected_window.title_hint,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
surfaces = None
|
|
61
|
+
if config.surfaces is not None:
|
|
62
|
+
surfaces = {
|
|
63
|
+
"sidebar": _surface_values(config.surfaces.sidebar),
|
|
64
|
+
"conversation": _surface_values(config.surfaces.conversation),
|
|
65
|
+
"composer": _surface_values(config.surfaces.composer),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
"bind_ip": config.bind_ip,
|
|
70
|
+
"port": config.port,
|
|
71
|
+
"selected_agent": config.selected_agent,
|
|
72
|
+
"selected_window": selected_window,
|
|
73
|
+
"surfaces": surfaces,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def save_config(path: str | os.PathLike[str], config: AppConfig) -> None:
|
|
78
|
+
destination = Path(path)
|
|
79
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
temporary = destination.with_name(f"{destination.name}.tmp")
|
|
81
|
+
|
|
82
|
+
with temporary.open("w", encoding="utf-8") as handle:
|
|
83
|
+
json.dump(_config_values(config), handle, indent=2)
|
|
84
|
+
handle.write("\n")
|
|
85
|
+
handle.flush()
|
|
86
|
+
os.fsync(handle.fileno())
|
|
87
|
+
os.replace(temporary, destination)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _number(value: Any, field: str) -> int | float:
|
|
91
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
92
|
+
raise ValueError(f"{field} must be a number")
|
|
93
|
+
return value
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _surface(value: Any) -> FractionalRegion:
|
|
97
|
+
if not isinstance(value, list) or len(value) != 4:
|
|
98
|
+
raise ValueError("surface must be an array of four numbers")
|
|
99
|
+
return FractionalRegion(
|
|
100
|
+
_number(value[0], "x"),
|
|
101
|
+
_number(value[1], "y"),
|
|
102
|
+
_number(value[2], "width"),
|
|
103
|
+
_number(value[3], "height"),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _parse_config(value: Any) -> AppConfig:
|
|
108
|
+
if not isinstance(value, dict):
|
|
109
|
+
raise ValueError("configuration must be an object")
|
|
110
|
+
|
|
111
|
+
bind_ip = value.get("bind_ip", AppConfig.bind_ip)
|
|
112
|
+
if not isinstance(bind_ip, str):
|
|
113
|
+
raise ValueError("bind_ip must be a string")
|
|
114
|
+
|
|
115
|
+
port = value.get("port", AppConfig.port)
|
|
116
|
+
if isinstance(port, bool) or not isinstance(port, int) or not 1 <= port <= 65535:
|
|
117
|
+
raise ValueError("port must be an integer between 1 and 65535")
|
|
118
|
+
|
|
119
|
+
selected_agent = value.get("selected_agent")
|
|
120
|
+
if selected_agent is not None and not isinstance(selected_agent, str):
|
|
121
|
+
raise ValueError("selected_agent must be a string or null")
|
|
122
|
+
|
|
123
|
+
selected_window_value = value.get("selected_window")
|
|
124
|
+
selected_window = None
|
|
125
|
+
if selected_window_value is not None:
|
|
126
|
+
if not isinstance(selected_window_value, dict):
|
|
127
|
+
raise ValueError("selected_window must be an object or null")
|
|
128
|
+
process_path = selected_window_value.get("process_path")
|
|
129
|
+
title_hint = selected_window_value.get("title_hint")
|
|
130
|
+
if not isinstance(process_path, str) or not isinstance(title_hint, str):
|
|
131
|
+
raise ValueError("window hints must be strings")
|
|
132
|
+
selected_window = WindowHint(process_path, title_hint)
|
|
133
|
+
|
|
134
|
+
surfaces_value = value.get("surfaces")
|
|
135
|
+
surfaces = None
|
|
136
|
+
if surfaces_value is not None:
|
|
137
|
+
if not isinstance(surfaces_value, dict):
|
|
138
|
+
raise ValueError("surfaces must be an object or null")
|
|
139
|
+
surfaces = SurfaceMap(
|
|
140
|
+
sidebar=_surface(surfaces_value["sidebar"]),
|
|
141
|
+
conversation=_surface(surfaces_value["conversation"]),
|
|
142
|
+
composer=_surface(surfaces_value["composer"]),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return AppConfig(bind_ip, port, selected_agent, selected_window, surfaces)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def load_config(path: str | os.PathLike[str]) -> ConfigLoad:
|
|
149
|
+
source = Path(path)
|
|
150
|
+
if not source.exists():
|
|
151
|
+
return ConfigLoad(AppConfig(), ())
|
|
152
|
+
try:
|
|
153
|
+
with source.open("r", encoding="utf-8") as handle:
|
|
154
|
+
config = _parse_config(json.load(handle))
|
|
155
|
+
except (OSError, ValueError, TypeError, KeyError, json.JSONDecodeError):
|
|
156
|
+
return ConfigLoad(AppConfig(), ("Invalid configuration; using defaults.",))
|
|
157
|
+
return ConfigLoad(config, ())
|