yee88 0.3.0__py3-none-any.whl → 0.6.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.
- yee88/__init__.py +1 -1
- yee88/cli/__init__.py +4 -0
- yee88/cli/cron.py +214 -0
- yee88/cli/reload.py +140 -0
- yee88/cli/run.py +7 -0
- yee88/config.py +2 -2
- yee88/cron/__init__.py +5 -0
- yee88/cron/manager.py +140 -0
- yee88/cron/models.py +13 -0
- yee88/cron/scheduler.py +57 -0
- yee88/markdown.py +2 -0
- yee88/progress.py +3 -0
- yee88/runner.py +1 -0
- yee88/runner_bridge.py +18 -0
- yee88/runners/mock.py +3 -0
- yee88/runtime_loader.py +4 -0
- yee88/settings.py +3 -2
- yee88/skills/yee88/SKILL.md +530 -0
- yee88/telegram/commands/__init__.py +2 -0
- yee88/telegram/commands/executor.py +5 -1
- yee88/telegram/commands/model.py +131 -4
- yee88/telegram/commands/topics.py +10 -0
- yee88/telegram/context.py +6 -0
- yee88/telegram/loop.py +63 -2
- yee88/telegram/onboarding.py +40 -0
- yee88/transport_runtime.py +21 -0
- {yee88-0.3.0.dist-info → yee88-0.6.0.dist-info}/METADATA +2 -1
- {yee88-0.3.0.dist-info → yee88-0.6.0.dist-info}/RECORD +31 -24
- {yee88-0.3.0.dist-info → yee88-0.6.0.dist-info}/WHEEL +0 -0
- {yee88-0.3.0.dist-info → yee88-0.6.0.dist-info}/entry_points.txt +0 -0
- {yee88-0.3.0.dist-info → yee88-0.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -40,6 +40,10 @@ class _ResumeLineProxy:
|
|
|
40
40
|
def engine(self) -> str:
|
|
41
41
|
return self.runner.engine
|
|
42
42
|
|
|
43
|
+
@property
|
|
44
|
+
def model(self) -> str | None:
|
|
45
|
+
return self.runner.model
|
|
46
|
+
|
|
43
47
|
def is_resume_line(self, line: str) -> bool:
|
|
44
48
|
return self.runner.is_resume_line(line)
|
|
45
49
|
|
|
@@ -126,7 +130,7 @@ async def _send_runner_unavailable(
|
|
|
126
130
|
) -> None:
|
|
127
131
|
tracker = ProgressTracker(engine=runner.engine)
|
|
128
132
|
tracker.set_resume(resume_token)
|
|
129
|
-
state = tracker.snapshot(resume_formatter=runner.format_resume)
|
|
133
|
+
state = tracker.snapshot(resume_formatter=runner.format_resume, model=runner.model)
|
|
130
134
|
message = exec_cfg.presenter.render_final(
|
|
131
135
|
state,
|
|
132
136
|
elapsed_s=0.0,
|
yee88/telegram/commands/model.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
import re
|
|
3
5
|
from typing import TYPE_CHECKING
|
|
4
6
|
|
|
5
7
|
from ...context import RunContext
|
|
@@ -8,7 +10,7 @@ from ..engine_overrides import EngineOverrides, resolve_override_value
|
|
|
8
10
|
from ..files import split_command_args
|
|
9
11
|
from ..topic_state import TopicStateStore
|
|
10
12
|
from ..topics import _topic_key
|
|
11
|
-
from ..types import TelegramIncomingMessage
|
|
13
|
+
from ..types import TelegramCallbackQuery, TelegramIncomingMessage
|
|
12
14
|
from .overrides import (
|
|
13
15
|
ENGINE_SOURCE_LABELS,
|
|
14
16
|
OVERRIDE_SOURCE_LABELS,
|
|
@@ -22,12 +24,107 @@ from .reply import make_reply
|
|
|
22
24
|
if TYPE_CHECKING:
|
|
23
25
|
from ..bridge import TelegramBridgeConfig
|
|
24
26
|
|
|
27
|
+
# Callback data prefix for model selection
|
|
28
|
+
MODEL_SELECT_CALLBACK_PREFIX = "yee88:model_select:"
|
|
29
|
+
|
|
25
30
|
MODEL_USAGE = (
|
|
26
|
-
"usage: `/model`, `/model set <model>`, "
|
|
31
|
+
"usage: `/model`, `/model status`, `/model set <model>`, "
|
|
27
32
|
"`/model set <engine> <model>`, or `/model clear [engine]`"
|
|
28
33
|
)
|
|
29
34
|
|
|
30
35
|
|
|
36
|
+
async def _get_opencode_models() -> list[str]:
|
|
37
|
+
"""Fetch available models from opencode CLI."""
|
|
38
|
+
try:
|
|
39
|
+
proc = await asyncio.create_subprocess_exec(
|
|
40
|
+
"opencode",
|
|
41
|
+
"models",
|
|
42
|
+
stdout=asyncio.subprocess.PIPE,
|
|
43
|
+
stderr=asyncio.subprocess.PIPE,
|
|
44
|
+
)
|
|
45
|
+
stdout, _ = await proc.communicate()
|
|
46
|
+
if proc.returncode != 0:
|
|
47
|
+
return []
|
|
48
|
+
models = stdout.decode().strip().split("\n")
|
|
49
|
+
return [m.strip() for m in models if m.strip()]
|
|
50
|
+
except Exception:
|
|
51
|
+
return []
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def _send_model_selector(
|
|
55
|
+
cfg: TelegramBridgeConfig,
|
|
56
|
+
msg: TelegramIncomingMessage,
|
|
57
|
+
engine: str,
|
|
58
|
+
models: list[str],
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Send a copyable model list for selection."""
|
|
61
|
+
if not models:
|
|
62
|
+
await cfg.bot.send_message(
|
|
63
|
+
chat_id=msg.chat_id,
|
|
64
|
+
text="No models available.",
|
|
65
|
+
reply_to_message_id=msg.message_id,
|
|
66
|
+
message_thread_id=msg.thread_id,
|
|
67
|
+
)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
header = f"Models for {engine}:\n\n"
|
|
71
|
+
max_len = 4000
|
|
72
|
+
chunks: list[str] = []
|
|
73
|
+
current = header
|
|
74
|
+
|
|
75
|
+
for model in models:
|
|
76
|
+
line = f"<code>/model set {model}</code>\n"
|
|
77
|
+
if len(current) + len(line) > max_len:
|
|
78
|
+
chunks.append(current.rstrip())
|
|
79
|
+
current = line
|
|
80
|
+
else:
|
|
81
|
+
current += line
|
|
82
|
+
|
|
83
|
+
if current.strip():
|
|
84
|
+
chunks.append(current.rstrip())
|
|
85
|
+
|
|
86
|
+
for i, chunk in enumerate(chunks):
|
|
87
|
+
await cfg.bot.send_message(
|
|
88
|
+
chat_id=msg.chat_id,
|
|
89
|
+
text=chunk,
|
|
90
|
+
reply_to_message_id=msg.message_id if i == 0 else None,
|
|
91
|
+
message_thread_id=msg.thread_id,
|
|
92
|
+
parse_mode="HTML",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def handle_model_select_callback(
|
|
97
|
+
cfg: TelegramBridgeConfig,
|
|
98
|
+
query: TelegramCallbackQuery,
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Handle model selection from inline keyboard."""
|
|
101
|
+
if not query.data:
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
# Parse callback data: yee88:model_select:<engine>:<model>
|
|
105
|
+
prefix = MODEL_SELECT_CALLBACK_PREFIX
|
|
106
|
+
if not query.data.startswith(prefix):
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
data = query.data[len(prefix):]
|
|
110
|
+
if ":" not in data:
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
engine, model = data.split(":", 1)
|
|
114
|
+
|
|
115
|
+
# Answer the callback query
|
|
116
|
+
await cfg.bot.answer_callback_query(
|
|
117
|
+
callback_query_id=query.callback_query_id,
|
|
118
|
+
text=f"Setting model to {model}...",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Send the model set command as a new message
|
|
122
|
+
await cfg.bot.send_message(
|
|
123
|
+
chat_id=query.chat_id,
|
|
124
|
+
text=f"/model set {engine} {model}",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
31
128
|
async def _handle_model_command(
|
|
32
129
|
cfg: TelegramBridgeConfig,
|
|
33
130
|
msg: TelegramIncomingMessage,
|
|
@@ -46,10 +143,39 @@ async def _handle_model_command(
|
|
|
46
143
|
else None
|
|
47
144
|
)
|
|
48
145
|
tokens = split_command_args(args_text)
|
|
49
|
-
action = tokens[0].lower() if tokens else "
|
|
146
|
+
action = tokens[0].lower() if tokens else ""
|
|
50
147
|
engine_ids = {engine.lower() for engine in cfg.runtime.engine_ids}
|
|
51
148
|
|
|
52
|
-
if action
|
|
149
|
+
if action == "":
|
|
150
|
+
selection = await resolve_engine_selection(
|
|
151
|
+
cfg,
|
|
152
|
+
msg,
|
|
153
|
+
ambient_context=ambient_context,
|
|
154
|
+
topic_store=topic_store,
|
|
155
|
+
chat_prefs=chat_prefs,
|
|
156
|
+
topic_key=tkey,
|
|
157
|
+
)
|
|
158
|
+
if selection is None:
|
|
159
|
+
return
|
|
160
|
+
engine, _ = selection
|
|
161
|
+
|
|
162
|
+
if engine == "opencode":
|
|
163
|
+
models = await _get_opencode_models()
|
|
164
|
+
opencode_cfg = cfg.runtime.engine_config("opencode")
|
|
165
|
+
model_filter = opencode_cfg.get("model_filter")
|
|
166
|
+
if model_filter and isinstance(model_filter, str):
|
|
167
|
+
try:
|
|
168
|
+
pattern = re.compile(model_filter, re.IGNORECASE)
|
|
169
|
+
models = [m for m in models if pattern.search(m)]
|
|
170
|
+
except re.error:
|
|
171
|
+
pass
|
|
172
|
+
if models:
|
|
173
|
+
await _send_model_selector(cfg, msg, engine, models)
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
action = "status"
|
|
177
|
+
|
|
178
|
+
if action == "status":
|
|
53
179
|
selection = await resolve_engine_selection(
|
|
54
180
|
cfg,
|
|
55
181
|
msg,
|
|
@@ -61,6 +187,7 @@ async def _handle_model_command(
|
|
|
61
187
|
if selection is None:
|
|
62
188
|
return
|
|
63
189
|
engine, engine_source = selection
|
|
190
|
+
|
|
64
191
|
topic_override = None
|
|
65
192
|
if tkey is not None and topic_store is not None:
|
|
66
193
|
topic_override = await topic_store.get_engine_override(
|
|
@@ -69,6 +69,14 @@ async def _handle_ctx_command(
|
|
|
69
69
|
chat_id=msg.chat_id,
|
|
70
70
|
ambient_context=ambient,
|
|
71
71
|
)
|
|
72
|
+
engine = None
|
|
73
|
+
model = None
|
|
74
|
+
if snapshot is not None:
|
|
75
|
+
engine = snapshot.default_engine
|
|
76
|
+
if engine is not None:
|
|
77
|
+
override = await store.get_engine_override(tkey[0], tkey[1], engine)
|
|
78
|
+
if override is not None and override.model is not None:
|
|
79
|
+
model = override.model
|
|
72
80
|
text = _format_ctx_status(
|
|
73
81
|
cfg=cfg,
|
|
74
82
|
runtime=cfg.runtime,
|
|
@@ -77,6 +85,8 @@ async def _handle_ctx_command(
|
|
|
77
85
|
context_source=resolved.context_source,
|
|
78
86
|
snapshot=snapshot,
|
|
79
87
|
chat_project=chat_project,
|
|
88
|
+
engine=engine,
|
|
89
|
+
model=model,
|
|
80
90
|
)
|
|
81
91
|
await reply(text=text)
|
|
82
92
|
return
|
yee88/telegram/context.py
CHANGED
|
@@ -107,6 +107,8 @@ def _format_ctx_status(
|
|
|
107
107
|
context_source: str,
|
|
108
108
|
snapshot: TopicThreadSnapshot | None,
|
|
109
109
|
chat_project: str | None,
|
|
110
|
+
engine: str | None = None,
|
|
111
|
+
model: str | None = None,
|
|
110
112
|
) -> str:
|
|
111
113
|
lines = [
|
|
112
114
|
f"topics: enabled (scope={_topics_scope_label(cfg)})",
|
|
@@ -125,6 +127,10 @@ def _format_ctx_status(
|
|
|
125
127
|
if snapshot is not None and snapshot.sessions:
|
|
126
128
|
sessions = ", ".join(sorted(snapshot.sessions))
|
|
127
129
|
lines.append(f"sessions: {sessions or 'none'}")
|
|
130
|
+
if engine is not None:
|
|
131
|
+
lines.append(f"engine: {engine}")
|
|
132
|
+
if model is not None:
|
|
133
|
+
lines.append(f"model: {model}")
|
|
128
134
|
return "\n".join(lines)
|
|
129
135
|
|
|
130
136
|
|
yee88/telegram/loop.py
CHANGED
|
@@ -6,11 +6,17 @@ from functools import partial
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import TYPE_CHECKING, cast
|
|
8
8
|
|
|
9
|
+
import signal
|
|
10
|
+
|
|
9
11
|
import anyio
|
|
10
12
|
from anyio.abc import TaskGroup
|
|
11
13
|
|
|
12
|
-
from ..
|
|
14
|
+
from ..cli.reload import request_reload
|
|
15
|
+
from ..config import ConfigError, HOME_CONFIG_PATH
|
|
13
16
|
from ..config_watch import ConfigReload, watch_config as watch_config_changes
|
|
17
|
+
from ..cron.manager import CronManager
|
|
18
|
+
from ..cron.scheduler import CronScheduler
|
|
19
|
+
from ..cron.models import CronJob
|
|
14
20
|
from ..commands import list_command_ids
|
|
15
21
|
from ..directives import DirectiveError
|
|
16
22
|
from ..logging import get_logger
|
|
@@ -25,6 +31,10 @@ from ..context import RunContext
|
|
|
25
31
|
from ..ids import RESERVED_CHAT_COMMANDS
|
|
26
32
|
from .bridge import CANCEL_CALLBACK_DATA, TelegramBridgeConfig, send_plain
|
|
27
33
|
from .commands.cancel import handle_callback_cancel, handle_cancel
|
|
34
|
+
from .commands.model import (
|
|
35
|
+
MODEL_SELECT_CALLBACK_PREFIX,
|
|
36
|
+
handle_model_select_callback,
|
|
37
|
+
)
|
|
28
38
|
from .commands.file_transfer import FILE_PUT_USAGE
|
|
29
39
|
from .commands.handlers import (
|
|
30
40
|
dispatch_command,
|
|
@@ -1066,6 +1076,48 @@ async def run_main_loop(
|
|
|
1066
1076
|
|
|
1067
1077
|
tg.start_soon(run_config_watch)
|
|
1068
1078
|
|
|
1079
|
+
if config_path is not None:
|
|
1080
|
+
cron_manager = CronManager(config_path.parent)
|
|
1081
|
+
|
|
1082
|
+
async def _execute_cron_job(job: CronJob) -> None:
|
|
1083
|
+
try:
|
|
1084
|
+
context = RunContext(project=job.project) if job.project else None
|
|
1085
|
+
await run_job(
|
|
1086
|
+
chat_id=cfg.chat_id,
|
|
1087
|
+
user_msg_id=0,
|
|
1088
|
+
text=job.message,
|
|
1089
|
+
resume_token=None,
|
|
1090
|
+
context=context,
|
|
1091
|
+
thread_id=None,
|
|
1092
|
+
force_hide_resume_line=True,
|
|
1093
|
+
)
|
|
1094
|
+
except Exception as exc:
|
|
1095
|
+
logger.error(
|
|
1096
|
+
"cron.job_failed",
|
|
1097
|
+
job_id=job.id,
|
|
1098
|
+
error=str(exc),
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
cron_scheduler = CronScheduler(cron_manager, _execute_cron_job, tg)
|
|
1102
|
+
|
|
1103
|
+
async def run_cron_scheduler() -> None:
|
|
1104
|
+
await cron_scheduler.start()
|
|
1105
|
+
|
|
1106
|
+
tg.start_soon(run_cron_scheduler)
|
|
1107
|
+
|
|
1108
|
+
async def run_signal_watcher() -> None:
|
|
1109
|
+
if not hasattr(signal, "SIGHUP"):
|
|
1110
|
+
return
|
|
1111
|
+
with anyio.open_signal_receiver(signal.SIGHUP) as signals:
|
|
1112
|
+
async for signum in signals:
|
|
1113
|
+
if signum == signal.SIGHUP:
|
|
1114
|
+
logger.info("reload.signal_received", signal="SIGHUP")
|
|
1115
|
+
request_reload()
|
|
1116
|
+
tg.cancel_scope.cancel()
|
|
1117
|
+
return
|
|
1118
|
+
|
|
1119
|
+
tg.start_soon(run_signal_watcher)
|
|
1120
|
+
|
|
1069
1121
|
def wrap_on_thread_known(
|
|
1070
1122
|
base_cb: Callable[[ResumeToken, anyio.Event], Awaitable[None]] | None,
|
|
1071
1123
|
topic_key: tuple[int, int] | None,
|
|
@@ -1104,6 +1156,7 @@ async def run_main_loop(
|
|
|
1104
1156
|
| None = None,
|
|
1105
1157
|
engine_override: EngineId | None = None,
|
|
1106
1158
|
progress_ref: MessageRef | None = None,
|
|
1159
|
+
force_hide_resume_line: bool = False,
|
|
1107
1160
|
) -> None:
|
|
1108
1161
|
topic_key = (
|
|
1109
1162
|
(chat_id, thread_id)
|
|
@@ -1115,7 +1168,7 @@ async def run_main_loop(
|
|
|
1115
1168
|
else None
|
|
1116
1169
|
)
|
|
1117
1170
|
stateful_mode = topic_key is not None or chat_session_key is not None
|
|
1118
|
-
show_resume_line = should_show_resume_line(
|
|
1171
|
+
show_resume_line = False if force_hide_resume_line else should_show_resume_line(
|
|
1119
1172
|
show_resume_line=cfg.show_resume_line,
|
|
1120
1173
|
stateful_mode=stateful_mode,
|
|
1121
1174
|
context=context,
|
|
@@ -1808,6 +1861,14 @@ async def run_main_loop(
|
|
|
1808
1861
|
state.running_tasks,
|
|
1809
1862
|
scheduler,
|
|
1810
1863
|
)
|
|
1864
|
+
elif update.data and update.data.startswith(
|
|
1865
|
+
MODEL_SELECT_CALLBACK_PREFIX
|
|
1866
|
+
):
|
|
1867
|
+
tg.start_soon(
|
|
1868
|
+
handle_model_select_callback,
|
|
1869
|
+
cfg,
|
|
1870
|
+
update,
|
|
1871
|
+
)
|
|
1811
1872
|
else:
|
|
1812
1873
|
tg.start_soon(
|
|
1813
1874
|
cfg.bot.answer_callback_query,
|
yee88/telegram/onboarding.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import shutil
|
|
4
|
+
import subprocess
|
|
4
5
|
from contextlib import contextmanager
|
|
5
6
|
from collections.abc import Awaitable, Callable
|
|
6
7
|
from dataclasses import dataclass
|
|
@@ -44,6 +45,8 @@ from .api_models import User
|
|
|
44
45
|
from .client import TelegramClient, TelegramRetryAfter
|
|
45
46
|
from .topics import _validate_topics_setup_for
|
|
46
47
|
|
|
48
|
+
import yee88
|
|
49
|
+
|
|
47
50
|
__all__ = [
|
|
48
51
|
"ChatInfo",
|
|
49
52
|
"check_setup",
|
|
@@ -929,6 +932,42 @@ async def step_save_config(ui: UI, svc: Services, state: OnboardingState) -> Non
|
|
|
929
932
|
ui.print(Text("✓ setup complete. starting yee88...", style="green"))
|
|
930
933
|
|
|
931
934
|
|
|
935
|
+
async def step_install_skill(ui: UI, _svc: Services, _state: OnboardingState) -> None:
|
|
936
|
+
install = await ui.confirm(
|
|
937
|
+
"install yee88 skill for AI agents (opencode, claude-code, etc.)?",
|
|
938
|
+
default=True,
|
|
939
|
+
)
|
|
940
|
+
if not install:
|
|
941
|
+
ui.print(" skipped skill installation")
|
|
942
|
+
return
|
|
943
|
+
|
|
944
|
+
skills_path = Path(yee88.__file__).parent / "skills"
|
|
945
|
+
if not skills_path.exists():
|
|
946
|
+
ui.print(f" warning: skills not found at {skills_path}")
|
|
947
|
+
return
|
|
948
|
+
|
|
949
|
+
ui.print(" installing yee88 skill...")
|
|
950
|
+
try:
|
|
951
|
+
result = subprocess.run(
|
|
952
|
+
["npx", "skills", "add", str(skills_path), "-g", "--all", "-y"],
|
|
953
|
+
capture_output=True,
|
|
954
|
+
text=True,
|
|
955
|
+
timeout=60,
|
|
956
|
+
)
|
|
957
|
+
if result.returncode == 0:
|
|
958
|
+
ui.print(" ✓ skill installed successfully")
|
|
959
|
+
else:
|
|
960
|
+
ui.print(f" warning: skill installation failed (exit code {result.returncode})")
|
|
961
|
+
if result.stderr:
|
|
962
|
+
ui.print(f" error: {result.stderr.strip()}")
|
|
963
|
+
except subprocess.TimeoutExpired:
|
|
964
|
+
ui.print(" warning: skill installation timed out")
|
|
965
|
+
except FileNotFoundError:
|
|
966
|
+
ui.print(" warning: npx not found. install node.js to use skills.")
|
|
967
|
+
except Exception as exc:
|
|
968
|
+
ui.print(f" warning: skill installation failed: {exc}")
|
|
969
|
+
|
|
970
|
+
|
|
932
971
|
def always_true(_state: OnboardingState) -> bool:
|
|
933
972
|
return True
|
|
934
973
|
|
|
@@ -947,6 +986,7 @@ STEPS: list[OnboardingStep] = [
|
|
|
947
986
|
OnboardingStep("connect chat", 3, step_capture_chat),
|
|
948
987
|
OnboardingStep("default engine", 4, step_default_engine),
|
|
949
988
|
OnboardingStep("save config", 5, step_save_config),
|
|
989
|
+
OnboardingStep("install skill", 6, step_install_skill),
|
|
950
990
|
]
|
|
951
991
|
|
|
952
992
|
|
yee88/transport_runtime.py
CHANGED
|
@@ -52,6 +52,7 @@ class TransportRuntime:
|
|
|
52
52
|
"_allowlist",
|
|
53
53
|
"_config_path",
|
|
54
54
|
"_plugin_configs",
|
|
55
|
+
"_engine_configs",
|
|
55
56
|
"_watch_config",
|
|
56
57
|
)
|
|
57
58
|
|
|
@@ -63,6 +64,7 @@ class TransportRuntime:
|
|
|
63
64
|
allowlist: Iterable[str] | None = None,
|
|
64
65
|
config_path: Path | None = None,
|
|
65
66
|
plugin_configs: Mapping[str, Any] | None = None,
|
|
67
|
+
engine_configs: Mapping[str, Any] | None = None,
|
|
66
68
|
watch_config: bool = False,
|
|
67
69
|
) -> None:
|
|
68
70
|
self._apply(
|
|
@@ -71,6 +73,7 @@ class TransportRuntime:
|
|
|
71
73
|
allowlist=allowlist,
|
|
72
74
|
config_path=config_path,
|
|
73
75
|
plugin_configs=plugin_configs,
|
|
76
|
+
engine_configs=engine_configs,
|
|
74
77
|
watch_config=watch_config,
|
|
75
78
|
)
|
|
76
79
|
|
|
@@ -82,6 +85,7 @@ class TransportRuntime:
|
|
|
82
85
|
allowlist: Iterable[str] | None = None,
|
|
83
86
|
config_path: Path | None = None,
|
|
84
87
|
plugin_configs: Mapping[str, Any] | None = None,
|
|
88
|
+
engine_configs: Mapping[str, Any] | None = None,
|
|
85
89
|
watch_config: bool = False,
|
|
86
90
|
) -> None:
|
|
87
91
|
self._apply(
|
|
@@ -90,6 +94,7 @@ class TransportRuntime:
|
|
|
90
94
|
allowlist=allowlist,
|
|
91
95
|
config_path=config_path,
|
|
92
96
|
plugin_configs=plugin_configs,
|
|
97
|
+
engine_configs=engine_configs,
|
|
93
98
|
watch_config=watch_config,
|
|
94
99
|
)
|
|
95
100
|
|
|
@@ -101,6 +106,7 @@ class TransportRuntime:
|
|
|
101
106
|
allowlist: Iterable[str] | None,
|
|
102
107
|
config_path: Path | None,
|
|
103
108
|
plugin_configs: Mapping[str, Any] | None,
|
|
109
|
+
engine_configs: Mapping[str, Any] | None,
|
|
104
110
|
watch_config: bool,
|
|
105
111
|
) -> None:
|
|
106
112
|
self._router = router
|
|
@@ -108,6 +114,7 @@ class TransportRuntime:
|
|
|
108
114
|
self._allowlist = normalize_allowlist(allowlist)
|
|
109
115
|
self._config_path = config_path
|
|
110
116
|
self._plugin_configs = dict(plugin_configs or {})
|
|
117
|
+
self._engine_configs = dict(engine_configs or {})
|
|
111
118
|
self._watch_config = watch_config
|
|
112
119
|
|
|
113
120
|
@property
|
|
@@ -147,6 +154,10 @@ class TransportRuntime:
|
|
|
147
154
|
def project_aliases(self) -> tuple[str, ...]:
|
|
148
155
|
return tuple(project.alias for project in self._projects.projects.values())
|
|
149
156
|
|
|
157
|
+
@property
|
|
158
|
+
def projects(self) -> ProjectsConfig:
|
|
159
|
+
return self._projects
|
|
160
|
+
|
|
150
161
|
@property
|
|
151
162
|
def allowlist(self) -> set[str] | None:
|
|
152
163
|
return self._allowlist
|
|
@@ -172,6 +183,16 @@ class TransportRuntime:
|
|
|
172
183
|
)
|
|
173
184
|
return dict(raw)
|
|
174
185
|
|
|
186
|
+
def engine_config(self, engine_id: str) -> dict[str, Any]:
|
|
187
|
+
if not self._engine_configs:
|
|
188
|
+
return {}
|
|
189
|
+
raw = self._engine_configs.get(engine_id)
|
|
190
|
+
if raw is None:
|
|
191
|
+
return {}
|
|
192
|
+
if not isinstance(raw, dict):
|
|
193
|
+
return {}
|
|
194
|
+
return dict(raw)
|
|
195
|
+
|
|
175
196
|
def resolve_message(
|
|
176
197
|
self,
|
|
177
198
|
*,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yee88
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Telegram bridge for Codex, Claude Code, and other agent CLIs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/banteg/yee88
|
|
6
6
|
Project-URL: Documentation, https://yee88.dev/
|
|
@@ -36,6 +36,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
36
36
|
Classifier: Programming Language :: Python :: 3.14
|
|
37
37
|
Requires-Python: >=3.14
|
|
38
38
|
Requires-Dist: anyio>=4.12.0
|
|
39
|
+
Requires-Dist: croniter>=2.0.0
|
|
39
40
|
Requires-Dist: httpx>=0.28.1
|
|
40
41
|
Requires-Dist: markdown-it-py
|
|
41
42
|
Requires-Dist: msgspec>=0.20.0
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
yee88/__init__.py,sha256=
|
|
1
|
+
yee88/__init__.py,sha256=42STGor_9nKYXumfeV5tiyD_M8VdcddX7CEexmibPBk,22
|
|
2
2
|
yee88/api.py,sha256=SnUoG0xnjgaSyqM9retJS9wd3axBEI8MCOkrBNz4w8A,2876
|
|
3
3
|
yee88/backends.py,sha256=80ScxPEse-BxVfK0zA6bAaorp2GpTpCWsgX3TTqmumI,543
|
|
4
4
|
yee88/backends_helpers.py,sha256=d84XilyOzVVCq5akygVbH85JbapjTaSUZKeGcAml15I,368
|
|
5
5
|
yee88/commands.py,sha256=DplCjFC8QQM2MYoblZS8HI6OngKmQLO2emxCi8NIOtY,3298
|
|
6
|
-
yee88/config.py,sha256=
|
|
6
|
+
yee88/config.py,sha256=YACUptmkn8TbUvg16kerNT7Pj78fukm99QzFwGA0X2s,4293
|
|
7
7
|
yee88/config_migrations.py,sha256=H-wJZz0HcwmTcNCulXj1nFR8I2ilUQtjo5upMRLrPWc,3501
|
|
8
8
|
yee88/config_watch.py,sha256=GBu4KrlgKACWalPFK-7C3Qo1v1BiOYOiOtK5OUOqz9c,4390
|
|
9
9
|
yee88/context.py,sha256=2GXdWY8KpWbEARJNo34unLN8nqGBtiE72Z_mjwrJTUM,187
|
|
@@ -13,33 +13,39 @@ yee88/events.py,sha256=1yo6l5D4xFreyPoU1d4L1ckaplPizxpFeJJevP8JDtk,4225
|
|
|
13
13
|
yee88/ids.py,sha256=uU2A7x61Xn3_Pj-YgY5-EruoOtwDSXOp2D-XduTqAEo,534
|
|
14
14
|
yee88/lockfile.py,sha256=xCSsxVQMqnCN2RWFkkDOVg1qm5D-Sb4AMKr6FdP0kbY,4162
|
|
15
15
|
yee88/logging.py,sha256=aqoIClE5_vlASKXsc9FjrmsmBy-7jLYOomHheSWSadA,8227
|
|
16
|
-
yee88/markdown.py,sha256=
|
|
16
|
+
yee88/markdown.py,sha256=__2qyoBXTBZVVD7DgZvjDaj67xQCJHMfU0yQ8jcY1pQ,9144
|
|
17
17
|
yee88/model.py,sha256=G8Aj2nyTM6_vmHh12vEXlvhNJHR1ZBpsPBKw-DboDbs,1695
|
|
18
18
|
yee88/plugins.py,sha256=XdvttyxL2j65l88P4yhGTzaoBbkCka2-PPTgJXuH0i4,9150
|
|
19
19
|
yee88/presenter.py,sha256=rhPIytd2ANoe5S6kGeVvTzsPSQzO5AEMBXK3F_zJEx4,513
|
|
20
|
-
yee88/progress.py,sha256=
|
|
20
|
+
yee88/progress.py,sha256=tbU81DIqgsQlh9qnPlxio4esqQ16QxwTubBTIGsdRLg,3243
|
|
21
21
|
yee88/router.py,sha256=pZkFI7OsUYeqjS2JmnL4gmHSPbEg8WF7_mQhzV45BCE,3853
|
|
22
|
-
yee88/runner.py,sha256=
|
|
23
|
-
yee88/runner_bridge.py,sha256=
|
|
24
|
-
yee88/runtime_loader.py,sha256=
|
|
22
|
+
yee88/runner.py,sha256=a0GZE9anqAnsZeVhzn6QaKl1CmXbMARPSrFlfLV-nac,21498
|
|
23
|
+
yee88/runner_bridge.py,sha256=CdD43GfU1rHktBf2FL9K4IK3wIY4BOFfUwx8XzNC-ck,19707
|
|
24
|
+
yee88/runtime_loader.py,sha256=LP-kuEXb4bG2MXEKHRKdYVYmwDTry3SgByKREk20sGM,6435
|
|
25
25
|
yee88/scheduler.py,sha256=ss_90rpZ3aKh_GutKhMwn8KWpg5QykkE2r6-ltimExk,5590
|
|
26
|
-
yee88/settings.py,sha256=
|
|
26
|
+
yee88/settings.py,sha256=slm41hLWBn2zm9iLEs84M89GH_9FaOPTcFHBN3Hg3k0,12105
|
|
27
27
|
yee88/transport.py,sha256=hzVJJO4mk1CaXgzsxSiMDqYA8xKMPD-SSQKhHDnwlzE,1330
|
|
28
|
-
yee88/transport_runtime.py,sha256=
|
|
28
|
+
yee88/transport_runtime.py,sha256=3NCcnnHAollwTOwpQzA9pYIF2jKNEZx3qKUIvK3d0Zk,10986
|
|
29
29
|
yee88/transports.py,sha256=8igVsWgvx4d743SE98LyNuKYaci0-X61IqNiO4U-_Ck,2013
|
|
30
30
|
yee88/worktrees.py,sha256=KDaT1S0-kQH5v0FmiWgG51jgGviyATVEgfoqRrLmh7Y,3907
|
|
31
|
-
yee88/cli/__init__.py,sha256=
|
|
31
|
+
yee88/cli/__init__.py,sha256=vJjSKBL51hRAXWHK0W1P93AKbKniYc8eR25UVY_MctI,6597
|
|
32
32
|
yee88/cli/config.py,sha256=gTDdaTkJGGooeGECgcOXkpF-Y7Kmqi_Iyum0ghQzazg,9560
|
|
33
|
+
yee88/cli/cron.py,sha256=uvzgkAmMom8sDMjLgdMcB8V7DACmmdoGTiefif1Ol3U,6215
|
|
33
34
|
yee88/cli/doctor.py,sha256=FPLXk-ZSPZiHtWrncEqborcz4e1dhb3S5sUVcOw-n60,6000
|
|
34
35
|
yee88/cli/init.py,sha256=y1Z08rh9t4RPhzsgJRtopgd6UvjEPYi9Wfv_t2KlrxI,3761
|
|
35
36
|
yee88/cli/onboarding_cmd.py,sha256=VNJl9cpfz-Bo7LNpGUIH-VtbOFdATTn1ry21cwtyQQM,4170
|
|
36
37
|
yee88/cli/plugins.py,sha256=Kxr1FgLIjN6c8il83_cfE9nix0jYPUvI6KXtol3Swow,6319
|
|
37
|
-
yee88/cli/
|
|
38
|
+
yee88/cli/reload.py,sha256=ays9R2kJ0IQEPGfxb3BY7R2mxbJBmp325dKrqqv1nw8,3552
|
|
39
|
+
yee88/cli/run.py,sha256=qKMJCEwoiNN4Qqt59Gmm1bDQHTboGf8JO73gMeACAck,14197
|
|
38
40
|
yee88/cli/topic.py,sha256=6j_o0wpHMfkyeyj9wcjU5T5PVXw_cOM0qM5fsMDRiIw,11019
|
|
41
|
+
yee88/cron/__init__.py,sha256=WZN2RcqJD4i6ZPZjI6b_sKI1VxhNGzhDu2Pju5u9J0o,152
|
|
42
|
+
yee88/cron/manager.py,sha256=YNIjzQwIS1fMi83LzZe2pxYSu2uPbey0UKtXa6xUmq8,4050
|
|
43
|
+
yee88/cron/models.py,sha256=IZtUvMBr2_wZbgEWR3U9yQW-QEOMzUybDER8B7Ne8Go,224
|
|
44
|
+
yee88/cron/scheduler.py,sha256=ITNqpF4MXUieuuFBvVHgzOVxxR5bnhGAFYvWTJWy-IU,1922
|
|
39
45
|
yee88/runners/__init__.py,sha256=McKaMqLXT9dJlgiEwKf6biD0Ns66Fk7SrxwtcP0ZgzI,30
|
|
40
46
|
yee88/runners/claude.py,sha256=-s88BOSTWxp4e7r6QJgUV3RJsEMEfSjk0Z9OmnZQB9Y,15296
|
|
41
47
|
yee88/runners/codex.py,sha256=oen08lhgaDdEn52x-GDcL7m4Hiaq9Vz3kb4b8KXh3yc,20956
|
|
42
|
-
yee88/runners/mock.py,sha256=
|
|
48
|
+
yee88/runners/mock.py,sha256=pC6AkPFe5mDu3qJOQh_5sewaTxzNDYvslCZP6QbuGPI,6700
|
|
43
49
|
yee88/runners/opencode.py,sha256=QEvpSSRtP5Uv-kzEp6D-85o2II886IHsEV-Us8vqsjA,15018
|
|
44
50
|
yee88/runners/pi.py,sha256=PH8xw_IBZzDo1KQ6AUQEPhqVRo-kb5I2OVKJrIuJq0w,16087
|
|
45
51
|
yee88/runners/run_options.py,sha256=83uU9k5vnBSbk4wT4tIDkzOwMa3qL0KK-tdQgomre6w,915
|
|
@@ -49,6 +55,7 @@ yee88/schemas/claude.py,sha256=HqOik1O4u3WcMb4RN2cTVJw6VRYn3EaYj8h5Sevs1XY,5702
|
|
|
49
55
|
yee88/schemas/codex.py,sha256=bgIsh35LuaqoOYdTl6BWR-mn-mvh3ouwes_Jn9JMVXg,3412
|
|
50
56
|
yee88/schemas/opencode.py,sha256=ODhnKXTzxZ_8qaQ7AYqXB7J-XoAjQnXbGMBVTUEM2qY,1175
|
|
51
57
|
yee88/schemas/pi.py,sha256=e5ETawxk8jrdJbEbeBI_pWQKeCFiBBZLEF-Wo5Fx0XY,2680
|
|
58
|
+
yee88/skills/yee88/SKILL.md,sha256=Bba3kuF2jrj07EVs7oI9D1TXvxkMKzwDRcOHp5sv1Zk,13320
|
|
52
59
|
yee88/telegram/__init__.py,sha256=hX_Wvu920dNLTDrKlj0fsZFSewOo-_otN26O1UNPNA4,452
|
|
53
60
|
yee88/telegram/api_models.py,sha256=d3H4o2sRZuFgfZfxF1Y1z_xzzCoOy3bKhMeKggYCW3w,538
|
|
54
61
|
yee88/telegram/api_schemas.py,sha256=Xj-i68GFSb9Uk6GhU2Etp-gYhAat9DKYcvQN9av1NPQ,3823
|
|
@@ -58,12 +65,12 @@ yee88/telegram/chat_prefs.py,sha256=tH7dXIehYHdemN_JOMhB7VuuFOiSBcjKz3FXpfW77Dk,
|
|
|
58
65
|
yee88/telegram/chat_sessions.py,sha256=7uL5bGX5BaQbQYTEpkWZVMh6HIImWdR_pPbqf6C4k0A,3736
|
|
59
66
|
yee88/telegram/client.py,sha256=GqNSCGA0cICaOC3E-qX7E4pcUTeP8edXS9HILYZNZgU,12800
|
|
60
67
|
yee88/telegram/client_api.py,sha256=6gDCrPQSBHHBhyiXX9cCwvNuQChsFXh0z8BvCgGD8Es,17692
|
|
61
|
-
yee88/telegram/context.py,sha256=
|
|
68
|
+
yee88/telegram/context.py,sha256=Hb8-k-YbAjO0EmZ35hA8yyMvl1kozgYHUL1L-lbCglA,4681
|
|
62
69
|
yee88/telegram/engine_defaults.py,sha256=n6ROkTmP_s-H5AhPz_OdT62oZf0QtZJyFEDjp5gfub4,2594
|
|
63
70
|
yee88/telegram/engine_overrides.py,sha256=kv2j102VP-Bqzbutd5ApBkjW3LmVwvCYixsFewVXVeY,3122
|
|
64
71
|
yee88/telegram/files.py,sha256=Cvmw6r_ocSb3jLzJLGVbzr46m8cRU159majJ1-A5lvg,5053
|
|
65
|
-
yee88/telegram/loop.py,sha256=
|
|
66
|
-
yee88/telegram/onboarding.py,sha256=
|
|
72
|
+
yee88/telegram/loop.py,sha256=TpsDgEQMv6bJFCwd72lIxzhCe8V8g3p7P_q7_XMI_WM,69363
|
|
73
|
+
yee88/telegram/onboarding.py,sha256=QWYaJT_s2bujDxzKjsZuLytyxs_XFDRuiBrsZGRjoOw,35633
|
|
67
74
|
yee88/telegram/outbox.py,sha256=OcoRyQ7zmQCXR8ZXEMK2f_7-UMRVRAbBgmJGS1u_lcU,5939
|
|
68
75
|
yee88/telegram/parsing.py,sha256=5PvIPns1NnKryt3XNxPCp4BpWX1gI8kjKi4VxcQ0W-Q,7429
|
|
69
76
|
yee88/telegram/render.py,sha256=tZuKUeiG6qBb7S_6Y90MBSuSFRUHVbcL-mXWC1r9fHA,6017
|
|
@@ -73,22 +80,22 @@ yee88/telegram/topics.py,sha256=TP9cFkWvbZXaJ_41Gmwv2vO6vmim1c_RkrtMkQ9RiQE,8129
|
|
|
73
80
|
yee88/telegram/trigger_mode.py,sha256=4wFjNbEANn45lpctO9uBhjXTGh1K3LOUKWlBEA6m6Ps,2070
|
|
74
81
|
yee88/telegram/types.py,sha256=7MQCR0_k1zDtF32h3VoRxPZM7IZe9K6nCcy0xW11Lus,1540
|
|
75
82
|
yee88/telegram/voice.py,sha256=6GsqgVXSCUCXupyS6S235BXXwTp4gSLmYQLBYWAGdfc,3312
|
|
76
|
-
yee88/telegram/commands/__init__.py,sha256=
|
|
83
|
+
yee88/telegram/commands/__init__.py,sha256=XkwjElDI1ur4wjKsi3rE-sA6M4EJ-fiMCJw0tX_pawI,369
|
|
77
84
|
yee88/telegram/commands/agent.py,sha256=Dtd8N0f3JyJM3gTb1CxQ8-0ez_ZR4DtrnrO9SpKBBQU,6969
|
|
78
85
|
yee88/telegram/commands/cancel.py,sha256=jE93VjztNETlmAgb7FJX0sLR3O-NHy5XcraUbK13TLs,3884
|
|
79
86
|
yee88/telegram/commands/dispatch.py,sha256=zcvX0V6_klf5jXqJlBwAK25e83WC9z3-KoRcDbeWre0,3572
|
|
80
|
-
yee88/telegram/commands/executor.py,sha256=
|
|
87
|
+
yee88/telegram/commands/executor.py,sha256=eczs7Ds_S8GVX2_OvdmN23y6zE1fhYTpIAuchfH0eRU,14781
|
|
81
88
|
yee88/telegram/commands/file_transfer.py,sha256=yfopf_If9f7Scaz4dlUsfcrVvg24QmQdajLzemaENy0,17697
|
|
82
89
|
yee88/telegram/commands/handlers.py,sha256=2zySjRW49e1iv2QJW6xq2m9j0t-uz9_FCJZpE7NhIDA,1834
|
|
83
90
|
yee88/telegram/commands/media.py,sha256=drSKTf_BbyvXOGhS9UKwey_243Ic5XissoaxCykpd-c,5045
|
|
84
91
|
yee88/telegram/commands/menu.py,sha256=AvEgKQUZageBx7TwV2-V_IGhT7AC0qUTtAHjcLIweNY,4466
|
|
85
|
-
yee88/telegram/commands/model.py,sha256=
|
|
92
|
+
yee88/telegram/commands/model.py,sha256=S8m-UmwiPsaPjfsCmw8EXfymYZ9Wh0QvN5na6QIYy5w,11177
|
|
86
93
|
yee88/telegram/commands/overrides.py,sha256=lLlIuCWkKwbS5WlQOOr_Ftv_EvfHR9DhjBpWaf6FBng,4853
|
|
87
94
|
yee88/telegram/commands/parse.py,sha256=0QVW1TVdBWadLbpJ9lRY3s7W4Cm62JJa9jfAaFHQmXU,887
|
|
88
95
|
yee88/telegram/commands/plan.py,sha256=iKsaRBS-qIfvAaxik5ZEA_VzAnFwx7aEED8sKXNq1wE,487
|
|
89
96
|
yee88/telegram/commands/reasoning.py,sha256=UFEJOHm4d0v2jFh5HC3oQGS41NYKbmJHRTaAmu_LiGo,8188
|
|
90
97
|
yee88/telegram/commands/reply.py,sha256=a3zkNjKzn3qZXEZFXuflX-tdhQKQyhYDqZskMy1nS3o,580
|
|
91
|
-
yee88/telegram/commands/topics.py,sha256=
|
|
98
|
+
yee88/telegram/commands/topics.py,sha256=Vs4BsVGRfsUSlZupOiWp7vpESm_0z-4gHOWuHOEtu7U,11207
|
|
92
99
|
yee88/telegram/commands/trigger.py,sha256=RgB4V7NoFdfDLk83xl3BPTsIIVr5A2zCNstR_5B_mEw,5201
|
|
93
100
|
yee88/utils/__init__.py,sha256=cV9_7v07Y6XkrUju0lHdO1ia0-Q57NqyFVMaFCg--do,34
|
|
94
101
|
yee88/utils/git.py,sha256=SVKcPNl2mUrv-cVHZQ5b8QXWKi33e_Jc4_vfCLwagkg,2413
|
|
@@ -96,8 +103,8 @@ yee88/utils/json_state.py,sha256=cnSvGbB9zj90GdYSyigId1M0nEx54T3A3CqqhkAm9kQ,524
|
|
|
96
103
|
yee88/utils/paths.py,sha256=_Tp-LyFLeyGD0P0agRudLuT1NR_XTIpryxk3OYDJAGQ,1318
|
|
97
104
|
yee88/utils/streams.py,sha256=TQezA-A5VCNksLOtwsJplfr8vm1xPTXoGxvik8G2NPI,1121
|
|
98
105
|
yee88/utils/subprocess.py,sha256=2if6IxTZVSB1kDa8SXw3igj3E-zhKB8P4z5MVe-odzY,2169
|
|
99
|
-
yee88-0.
|
|
100
|
-
yee88-0.
|
|
101
|
-
yee88-0.
|
|
102
|
-
yee88-0.
|
|
103
|
-
yee88-0.
|
|
106
|
+
yee88-0.6.0.dist-info/METADATA,sha256=wRSs8V6d5p6CJ2JUHb5fCtixKarwAX665m4twFrMBuc,4340
|
|
107
|
+
yee88-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
108
|
+
yee88-0.6.0.dist-info/entry_points.txt,sha256=P4MVZ_sZfrHaARVMImNJjoGamP8VDukARWMKfDh20V8,282
|
|
109
|
+
yee88-0.6.0.dist-info/licenses/LICENSE,sha256=poyQ59wnbmL3Ox3TiiephfHvUpLvJl0DwLFFgqBDdHY,1063
|
|
110
|
+
yee88-0.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|