revengelibrary 0.1.5__py3-none-any.whl → 0.1.7__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.
- revengelibrary/__init__.py +6 -1
- revengelibrary/agents.py +77 -0
- revengelibrary/chat.py +8 -1
- revengelibrary/cli.py +28 -4
- revengelibrary/ide/app.js +311 -0
- revengelibrary/ide/index.html +101 -0
- revengelibrary/ide/styles.css +405 -0
- revengelibrary/ide_server.py +480 -0
- {revengelibrary-0.1.5.dist-info → revengelibrary-0.1.7.dist-info}/METADATA +2 -2
- revengelibrary-0.1.7.dist-info/RECORD +15 -0
- {revengelibrary-0.1.5.dist-info → revengelibrary-0.1.7.dist-info}/entry_points.txt +1 -0
- revengelibrary-0.1.5.dist-info/RECORD +0 -10
- {revengelibrary-0.1.5.dist-info → revengelibrary-0.1.7.dist-info}/WHEEL +0 -0
- {revengelibrary-0.1.5.dist-info → revengelibrary-0.1.7.dist-info}/licenses/LICENSE +0 -0
- {revengelibrary-0.1.5.dist-info → revengelibrary-0.1.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import mimetypes
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
import tempfile
|
|
10
|
+
import threading
|
|
11
|
+
import webbrowser
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from http import HTTPStatus
|
|
14
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
from urllib.parse import parse_qs, urlparse
|
|
18
|
+
|
|
19
|
+
from .agents import DEFAULT_AGENT, build_agent_memory_file, get_agent, list_agents
|
|
20
|
+
from .chat import (
|
|
21
|
+
APIError,
|
|
22
|
+
DEFAULT_MODEL,
|
|
23
|
+
DEFAULT_OPENROUTER_API_KEY,
|
|
24
|
+
FreeNeuroChatClient,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
IDE_DIR = Path(__file__).resolve().parent / "ide"
|
|
28
|
+
HIDDEN_SKIP = {".git", "venv", ".venv", "__pycache__", "build", "dist"}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class IDEState:
|
|
33
|
+
root_dir: Path
|
|
34
|
+
api_key: str
|
|
35
|
+
default_model: str
|
|
36
|
+
default_memory_file: str
|
|
37
|
+
clients: dict[tuple[str, str, str, str], FreeNeuroChatClient] = field(
|
|
38
|
+
default_factory=dict
|
|
39
|
+
)
|
|
40
|
+
lock: threading.Lock = field(default_factory=threading.Lock)
|
|
41
|
+
|
|
42
|
+
def get_client(
|
|
43
|
+
self,
|
|
44
|
+
agent_name: str,
|
|
45
|
+
model: str,
|
|
46
|
+
memory_file: str | None = None,
|
|
47
|
+
system_prompt: str | None = None,
|
|
48
|
+
) -> FreeNeuroChatClient:
|
|
49
|
+
agent = get_agent(agent_name)
|
|
50
|
+
chosen_model = model or self.default_model
|
|
51
|
+
prompt = system_prompt or agent.system_prompt
|
|
52
|
+
|
|
53
|
+
base_memory_file = memory_file or self.default_memory_file
|
|
54
|
+
scoped_memory_file = build_agent_memory_file(base_memory_file, agent.name) or ""
|
|
55
|
+
|
|
56
|
+
key = (agent.name, chosen_model, scoped_memory_file, prompt)
|
|
57
|
+
with self.lock:
|
|
58
|
+
client = self.clients.get(key)
|
|
59
|
+
if client is not None:
|
|
60
|
+
return client
|
|
61
|
+
|
|
62
|
+
client = FreeNeuroChatClient(
|
|
63
|
+
api_key=self.api_key,
|
|
64
|
+
model=chosen_model,
|
|
65
|
+
system_prompt=prompt,
|
|
66
|
+
memory_file=scoped_memory_file or None,
|
|
67
|
+
)
|
|
68
|
+
self.clients[key] = client
|
|
69
|
+
return client
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _json_response(
|
|
73
|
+
handler: BaseHTTPRequestHandler,
|
|
74
|
+
payload: dict[str, Any],
|
|
75
|
+
status: int = HTTPStatus.OK,
|
|
76
|
+
) -> None:
|
|
77
|
+
raw = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
78
|
+
handler.send_response(status)
|
|
79
|
+
handler.send_header("Content-Type", "application/json; charset=utf-8")
|
|
80
|
+
handler.send_header("Content-Length", str(len(raw)))
|
|
81
|
+
handler.end_headers()
|
|
82
|
+
handler.wfile.write(raw)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _text_response(
|
|
86
|
+
handler: BaseHTTPRequestHandler,
|
|
87
|
+
content: bytes,
|
|
88
|
+
content_type: str,
|
|
89
|
+
status: int = HTTPStatus.OK,
|
|
90
|
+
) -> None:
|
|
91
|
+
handler.send_response(status)
|
|
92
|
+
handler.send_header("Content-Type", content_type)
|
|
93
|
+
handler.send_header("Content-Length", str(len(content)))
|
|
94
|
+
handler.end_headers()
|
|
95
|
+
handler.wfile.write(content)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _read_json(handler: BaseHTTPRequestHandler) -> dict[str, Any]:
|
|
99
|
+
raw_len = handler.headers.get("Content-Length", "0")
|
|
100
|
+
try:
|
|
101
|
+
length = int(raw_len)
|
|
102
|
+
except ValueError:
|
|
103
|
+
return {}
|
|
104
|
+
if length <= 0:
|
|
105
|
+
return {}
|
|
106
|
+
raw = handler.rfile.read(length)
|
|
107
|
+
try:
|
|
108
|
+
data = json.loads(raw.decode("utf-8"))
|
|
109
|
+
except (UnicodeDecodeError, json.JSONDecodeError):
|
|
110
|
+
return {}
|
|
111
|
+
return data if isinstance(data, dict) else {}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _safe_project_path(root: Path, rel_path: str) -> Path:
|
|
115
|
+
rel = Path(rel_path)
|
|
116
|
+
if rel.is_absolute():
|
|
117
|
+
raise ValueError("Absolute paths are not allowed.")
|
|
118
|
+
candidate = (root / rel).resolve()
|
|
119
|
+
resolved_root = root.resolve()
|
|
120
|
+
if candidate != resolved_root and resolved_root not in candidate.parents:
|
|
121
|
+
raise ValueError("Path escapes project root.")
|
|
122
|
+
return candidate
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _collect_files(root: Path) -> list[str]:
|
|
126
|
+
result: list[str] = []
|
|
127
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
128
|
+
dirnames[:] = [name for name in dirnames if name not in HIDDEN_SKIP]
|
|
129
|
+
base = Path(dirpath)
|
|
130
|
+
for filename in filenames:
|
|
131
|
+
if filename.endswith((".pyc", ".pyo")):
|
|
132
|
+
continue
|
|
133
|
+
if filename in {".DS_Store"}:
|
|
134
|
+
continue
|
|
135
|
+
file_path = base / filename
|
|
136
|
+
rel = file_path.relative_to(root).as_posix()
|
|
137
|
+
result.append(rel)
|
|
138
|
+
result.sort()
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _normalize_text(content: str) -> str:
|
|
143
|
+
normalized = content.replace("\r\n", "\n").replace("\r", "\n")
|
|
144
|
+
cleaned_lines = [line.rstrip() for line in normalized.split("\n")]
|
|
145
|
+
return "\n".join(cleaned_lines).rstrip() + "\n"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _run_external_formatter(
|
|
149
|
+
command: list[str],
|
|
150
|
+
source: str,
|
|
151
|
+
suffix: str,
|
|
152
|
+
) -> str | None:
|
|
153
|
+
temp_path: Path | None = None
|
|
154
|
+
try:
|
|
155
|
+
with tempfile.NamedTemporaryFile(
|
|
156
|
+
mode="w",
|
|
157
|
+
suffix=suffix,
|
|
158
|
+
encoding="utf-8",
|
|
159
|
+
delete=False,
|
|
160
|
+
) as tmp:
|
|
161
|
+
tmp.write(source)
|
|
162
|
+
temp_path = Path(tmp.name)
|
|
163
|
+
|
|
164
|
+
result = subprocess.run(
|
|
165
|
+
[*command, str(temp_path)],
|
|
166
|
+
capture_output=True,
|
|
167
|
+
text=True,
|
|
168
|
+
check=False,
|
|
169
|
+
)
|
|
170
|
+
if result.returncode != 0:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
return temp_path.read_text(encoding="utf-8")
|
|
174
|
+
except OSError:
|
|
175
|
+
return None
|
|
176
|
+
finally:
|
|
177
|
+
if temp_path is not None:
|
|
178
|
+
temp_path.unlink(missing_ok=True)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _format_source(path: Path, content: str) -> str:
|
|
182
|
+
suffix = path.suffix.lower()
|
|
183
|
+
|
|
184
|
+
if suffix == ".json":
|
|
185
|
+
try:
|
|
186
|
+
payload = json.loads(content)
|
|
187
|
+
return json.dumps(payload, ensure_ascii=False, indent=2) + "\n"
|
|
188
|
+
except json.JSONDecodeError:
|
|
189
|
+
return _normalize_text(content)
|
|
190
|
+
|
|
191
|
+
if suffix == ".py":
|
|
192
|
+
if shutil.which("ruff"):
|
|
193
|
+
formatted = _run_external_formatter(["ruff", "format"], content, ".py")
|
|
194
|
+
if formatted is not None:
|
|
195
|
+
return formatted
|
|
196
|
+
if shutil.which("black"):
|
|
197
|
+
formatted = _run_external_formatter(["black", "-q"], content, ".py")
|
|
198
|
+
if formatted is not None:
|
|
199
|
+
return formatted
|
|
200
|
+
return _normalize_text(content)
|
|
201
|
+
|
|
202
|
+
if suffix in {".js", ".ts", ".tsx", ".jsx", ".css", ".html"}:
|
|
203
|
+
if shutil.which("prettier"):
|
|
204
|
+
formatted = _run_external_formatter(["prettier", "--write"], content, suffix)
|
|
205
|
+
if formatted is not None:
|
|
206
|
+
return formatted
|
|
207
|
+
return _normalize_text(content)
|
|
208
|
+
|
|
209
|
+
return _normalize_text(content)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _load_asset(relative_name: str) -> tuple[bytes, str]:
|
|
213
|
+
asset_path = IDE_DIR / relative_name
|
|
214
|
+
if not asset_path.exists():
|
|
215
|
+
raise FileNotFoundError(relative_name)
|
|
216
|
+
content = asset_path.read_bytes()
|
|
217
|
+
content_type, _ = mimetypes.guess_type(asset_path.as_posix())
|
|
218
|
+
return content, content_type or "application/octet-stream"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _make_handler(state: IDEState) -> type[BaseHTTPRequestHandler]:
|
|
222
|
+
class IDEHandler(BaseHTTPRequestHandler):
|
|
223
|
+
server_version = "revengelibrary-ide/1.0"
|
|
224
|
+
|
|
225
|
+
def log_message(self, format: str, *args: Any) -> None: # noqa: A003
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
def do_GET(self) -> None: # noqa: N802
|
|
229
|
+
parsed = urlparse(self.path)
|
|
230
|
+
path = parsed.path
|
|
231
|
+
query = parse_qs(parsed.query)
|
|
232
|
+
|
|
233
|
+
if path == "/api/config":
|
|
234
|
+
payload = {
|
|
235
|
+
"root_name": state.root_dir.name,
|
|
236
|
+
"default_model": state.default_model,
|
|
237
|
+
"default_memory_file": state.default_memory_file,
|
|
238
|
+
"default_agent": DEFAULT_AGENT,
|
|
239
|
+
"agents": [
|
|
240
|
+
{"name": item.name, "title": item.title}
|
|
241
|
+
for item in list_agents()
|
|
242
|
+
],
|
|
243
|
+
}
|
|
244
|
+
_json_response(self, payload)
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
if path == "/api/files":
|
|
248
|
+
files = _collect_files(state.root_dir)
|
|
249
|
+
_json_response(self, {"files": files})
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
if path == "/api/file":
|
|
253
|
+
file_values = query.get("path") or []
|
|
254
|
+
if not file_values:
|
|
255
|
+
_json_response(
|
|
256
|
+
self,
|
|
257
|
+
{"error": "Missing query parameter: path"},
|
|
258
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
259
|
+
)
|
|
260
|
+
return
|
|
261
|
+
try:
|
|
262
|
+
target = _safe_project_path(state.root_dir, file_values[0])
|
|
263
|
+
content = target.read_text(encoding="utf-8")
|
|
264
|
+
except (OSError, ValueError) as exc:
|
|
265
|
+
_json_response(
|
|
266
|
+
self,
|
|
267
|
+
{"error": str(exc)},
|
|
268
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
269
|
+
)
|
|
270
|
+
return
|
|
271
|
+
_json_response(self, {"path": file_values[0], "content": content})
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
if path == "/" or path == "/index.html":
|
|
275
|
+
content, content_type = _load_asset("index.html")
|
|
276
|
+
_text_response(self, content, content_type)
|
|
277
|
+
return
|
|
278
|
+
if path == "/app.css":
|
|
279
|
+
content, content_type = _load_asset("styles.css")
|
|
280
|
+
_text_response(self, content, content_type)
|
|
281
|
+
return
|
|
282
|
+
if path == "/app.js":
|
|
283
|
+
content, content_type = _load_asset("app.js")
|
|
284
|
+
_text_response(self, content, content_type)
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
_json_response(
|
|
288
|
+
self,
|
|
289
|
+
{"error": f"Not found: {path}"},
|
|
290
|
+
status=HTTPStatus.NOT_FOUND,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def do_POST(self) -> None: # noqa: N802
|
|
294
|
+
path = urlparse(self.path).path
|
|
295
|
+
body = _read_json(self)
|
|
296
|
+
|
|
297
|
+
if path == "/api/file":
|
|
298
|
+
rel_path = str(body.get("path", "")).strip()
|
|
299
|
+
content = str(body.get("content", ""))
|
|
300
|
+
if not rel_path:
|
|
301
|
+
_json_response(
|
|
302
|
+
self,
|
|
303
|
+
{"error": "Field 'path' is required"},
|
|
304
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
305
|
+
)
|
|
306
|
+
return
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
target = _safe_project_path(state.root_dir, rel_path)
|
|
310
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
311
|
+
target.write_text(content, encoding="utf-8")
|
|
312
|
+
except (OSError, ValueError) as exc:
|
|
313
|
+
_json_response(
|
|
314
|
+
self,
|
|
315
|
+
{"error": str(exc)},
|
|
316
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
317
|
+
)
|
|
318
|
+
return
|
|
319
|
+
|
|
320
|
+
_json_response(self, {"ok": True, "path": rel_path})
|
|
321
|
+
return
|
|
322
|
+
|
|
323
|
+
if path == "/api/format":
|
|
324
|
+
rel_path = str(body.get("path", "")).strip()
|
|
325
|
+
if not rel_path:
|
|
326
|
+
_json_response(
|
|
327
|
+
self,
|
|
328
|
+
{"error": "Field 'path' is required"},
|
|
329
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
330
|
+
)
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
try:
|
|
334
|
+
target = _safe_project_path(state.root_dir, rel_path)
|
|
335
|
+
input_content = body.get("content")
|
|
336
|
+
source = (
|
|
337
|
+
str(input_content)
|
|
338
|
+
if input_content is not None
|
|
339
|
+
else target.read_text(encoding="utf-8")
|
|
340
|
+
)
|
|
341
|
+
formatted = _format_source(target, source)
|
|
342
|
+
target.write_text(formatted, encoding="utf-8")
|
|
343
|
+
except (OSError, ValueError) as exc:
|
|
344
|
+
_json_response(
|
|
345
|
+
self,
|
|
346
|
+
{"error": str(exc)},
|
|
347
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
348
|
+
)
|
|
349
|
+
return
|
|
350
|
+
|
|
351
|
+
_json_response(self, {"ok": True, "path": rel_path, "content": formatted})
|
|
352
|
+
return
|
|
353
|
+
|
|
354
|
+
if path == "/api/chat":
|
|
355
|
+
message = str(body.get("message", "")).strip()
|
|
356
|
+
agent_name = str(body.get("agent", DEFAULT_AGENT))
|
|
357
|
+
model = str(body.get("model", state.default_model))
|
|
358
|
+
memory_file = body.get("memory_file")
|
|
359
|
+
system_prompt = body.get("system_prompt")
|
|
360
|
+
|
|
361
|
+
if not message:
|
|
362
|
+
_json_response(
|
|
363
|
+
self,
|
|
364
|
+
{"error": "Field 'message' is required"},
|
|
365
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
366
|
+
)
|
|
367
|
+
return
|
|
368
|
+
|
|
369
|
+
try:
|
|
370
|
+
client = state.get_client(
|
|
371
|
+
agent_name=agent_name,
|
|
372
|
+
model=model,
|
|
373
|
+
memory_file=str(memory_file) if memory_file else None,
|
|
374
|
+
system_prompt=str(system_prompt) if system_prompt else None,
|
|
375
|
+
)
|
|
376
|
+
reply = client.send(message)
|
|
377
|
+
except (ValueError, APIError) as exc:
|
|
378
|
+
_json_response(
|
|
379
|
+
self,
|
|
380
|
+
{"error": str(exc)},
|
|
381
|
+
status=HTTPStatus.BAD_REQUEST,
|
|
382
|
+
)
|
|
383
|
+
return
|
|
384
|
+
except Exception as exc: # noqa: BLE001
|
|
385
|
+
_json_response(
|
|
386
|
+
self,
|
|
387
|
+
{"error": f"Unexpected chat error: {exc}"},
|
|
388
|
+
status=HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
389
|
+
)
|
|
390
|
+
return
|
|
391
|
+
|
|
392
|
+
_json_response(
|
|
393
|
+
self,
|
|
394
|
+
{
|
|
395
|
+
"reply": reply,
|
|
396
|
+
"agent": agent_name,
|
|
397
|
+
"model": client.model,
|
|
398
|
+
"memory_file": client.memory_file,
|
|
399
|
+
},
|
|
400
|
+
)
|
|
401
|
+
return
|
|
402
|
+
|
|
403
|
+
_json_response(
|
|
404
|
+
self,
|
|
405
|
+
{"error": f"Not found: {path}"},
|
|
406
|
+
status=HTTPStatus.NOT_FOUND,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
return IDEHandler
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
413
|
+
parser = argparse.ArgumentParser(
|
|
414
|
+
prog="revengelibrary-ide",
|
|
415
|
+
description="Run local IDE-style interface for revengelibrary.",
|
|
416
|
+
)
|
|
417
|
+
parser.add_argument("--host", default="127.0.0.1", help="Bind host.")
|
|
418
|
+
parser.add_argument("--port", default=8765, type=int, help="Bind port.")
|
|
419
|
+
parser.add_argument(
|
|
420
|
+
"--root",
|
|
421
|
+
default=os.getcwd(),
|
|
422
|
+
help="Project root shown in the IDE.",
|
|
423
|
+
)
|
|
424
|
+
parser.add_argument(
|
|
425
|
+
"--api-key",
|
|
426
|
+
default=os.getenv("OPENROUTER_API_KEY", DEFAULT_OPENROUTER_API_KEY),
|
|
427
|
+
help="OpenRouter API key.",
|
|
428
|
+
)
|
|
429
|
+
parser.add_argument(
|
|
430
|
+
"--model",
|
|
431
|
+
default=DEFAULT_MODEL,
|
|
432
|
+
help="Default chat model.",
|
|
433
|
+
)
|
|
434
|
+
parser.add_argument(
|
|
435
|
+
"--memory-file",
|
|
436
|
+
default=".revide_memory",
|
|
437
|
+
help="Base memory file name for IDE chat sessions.",
|
|
438
|
+
)
|
|
439
|
+
parser.add_argument(
|
|
440
|
+
"--no-browser",
|
|
441
|
+
action="store_true",
|
|
442
|
+
help="Do not auto-open browser.",
|
|
443
|
+
)
|
|
444
|
+
return parser
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def main() -> int:
|
|
448
|
+
parser = _build_parser()
|
|
449
|
+
args = parser.parse_args()
|
|
450
|
+
|
|
451
|
+
root_dir = Path(args.root).expanduser().resolve()
|
|
452
|
+
if not root_dir.exists() or not root_dir.is_dir():
|
|
453
|
+
parser.error(f"Invalid --root: {root_dir}")
|
|
454
|
+
|
|
455
|
+
state = IDEState(
|
|
456
|
+
root_dir=root_dir,
|
|
457
|
+
api_key=args.api_key or DEFAULT_OPENROUTER_API_KEY,
|
|
458
|
+
default_model=args.model,
|
|
459
|
+
default_memory_file=str((root_dir / args.memory_file).resolve()),
|
|
460
|
+
)
|
|
461
|
+
handler_cls = _make_handler(state)
|
|
462
|
+
server = ThreadingHTTPServer((args.host, args.port), handler_cls)
|
|
463
|
+
|
|
464
|
+
url = f"http://{args.host}:{args.port}"
|
|
465
|
+
print(f"revengelibrary IDE: {url}")
|
|
466
|
+
print(f"root: {root_dir}")
|
|
467
|
+
if not args.no_browser:
|
|
468
|
+
webbrowser.open(url)
|
|
469
|
+
|
|
470
|
+
try:
|
|
471
|
+
server.serve_forever()
|
|
472
|
+
except KeyboardInterrupt:
|
|
473
|
+
pass
|
|
474
|
+
finally:
|
|
475
|
+
server.server_close()
|
|
476
|
+
return 0
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
if __name__ == "__main__":
|
|
480
|
+
raise SystemExit(main())
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: revengelibrary
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.1.7
|
|
4
|
+
Summary: Не нейросеть
|
|
5
5
|
Author: revengebibliotek contributors
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/example/revengelibrary
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
revengelibrary/__init__.py,sha256=gCrIdVqm-vWoL5KmjtJQr5-W44RXHkrUt2udhvnBT5I,448
|
|
2
|
+
revengelibrary/agents.py,sha256=nF--yecnFXaaNayi5I-41j0KDy-k64tqogRfw5re0wA,2675
|
|
3
|
+
revengelibrary/chat.py,sha256=1UyJ9HZNea_oglsYMVqg3VdR2ASJvn4IPTBdUjNQ03k,7542
|
|
4
|
+
revengelibrary/cli.py,sha256=ztrcFN9Zs5JIOFZ17j2K624BWpAffyYQ-Fj9ReDcqtU,3363
|
|
5
|
+
revengelibrary/ide_server.py,sha256=ORPN2iSkMsA3dRv5y5NGEigA0aY2kog6yEWUvDj9Mlg,15961
|
|
6
|
+
revengelibrary/memory_store.json,sha256=N1F-Xz3GaBn2H1p7uKzhkhKCQV8QVR0t76XD6wmFtXA,3
|
|
7
|
+
revengelibrary/ide/app.js,sha256=ulYjtCALokdG3EsFHxP2oQe1XCfcpNx24RhTLciTiHo,9614
|
|
8
|
+
revengelibrary/ide/index.html,sha256=4aC007wOnnB0qbh0Yu5JLPDL41pSDJ4LXpjzGDP9vu4,3207
|
|
9
|
+
revengelibrary/ide/styles.css,sha256=7BJPxEJ9ppn9OE81W3OW0ydmb_OOLTN8l8itlytSPAY,6858
|
|
10
|
+
revengelibrary-0.1.7.dist-info/licenses/LICENSE,sha256=UgqQ8UtVfIOP8-clGkGAylmb9AUbT4-Ue49N_WjIpi4,1073
|
|
11
|
+
revengelibrary-0.1.7.dist-info/METADATA,sha256=qQ-5nOdCnC1ijicsulU54rODc6kH9F76JCXROCRuaoc,822
|
|
12
|
+
revengelibrary-0.1.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
13
|
+
revengelibrary-0.1.7.dist-info/entry_points.txt,sha256=z5tzRCdH6_7gF5IQjgwFu2pQImVJayK7V5DDFmmk-d8,111
|
|
14
|
+
revengelibrary-0.1.7.dist-info/top_level.txt,sha256=gfUsAxA-IgliQakMoupLAAjrTEYtUJurH5n29eHhUBE,15
|
|
15
|
+
revengelibrary-0.1.7.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
revengelibrary/__init__.py,sha256=HWDxVkOjWf65-SM4JIm0p3aNyDoEByCuCjTJCiFgBD4,299
|
|
2
|
-
revengelibrary/chat.py,sha256=a1crCvwW9L3w3YYstE_OFtCM4khrPoGtr8eZFL6-EnQ,7374
|
|
3
|
-
revengelibrary/cli.py,sha256=zllHMGN8EHJCSzMI5kpT8wk2-h9sD_38ysJ9zY6NxO0,2543
|
|
4
|
-
revengelibrary/memory_store.json,sha256=N1F-Xz3GaBn2H1p7uKzhkhKCQV8QVR0t76XD6wmFtXA,3
|
|
5
|
-
revengelibrary-0.1.5.dist-info/licenses/LICENSE,sha256=UgqQ8UtVfIOP8-clGkGAylmb9AUbT4-Ue49N_WjIpi4,1073
|
|
6
|
-
revengelibrary-0.1.5.dist-info/METADATA,sha256=bqiA6Mi7yblgWF42hGOXMO2gDgcExiPNUIEL-U4O6gQ,862
|
|
7
|
-
revengelibrary-0.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
-
revengelibrary-0.1.5.dist-info/entry_points.txt,sha256=299WMDffRchuwIlqEIjW-DwHYkBvRZQwGjiASIVofYE,59
|
|
9
|
-
revengelibrary-0.1.5.dist-info/top_level.txt,sha256=gfUsAxA-IgliQakMoupLAAjrTEYtUJurH5n29eHhUBE,15
|
|
10
|
-
revengelibrary-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|