alissa-tools-github-devloop 0.8.43__tar.gz → 0.8.44__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.
- {alissa_tools_github_devloop-0.8.43/src/main/alissa_tools_github_devloop.egg-info → alissa_tools_github_devloop-0.8.44}/PKG-INFO +1 -1
- alissa_tools_github_devloop-0.8.44/src/main/alissa/tools/github/devloop/version +1 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/webui/sources.py +66 -29
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44/src/main/alissa_tools_github_devloop.egg-info}/PKG-INFO +1 -1
- alissa_tools_github_devloop-0.8.43/src/main/alissa/tools/github/devloop/version +0 -1
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/LICENSE +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/MANIFEST.in +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/NOTICE +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/README.md +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/requirements.txt +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/setup.cfg +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/setup.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/__init__.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/__main__.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/alissa.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/alissa_client.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/bows.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/config.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/fleet_vitals.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/ghclient.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/hubs.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/janitor.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/loop.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/loop_events.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/parking.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/proc.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/prompts.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/state.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/version.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/webui/__init__.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/webui/__main__.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/webui/auth.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/webui/page.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/webui/server.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa/tools/github/devloop/webui/sysinfo.py +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa_tools_github_devloop.egg-info/SOURCES.txt +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa_tools_github_devloop.egg-info/dependency_links.txt +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa_tools_github_devloop.egg-info/entry_points.txt +0 -0
- {alissa_tools_github_devloop-0.8.43 → alissa_tools_github_devloop-0.8.44}/src/main/alissa_tools_github_devloop.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.8.44
|
|
@@ -31,6 +31,7 @@ logic lives here -- the console only moves a clock the daemon already reads.
|
|
|
31
31
|
from __future__ import annotations
|
|
32
32
|
|
|
33
33
|
import json
|
|
34
|
+
import re
|
|
34
35
|
import sqlite3
|
|
35
36
|
import time
|
|
36
37
|
import urllib.error
|
|
@@ -111,8 +112,20 @@ VERSION_CACHE_TTL = 600.0
|
|
|
111
112
|
# non-terminal task corpus 1,440x/day.
|
|
112
113
|
TASKS_CACHE_TTL = 300.0
|
|
113
114
|
# The flag that narrows `alissa task list` to the calling actor's own rows
|
|
114
|
-
# instead of the sponsor's corpus. Newer CLIs only -- see `
|
|
115
|
+
# instead of the sponsor's corpus. Newer CLIs only -- see `_task_list_flags`.
|
|
115
116
|
TASKS_SELF_FLAG = "--self"
|
|
117
|
+
# The pane shows live work only, so the read asks for exactly those statuses
|
|
118
|
+
# rather than every non-terminal row: the pending_validation backlog alone was
|
|
119
|
+
# ~2,600 rows (~3.5 MB) per refresh on prod (issue #168).
|
|
120
|
+
TASKS_STATUS_FLAG = "--status"
|
|
121
|
+
TASKS_LIVE_STATUSES = ("draft", "committed", "in_progress", "blocked")
|
|
122
|
+
# A row cap, a small multiple of what the pane displays, for a CLI that offers
|
|
123
|
+
# one. Kept within the 1..100 the CLI's paged listings accept.
|
|
124
|
+
TASKS_LIMIT_FLAG = "--limit"
|
|
125
|
+
TASKS_LIMIT_FACTOR = 4
|
|
126
|
+
TASKS_LIMIT_MAX = 100
|
|
127
|
+
# The pane's displayed row count (`worker_tasks`' default limit).
|
|
128
|
+
TASKS_DISPLAYED = 25
|
|
116
129
|
# PyPI JSON endpoint for the running-vs-latest drift chip.
|
|
117
130
|
PYPI_URL = "https://pypi.org/pypi/alissa-tools-github-devloop/json"
|
|
118
131
|
# How many minutes past the stale window retry-now ages a row: the daemon
|
|
@@ -194,6 +207,12 @@ def edge_of(name: "str | None") -> "str | None":
|
|
|
194
207
|
return None
|
|
195
208
|
|
|
196
209
|
|
|
210
|
+
def _offers_flag(helptext: str, flag: str) -> bool:
|
|
211
|
+
"""Whether a CLI `--help` text lists `flag` as an option of its own --
|
|
212
|
+
`--self` must not be read off a longer `--self-x` it happens to prefix."""
|
|
213
|
+
return re.search(re.escape(flag) + r"(?![\w-])", helptext) is not None
|
|
214
|
+
|
|
215
|
+
|
|
197
216
|
def _default_http_get(url: str, timeout: float) -> "bytes | None":
|
|
198
217
|
try:
|
|
199
218
|
with urllib.request.urlopen(url, timeout=timeout) as resp: # noqa: S310
|
|
@@ -250,8 +269,8 @@ class Sources:
|
|
|
250
269
|
self._rate_cache = _Cache(RATE_CACHE_TTL, clock)
|
|
251
270
|
self._version_cache = _Cache(VERSION_CACHE_TTL, clock)
|
|
252
271
|
self._tasks_cache = _Cache(TASKS_CACHE_TTL, clock)
|
|
253
|
-
#
|
|
254
|
-
self.
|
|
272
|
+
# None = not probed yet (see `_task_list_flags`).
|
|
273
|
+
self._list_flags: "frozenset[str] | None" = None
|
|
255
274
|
self.boot_wall = int(wall_clock())
|
|
256
275
|
|
|
257
276
|
# -- thin, failure-tolerant subprocess helpers -------------------------
|
|
@@ -460,40 +479,67 @@ class Sources:
|
|
|
460
479
|
drain=self.drain(), now=self._wall(),
|
|
461
480
|
)
|
|
462
481
|
|
|
463
|
-
def
|
|
464
|
-
"""
|
|
482
|
+
def _task_list_flags(self) -> "frozenset[str]":
|
|
483
|
+
"""Which of the narrowing flags (`--self`, `--status`, `--limit`) the
|
|
484
|
+
installed `alissa` CLI offers on `task list`.
|
|
465
485
|
|
|
466
486
|
Read off the CLI's own `--help`, which is local, tokenless and free.
|
|
467
|
-
The alternative -- send
|
|
487
|
+
The alternative -- send a flag and fall back when the call fails --
|
|
468
488
|
cannot tell an unknown flag from an auth hiccup, so it would spend a
|
|
469
|
-
second
|
|
470
|
-
|
|
489
|
+
second, wider call on every unlucky refresh, which is the cost this
|
|
490
|
+
whole path exists to avoid.
|
|
471
491
|
|
|
472
492
|
A probe that *answers* is memoized for the process (the CLI cannot
|
|
473
493
|
change under a running console). A probe that fails is deliberately
|
|
474
494
|
NOT memoized: a transient `alissa` failure then degrades this refresh
|
|
475
|
-
only, instead of pinning the console to the expensive
|
|
495
|
+
only, instead of pinning the console to the expensive unflagged call
|
|
476
496
|
until someone restarts it."""
|
|
477
|
-
if self.
|
|
497
|
+
if self._list_flags is None:
|
|
478
498
|
helptext = self._safe_run(
|
|
479
499
|
["alissa", "task", "list", "--help"], timeout=20
|
|
480
500
|
)
|
|
481
501
|
if helptext is None:
|
|
482
|
-
return
|
|
483
|
-
self.
|
|
484
|
-
|
|
502
|
+
return frozenset()
|
|
503
|
+
self._list_flags = frozenset(
|
|
504
|
+
flag for flag in
|
|
505
|
+
(TASKS_SELF_FLAG, TASKS_STATUS_FLAG, TASKS_LIMIT_FLAG)
|
|
506
|
+
if _offers_flag(helptext, flag)
|
|
507
|
+
)
|
|
508
|
+
return self._list_flags
|
|
485
509
|
|
|
486
|
-
def
|
|
510
|
+
def _supports_self_scope(self) -> bool:
|
|
511
|
+
"""Whether the installed `alissa` CLI understands `task list --self`."""
|
|
512
|
+
return TASKS_SELF_FLAG in self._task_list_flags()
|
|
513
|
+
|
|
514
|
+
def _task_list_argv(self) -> "list[str]":
|
|
515
|
+
"""The `alissa task list` read the pane refreshes from: narrowed by
|
|
516
|
+
every flag the CLI offers -- the actor's own rows (`--self`), the live
|
|
517
|
+
statuses the pane shows (`--status`) and a row cap a small multiple
|
|
518
|
+
of the displayed count (`--limit`). A flag the CLI lacks is simply left
|
|
519
|
+
off; that per-flag degrade is the ONLY way the read widens."""
|
|
520
|
+
flags = self._task_list_flags()
|
|
521
|
+
argv = ["alissa", "task", "list", "--json"]
|
|
522
|
+
if TASKS_SELF_FLAG in flags:
|
|
523
|
+
argv.append(TASKS_SELF_FLAG)
|
|
524
|
+
if TASKS_STATUS_FLAG in flags:
|
|
525
|
+
argv += [TASKS_STATUS_FLAG, ",".join(TASKS_LIVE_STATUSES)]
|
|
526
|
+
if TASKS_LIMIT_FLAG in flags:
|
|
527
|
+
cap = min(TASKS_DISPLAYED * TASKS_LIMIT_FACTOR, TASKS_LIMIT_MAX)
|
|
528
|
+
argv += [TASKS_LIMIT_FLAG, str(cap)]
|
|
529
|
+
return argv
|
|
530
|
+
|
|
531
|
+
def worker_tasks(self, limit: int = TASKS_DISPLAYED) -> "list[dict]":
|
|
487
532
|
"""The worker actor's own tasks via `alissa task list` -- the
|
|
488
533
|
actor-partition bridge (the console runs as the operator, whose actor
|
|
489
534
|
owns the implementation tasks the dev sessions created). Best-effort:
|
|
490
535
|
an old CLI or an auth hiccup just yields an empty list.
|
|
491
536
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
537
|
+
Narrowed (`_task_list_argv`) to what the pane displays wherever the
|
|
538
|
+
installed CLI offers the flags, so a refresh reads the console actor's
|
|
539
|
+
live rows rather than the sponsor's whole non-terminal corpus. Only a
|
|
540
|
+
CLI that lacks a flag reads wider. A narrowed call that fails is never
|
|
541
|
+
retried unflagged (issue #168: that fallback re-read the sponsor's
|
|
542
|
+
whole corpus), and a valid but empty `[]` is a real answer.
|
|
497
543
|
|
|
498
544
|
Cached (TASKS_CACHE_TTL) exactly like the two other remote checks: this
|
|
499
545
|
call rides the shared ALISSA_API_TOKEN the daemon uses, so on the ~10s
|
|
@@ -501,16 +547,7 @@ class Sources:
|
|
|
501
547
|
the API budget. A fetch that fails returns None and keeps the last good
|
|
502
548
|
list (the _Cache contract), never overwriting it with junk."""
|
|
503
549
|
def fetch() -> "list[dict] | None":
|
|
504
|
-
|
|
505
|
-
self_scoped = self._supports_self_scope()
|
|
506
|
-
argv = (unscoped + [TASKS_SELF_FLAG]) if self_scoped else unscoped
|
|
507
|
-
raw = self._safe_json(argv, timeout=45)
|
|
508
|
-
if raw is None and self_scoped:
|
|
509
|
-
# The CLI advertised the flag but the call brought nothing
|
|
510
|
-
# back -- an API that does not serve the scope yet, say. One
|
|
511
|
-
# unflagged retry keeps the pane populated. A valid but empty
|
|
512
|
-
# `[]` is a real answer, not a failure, and is never retried.
|
|
513
|
-
raw = self._safe_json(unscoped, timeout=45)
|
|
550
|
+
raw = self._safe_json(self._task_list_argv(), timeout=45)
|
|
514
551
|
if not isinstance(raw, list):
|
|
515
552
|
return None
|
|
516
553
|
rows = [t for t in raw if isinstance(t, dict)]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.8.43
|
|
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
|