witbitz-code 1.5.0__tar.gz → 1.5.1__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.
- {witbitz_code-1.5.0/src/witbitz_code.egg-info → witbitz_code-1.5.1}/PKG-INFO +1 -1
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/pyproject.toml +1 -1
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/__init__.py +1 -1
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/ui.py +64 -6
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/ui_static/app.js +10 -2
- {witbitz_code-1.5.0 → witbitz_code-1.5.1/src/witbitz_code.egg-info}/PKG-INFO +1 -1
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_ui.py +51 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/LICENSE +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/README.md +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/setup.cfg +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/__main__.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/_js.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/account.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/asks.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/attachments.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/auto.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/auto_runner.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/cli.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/codekeys.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/codetools.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/compress.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/computers.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/confidential.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/confidential_models.json +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/connector.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/devicelink.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/dirs.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/folders.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/gateway.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/keys.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/link.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/mkdir.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/models.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/net.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/offered_models.json +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/opencode_plugins/notes-init.md +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/opencode_plugins/witbitz-media-server.mjs +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/opencode_plugins/witbitz-notes.js +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/opencode_plugins/witbitz-progress.js +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/outputs.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/pair.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/pairings.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/payers.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/plugins.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/policy.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/ports.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/qr.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/receipt.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/relay.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/seen.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/tinfoil_read.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/tools_catalog.json +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/tools_probe.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/toolsetup.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/ui_static/app.css +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/ui_static/index.html +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/uninstall.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/winagent.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/winconsole.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/wizard.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/wslstart.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code.egg-info/SOURCES.txt +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code.egg-info/dependency_links.txt +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code.egg-info/entry_points.txt +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code.egg-info/requires.txt +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code.egg-info/top_level.txt +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_asks.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_attachments.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_auto.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_cli.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_codekeys.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_codetools.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_confidential.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_connector_e2e.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_devicelink.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_dirs.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_files_and_compress.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_folders.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_outputs.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_pair_flows.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_plugins.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_policy.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_ports.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_ports_win.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_registry.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_relay_codec.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_setup.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_tools_probe.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_toolsetup.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_uninstall.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_winagent.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_winconsole.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_windows_files.py +0 -0
- {witbitz_code-1.5.0 → witbitz_code-1.5.1}/tests/test_wslstart.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "witbitz-code"
|
|
7
|
-
version = "1.5.
|
|
7
|
+
version = "1.5.1"
|
|
8
8
|
description = "Reach OpenCode on this computer from the Witbitz Spaces Code section, through an end-to-end sealed relay."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -35,7 +35,7 @@ from importlib import resources
|
|
|
35
35
|
from pathlib import Path
|
|
36
36
|
from typing import Any, Awaitable, Callable
|
|
37
37
|
|
|
38
|
-
from . import __version__, devicelink, link, net, ports, qr, wizard, wslstart
|
|
38
|
+
from . import __version__, devicelink, link, net, ports, qr, uninstall, wizard, wslstart
|
|
39
39
|
from . import pair as pair_cmd
|
|
40
40
|
from . import pairings as pp
|
|
41
41
|
|
|
@@ -91,7 +91,7 @@ class _Tee(io.TextIOBase):
|
|
|
91
91
|
while "\n" in self._part:
|
|
92
92
|
line, self._part = self._part.split("\n", 1)
|
|
93
93
|
if line.strip() and "\x1b[" not in line:
|
|
94
|
-
self.sink.append(line.rstrip()[:
|
|
94
|
+
self.sink.append(line.rstrip()[:600])
|
|
95
95
|
return len(s)
|
|
96
96
|
|
|
97
97
|
def flush(self) -> None:
|
|
@@ -122,9 +122,13 @@ class Manager:
|
|
|
122
122
|
login_state: Callable[[dict], str] = wslstart.startup_state,
|
|
123
123
|
login_enable: Callable[[dict], tuple] = wslstart.startup_enable,
|
|
124
124
|
login_disable: Callable[[dict], bool] = wslstart.startup_disable,
|
|
125
|
+
port_owners: Callable[[int], list] | None = None, stop_pid: Callable[[int], bool] | None = None,
|
|
125
126
|
label: dict | None = None, log: deque | None = None) -> None:
|
|
126
127
|
self._serve, self._is_listening, self._pair, self._link = serve, is_listening, pair, link_node
|
|
127
128
|
self._load, self._keys_line = load_pairings, keys_line
|
|
129
|
+
self._owners = port_owners or ports.port_owners
|
|
130
|
+
self._stop_pid = stop_pid or uninstall.stop_process
|
|
131
|
+
self._elsewhere_cache: tuple[float, dict | None] | None = None
|
|
128
132
|
self._login_spec, self._login_state, self._login_enable, self._login_disable = login_spec, login_state, login_enable, login_disable
|
|
129
133
|
self.label = label or computer_label()
|
|
130
134
|
self.log: deque = log if log is not None else deque(maxlen=60)
|
|
@@ -196,15 +200,66 @@ class Manager:
|
|
|
196
200
|
for p in ps:
|
|
197
201
|
paired.append({"name": p.get("name") or "", "account": p.get("account") or "", "port": wizard.port_of(p), "keys": await self.keys(p)})
|
|
198
202
|
pair = {k: v for k, v in self.pair_state.items() if k != "log"}
|
|
199
|
-
|
|
203
|
+
serving = self.serving()
|
|
204
|
+
if serving["state"] == "stopped" and ps:
|
|
205
|
+
el = await self.elsewhere()
|
|
206
|
+
if el:
|
|
207
|
+
serving = {"state": "elsewhere", "port": el["port"], "owners": el["owners"]}
|
|
208
|
+
return {"version": __version__, "computer": self.label, "paired": paired, "serving": serving, "pair": pair,
|
|
200
209
|
"login": self.login(), "busy": self.busy, "log": list(self.log)[-14:]}
|
|
201
210
|
|
|
211
|
+
async def elsewhere(self, fresh: bool = False) -> dict | None:
|
|
212
|
+
"""{port, owners} when THIS person's other process holds OpenCode's port or the model proxy's (port + 100) while this
|
|
213
|
+
page serves nothing — `witbitz-code setup` serving in its own window, a login-started copy, an earlier serve (the owner,
|
|
214
|
+
2026-10-09: the installer's setup was serving and the page said Stopped; its Start then failed "held by python.exe").
|
|
215
|
+
Another user's program, or WSL's copy (Windows cannot name it), is not listed: Start then fails and says why."""
|
|
216
|
+
if self.serve_task and not self.serve_task.done():
|
|
217
|
+
return None
|
|
218
|
+
hit = self._elsewhere_cache
|
|
219
|
+
if hit and not fresh and time.monotonic() - hit[0] < 5:
|
|
220
|
+
return hit[1]
|
|
221
|
+
port, owners = self.port(), []
|
|
222
|
+
for q in (port, port + 100):
|
|
223
|
+
if not await self._is_listening(q):
|
|
224
|
+
continue
|
|
225
|
+
try:
|
|
226
|
+
found = await asyncio.to_thread(self._owners, q)
|
|
227
|
+
except Exception: # noqa: BLE001 — a reading, never a failure of the page
|
|
228
|
+
found = []
|
|
229
|
+
for o in found:
|
|
230
|
+
if all(o["pid"] != x["pid"] for x in owners):
|
|
231
|
+
owners.append({"pid": int(o["pid"]), "cmd": str(o.get("cmd") or "")[:80]})
|
|
232
|
+
out = {"port": port, "owners": owners} if owners else None
|
|
233
|
+
self._elsewhere_cache = (time.monotonic(), out)
|
|
234
|
+
return out
|
|
235
|
+
|
|
236
|
+
async def _stop_elsewhere(self, el: dict) -> list[str]:
|
|
237
|
+
"""Stop what elsewhere() named, then wait (≤ 6 s) for both ports to come free. The lines that say what happened."""
|
|
238
|
+
said = []
|
|
239
|
+
for o in el["owners"]:
|
|
240
|
+
ok = await asyncio.to_thread(self._stop_pid, o["pid"])
|
|
241
|
+
said.append(f"{'stopped' if ok else 'could not stop'} {o['cmd'] or 'a program'} (process {o['pid']})")
|
|
242
|
+
self.log.append("witbitz-code: " + said[-1])
|
|
243
|
+
for _ in range(30):
|
|
244
|
+
if not [q for q in (el["port"], el["port"] + 100) if await self._is_listening(q)]:
|
|
245
|
+
break
|
|
246
|
+
await asyncio.sleep(0.2)
|
|
247
|
+
self._elsewhere_cache = None
|
|
248
|
+
return said
|
|
249
|
+
|
|
202
250
|
# ── serving ────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
203
|
-
async def start_serving(self) -> dict:
|
|
251
|
+
async def start_serving(self, takeover: bool = False) -> dict:
|
|
252
|
+
"""Serve from this page. Another copy of witbitz-code already serving here: the page's Start (takeover) stops it
|
|
253
|
+
and runs here instead; the launch's own start leaves it alone (the page then shows where it runs)."""
|
|
204
254
|
if self.serve_task and not self.serve_task.done():
|
|
205
255
|
return {"ok": True}
|
|
206
256
|
if not self.pairings():
|
|
207
257
|
return {"ok": False, "why": "pair this computer first"}
|
|
258
|
+
el = await self.elsewhere(fresh=True)
|
|
259
|
+
if el and not takeover:
|
|
260
|
+
return {"ok": False, "why": "already running in another window", "elsewhere": el}
|
|
261
|
+
if el:
|
|
262
|
+
await self._stop_elsewhere(el)
|
|
208
263
|
self.serve_stop, self.serve_exit, self.live = _StopFlag(), None, {}
|
|
209
264
|
self.serve_port = self.port()
|
|
210
265
|
self.serve_task = asyncio.ensure_future(self._serve_run(self.serve_port))
|
|
@@ -216,7 +271,7 @@ class Manager:
|
|
|
216
271
|
except asyncio.CancelledError:
|
|
217
272
|
raise
|
|
218
273
|
except Exception as e: # noqa: BLE001
|
|
219
|
-
self.serve_exit = {"code": 1, "why": str(e)[:
|
|
274
|
+
self.serve_exit = {"code": 1, "why": str(e)[:500]}
|
|
220
275
|
return 1
|
|
221
276
|
last = next((ln for ln in reversed(self.log) if ln.startswith("witbitz-code:")), "")
|
|
222
277
|
self.serve_exit = {"code": code, "why": "" if code == 0 else last.replace("witbitz-code: ", "", 1)}
|
|
@@ -225,6 +280,9 @@ class Manager:
|
|
|
225
280
|
async def stop_serving(self) -> dict:
|
|
226
281
|
t = self.serve_task
|
|
227
282
|
if t is None or t.done():
|
|
283
|
+
el = await self.elsewhere(fresh=True)
|
|
284
|
+
if el:
|
|
285
|
+
return {"ok": True, "said": await self._stop_elsewhere(el)}
|
|
228
286
|
return {"ok": True}
|
|
229
287
|
self.serve_stop.event.set()
|
|
230
288
|
try:
|
|
@@ -455,7 +513,7 @@ class Server:
|
|
|
455
513
|
return 400, {"content-type": "application/json"}, b'{"error":"not json"}'
|
|
456
514
|
data = data if isinstance(data, dict) else {}
|
|
457
515
|
actions: dict[str, Callable[[], Awaitable[dict]]] = {
|
|
458
|
-
"/api/serve": lambda: self.m.start_serving() if data.get("on") else self.m.stop_serving(),
|
|
516
|
+
"/api/serve": lambda: self.m.start_serving(takeover=True) if data.get("on") else self.m.stop_serving(),
|
|
459
517
|
"/api/pair": self.m.start_pairing,
|
|
460
518
|
"/api/pair/cancel": self.m.cancel_pairing,
|
|
461
519
|
"/api/unpair": lambda: self.m.unpair(str(data.get("account") or "")),
|
|
@@ -31,6 +31,13 @@ const PILL = {
|
|
|
31
31
|
connecting: ['Connecting…', 'connecting', 'reaching the relay'],
|
|
32
32
|
starting: ['Starting…', 'starting', 'starting OpenCode'],
|
|
33
33
|
stopped: ['Stopped', '', 'not reachable from Spaces'],
|
|
34
|
+
elsewhere: ['Running in another window', 'online', ''],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Another copy of witbitz-code serves this computer (setup's window, a login-started one): say which, so Start/Stop make sense.
|
|
38
|
+
function elsewhereText(sv) {
|
|
39
|
+
const who = (sv.owners || []).map((o) => `${o.cmd || 'a program'} (process ${o.pid})`).join(', ')
|
|
40
|
+
return `witbitz-code is running in another window${who ? ' — ' + who : ''}. Run it here to manage it from this page, or Stop it.`
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
function render(s) {
|
|
@@ -43,7 +50,7 @@ function render(s) {
|
|
|
43
50
|
const pill = $('pill')
|
|
44
51
|
pill.textContent = label
|
|
45
52
|
pill.className = 'pill ' + cls
|
|
46
|
-
$('stateText').textContent = run === 'stopped' && s.serving.why ? s.serving.why : hint + (s.serving.port && s.serving.port !== 4096 ? ` · port ${s.serving.port}` : '')
|
|
53
|
+
$('stateText').textContent = run === 'elsewhere' ? elsewhereText(s.serving) : run === 'stopped' && s.serving.why ? s.serving.why : hint + (s.serving.port && s.serving.port !== 4096 ? ` · port ${s.serving.port}` : '')
|
|
47
54
|
|
|
48
55
|
const list = $('paired')
|
|
49
56
|
list.textContent = ''
|
|
@@ -58,7 +65,8 @@ function render(s) {
|
|
|
58
65
|
const waiting = s.pair.state === 'waiting'
|
|
59
66
|
const busy = !!s.busy
|
|
60
67
|
show($('pair'), !paired && !waiting)
|
|
61
|
-
show($('start'), paired && run === 'stopped' && !waiting)
|
|
68
|
+
show($('start'), paired && (run === 'stopped' || run === 'elsewhere') && !waiting)
|
|
69
|
+
$('start').textContent = run === 'elsewhere' ? 'Run it here' : 'Start'
|
|
62
70
|
show($('stop'), paired && run !== 'stopped')
|
|
63
71
|
show($('pairAgain'), paired && !waiting)
|
|
64
72
|
show($('unpair'), paired && !waiting)
|
|
@@ -192,3 +192,54 @@ def test_the_page_names_which_computer_it_controls(monkeypatch):
|
|
|
192
192
|
monkeypatch.setattr(ui.sys, "platform", "linux")
|
|
193
193
|
monkeypatch.setenv("WSL_DISTRO_NAME", "UbuntuD")
|
|
194
194
|
assert ui.computer_label() == {"label": "UbuntuD (WSL) on AlonASUS", "kind": "WSL UbuntuD"}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _other_window_manager(w, held):
|
|
198
|
+
"""A manager on a computer where setup's window already serves port 4097 (+ the proxy's 4197), as the owner's laptop did."""
|
|
199
|
+
stopped = []
|
|
200
|
+
|
|
201
|
+
async def is_listening(p):
|
|
202
|
+
return p in held
|
|
203
|
+
|
|
204
|
+
def stop_pid(pid):
|
|
205
|
+
stopped.append(pid)
|
|
206
|
+
held.clear()
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
m = ui.Manager(serve=w.serve, is_listening=is_listening, pair=w.pair, load_pairings=lambda: list(w.pairings),
|
|
210
|
+
port_owners=lambda q: [{"pid": 19824, "cmd": "python.exe"}] if q in held else [],
|
|
211
|
+
stop_pid=stop_pid, login_spec=lambda: None, label={"label": "AlonASUS", "kind": "Windows"}, log=deque(maxlen=60))
|
|
212
|
+
return m, stopped
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_a_copy_serving_in_another_window_is_shown_and_run_it_here_takes_over():
|
|
216
|
+
async def go():
|
|
217
|
+
w = World([DESK])
|
|
218
|
+
m, stopped = _other_window_manager(w, {4097, 4197})
|
|
219
|
+
assert (await m.status())["serving"] == {"state": "elsewhere", "port": 4097, "owners": [{"pid": 19824, "cmd": "python.exe"}]}
|
|
220
|
+
r = await m.start_serving() # the launch's own start: leaves setup's window alone, starts nothing
|
|
221
|
+
assert r["ok"] is False and stopped == [] and w.serves == []
|
|
222
|
+
srv, base = await with_server(m)
|
|
223
|
+
try:
|
|
224
|
+
async with httpx.AsyncClient() as c:
|
|
225
|
+
assert (await c.post(f"{base}/api/serve", headers=authed(srv), json={"on": True})).json() == {"ok": True}
|
|
226
|
+
assert stopped == [19824], "Run it here stops the other copy first"
|
|
227
|
+
async def served():
|
|
228
|
+
return w.serves == [["--port", "4097"]]
|
|
229
|
+
await until(served)
|
|
230
|
+
assert (await m.status())["serving"]["state"] == "online"
|
|
231
|
+
finally:
|
|
232
|
+
await m.shutdown()
|
|
233
|
+
await srv.close()
|
|
234
|
+
run(go())
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_stop_stops_a_copy_running_in_another_window():
|
|
238
|
+
async def go():
|
|
239
|
+
w = World([DESK])
|
|
240
|
+
m, stopped = _other_window_manager(w, {4197})
|
|
241
|
+
r = await m.stop_serving()
|
|
242
|
+
assert r["ok"] is True and stopped == [19824] and "process 19824" in r["said"][0]
|
|
243
|
+
assert (await m.status())["serving"]["state"] == "stopped"
|
|
244
|
+
assert w.serves == []
|
|
245
|
+
run(go())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/opencode_plugins/witbitz-media-server.mjs
RENAMED
|
File without changes
|
{witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/opencode_plugins/witbitz-notes.js
RENAMED
|
File without changes
|
{witbitz_code-1.5.0 → witbitz_code-1.5.1}/src/witbitz_code/opencode_plugins/witbitz-progress.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|