yee88 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.
- takopi/__init__.py +1 -0
- takopi/api.py +116 -0
- takopi/backends.py +25 -0
- takopi/backends_helpers.py +14 -0
- takopi/cli/__init__.py +228 -0
- takopi/cli/config.py +320 -0
- takopi/cli/doctor.py +173 -0
- takopi/cli/init.py +113 -0
- takopi/cli/onboarding_cmd.py +126 -0
- takopi/cli/plugins.py +196 -0
- takopi/cli/run.py +419 -0
- takopi/cli/topic.py +355 -0
- takopi/commands.py +134 -0
- takopi/config.py +142 -0
- takopi/config_migrations.py +124 -0
- takopi/config_watch.py +146 -0
- takopi/context.py +9 -0
- takopi/directives.py +146 -0
- takopi/engines.py +53 -0
- takopi/events.py +170 -0
- takopi/ids.py +17 -0
- takopi/lockfile.py +158 -0
- takopi/logging.py +283 -0
- takopi/markdown.py +298 -0
- takopi/model.py +77 -0
- takopi/plugins.py +312 -0
- takopi/presenter.py +25 -0
- takopi/progress.py +99 -0
- takopi/router.py +113 -0
- takopi/runner.py +712 -0
- takopi/runner_bridge.py +619 -0
- takopi/runners/__init__.py +1 -0
- takopi/runners/claude.py +483 -0
- takopi/runners/codex.py +656 -0
- takopi/runners/mock.py +221 -0
- takopi/runners/opencode.py +505 -0
- takopi/runners/pi.py +523 -0
- takopi/runners/run_options.py +39 -0
- takopi/runners/tool_actions.py +90 -0
- takopi/runtime_loader.py +207 -0
- takopi/scheduler.py +159 -0
- takopi/schemas/__init__.py +1 -0
- takopi/schemas/claude.py +238 -0
- takopi/schemas/codex.py +169 -0
- takopi/schemas/opencode.py +51 -0
- takopi/schemas/pi.py +117 -0
- takopi/settings.py +360 -0
- takopi/telegram/__init__.py +20 -0
- takopi/telegram/api_models.py +37 -0
- takopi/telegram/api_schemas.py +152 -0
- takopi/telegram/backend.py +163 -0
- takopi/telegram/bridge.py +425 -0
- takopi/telegram/chat_prefs.py +242 -0
- takopi/telegram/chat_sessions.py +112 -0
- takopi/telegram/client.py +409 -0
- takopi/telegram/client_api.py +539 -0
- takopi/telegram/commands/__init__.py +12 -0
- takopi/telegram/commands/agent.py +196 -0
- takopi/telegram/commands/cancel.py +116 -0
- takopi/telegram/commands/dispatch.py +111 -0
- takopi/telegram/commands/executor.py +449 -0
- takopi/telegram/commands/file_transfer.py +586 -0
- takopi/telegram/commands/handlers.py +45 -0
- takopi/telegram/commands/media.py +143 -0
- takopi/telegram/commands/menu.py +139 -0
- takopi/telegram/commands/model.py +215 -0
- takopi/telegram/commands/overrides.py +159 -0
- takopi/telegram/commands/parse.py +30 -0
- takopi/telegram/commands/plan.py +16 -0
- takopi/telegram/commands/reasoning.py +234 -0
- takopi/telegram/commands/reply.py +23 -0
- takopi/telegram/commands/topics.py +332 -0
- takopi/telegram/commands/trigger.py +143 -0
- takopi/telegram/context.py +140 -0
- takopi/telegram/engine_defaults.py +86 -0
- takopi/telegram/engine_overrides.py +105 -0
- takopi/telegram/files.py +178 -0
- takopi/telegram/loop.py +1822 -0
- takopi/telegram/onboarding.py +1088 -0
- takopi/telegram/outbox.py +177 -0
- takopi/telegram/parsing.py +239 -0
- takopi/telegram/render.py +198 -0
- takopi/telegram/state_store.py +88 -0
- takopi/telegram/topic_state.py +334 -0
- takopi/telegram/topics.py +256 -0
- takopi/telegram/trigger_mode.py +68 -0
- takopi/telegram/types.py +63 -0
- takopi/telegram/voice.py +110 -0
- takopi/transport.py +53 -0
- takopi/transport_runtime.py +323 -0
- takopi/transports.py +76 -0
- takopi/utils/__init__.py +1 -0
- takopi/utils/git.py +87 -0
- takopi/utils/json_state.py +21 -0
- takopi/utils/paths.py +47 -0
- takopi/utils/streams.py +44 -0
- takopi/utils/subprocess.py +86 -0
- takopi/worktrees.py +135 -0
- yee88-0.1.0.dist-info/METADATA +116 -0
- yee88-0.1.0.dist-info/RECORD +103 -0
- yee88-0.1.0.dist-info/WHEEL +4 -0
- yee88-0.1.0.dist-info/entry_points.txt +11 -0
- yee88-0.1.0.dist-info/licenses/LICENSE +21 -0
takopi/cli/run.py
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from functools import partial
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, cast
|
|
9
|
+
|
|
10
|
+
import anyio
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from .. import __version__
|
|
14
|
+
from ..backends import EngineBackend
|
|
15
|
+
from ..config import ConfigError, load_or_init_config
|
|
16
|
+
from ..engines import get_backend
|
|
17
|
+
from ..ids import RESERVED_CHAT_COMMANDS
|
|
18
|
+
from ..lockfile import LockError, LockHandle, acquire_lock, token_fingerprint
|
|
19
|
+
from ..logging import get_logger, setup_logging
|
|
20
|
+
from ..runtime_loader import build_runtime_spec, resolve_plugins_allowlist
|
|
21
|
+
from ..settings import TakopiSettings, load_settings, load_settings_if_exists
|
|
22
|
+
from ..transports import SetupResult, get_transport
|
|
23
|
+
from .config import _config_path_display, _fail_missing_config
|
|
24
|
+
|
|
25
|
+
logger = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _load_settings_optional() -> tuple[TakopiSettings | None, Path | None]:
|
|
29
|
+
try:
|
|
30
|
+
loaded = load_settings_if_exists()
|
|
31
|
+
except ConfigError:
|
|
32
|
+
return None, None
|
|
33
|
+
if loaded is None:
|
|
34
|
+
return None, None
|
|
35
|
+
return loaded
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _resolve_transport_id(override: str | None) -> str:
|
|
39
|
+
if override is not None:
|
|
40
|
+
value = override.strip()
|
|
41
|
+
if not value:
|
|
42
|
+
raise ConfigError("Invalid `--transport`; expected a non-empty string.")
|
|
43
|
+
return value
|
|
44
|
+
load_or_init_config_fn = cast(
|
|
45
|
+
Callable[[], tuple[dict, Path]],
|
|
46
|
+
_resolve_cli_attr("load_or_init_config") or load_or_init_config,
|
|
47
|
+
)
|
|
48
|
+
try:
|
|
49
|
+
config, _ = load_or_init_config_fn()
|
|
50
|
+
except ConfigError:
|
|
51
|
+
return "telegram"
|
|
52
|
+
raw = config.get("transport")
|
|
53
|
+
if not isinstance(raw, str) or not raw.strip():
|
|
54
|
+
return "telegram"
|
|
55
|
+
return raw.strip()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def acquire_config_lock(config_path: Path, token: str | None) -> LockHandle:
|
|
59
|
+
fingerprint = token_fingerprint(token) if token else None
|
|
60
|
+
acquire_lock_fn = cast(
|
|
61
|
+
Callable[..., LockHandle],
|
|
62
|
+
_resolve_cli_attr("acquire_lock") or acquire_lock,
|
|
63
|
+
)
|
|
64
|
+
try:
|
|
65
|
+
return acquire_lock_fn(
|
|
66
|
+
config_path=config_path,
|
|
67
|
+
token_fingerprint=fingerprint,
|
|
68
|
+
)
|
|
69
|
+
except LockError as exc:
|
|
70
|
+
lines = str(exc).splitlines()
|
|
71
|
+
if lines:
|
|
72
|
+
typer.echo(lines[0], err=True)
|
|
73
|
+
if len(lines) > 1:
|
|
74
|
+
typer.echo("\n".join(lines[1:]), err=True)
|
|
75
|
+
else:
|
|
76
|
+
typer.echo("error: unknown error", err=True)
|
|
77
|
+
raise typer.Exit(code=1) from exc
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _default_engine_for_setup(
|
|
81
|
+
override: str | None,
|
|
82
|
+
*,
|
|
83
|
+
settings: TakopiSettings | None,
|
|
84
|
+
config_path: Path | None,
|
|
85
|
+
) -> str:
|
|
86
|
+
if override:
|
|
87
|
+
return override
|
|
88
|
+
if settings is None or config_path is None:
|
|
89
|
+
return "codex"
|
|
90
|
+
value = settings.default_engine
|
|
91
|
+
return value
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _resolve_setup_engine(
|
|
95
|
+
default_engine_override: str | None,
|
|
96
|
+
) -> tuple[
|
|
97
|
+
TakopiSettings | None,
|
|
98
|
+
Path | None,
|
|
99
|
+
list[str] | None,
|
|
100
|
+
str,
|
|
101
|
+
EngineBackend,
|
|
102
|
+
]:
|
|
103
|
+
load_settings_optional_fn = cast(
|
|
104
|
+
Callable[[], tuple[TakopiSettings | None, Path | None]],
|
|
105
|
+
_resolve_cli_attr("_load_settings_optional") or _load_settings_optional,
|
|
106
|
+
)
|
|
107
|
+
resolve_plugins_allowlist_fn = cast(
|
|
108
|
+
Callable[[TakopiSettings | None], list[str] | None],
|
|
109
|
+
_resolve_cli_attr("resolve_plugins_allowlist") or resolve_plugins_allowlist,
|
|
110
|
+
)
|
|
111
|
+
default_engine_for_setup_fn = cast(
|
|
112
|
+
Callable[..., str],
|
|
113
|
+
_resolve_cli_attr("_default_engine_for_setup") or _default_engine_for_setup,
|
|
114
|
+
)
|
|
115
|
+
get_backend_fn = cast(
|
|
116
|
+
Callable[..., EngineBackend],
|
|
117
|
+
_resolve_cli_attr("get_backend") or get_backend,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
settings_hint, config_hint = load_settings_optional_fn()
|
|
121
|
+
allowlist = resolve_plugins_allowlist_fn(settings_hint)
|
|
122
|
+
default_engine = default_engine_for_setup_fn(
|
|
123
|
+
default_engine_override,
|
|
124
|
+
settings=settings_hint,
|
|
125
|
+
config_path=config_hint,
|
|
126
|
+
)
|
|
127
|
+
engine_backend = get_backend_fn(default_engine, allowlist=allowlist)
|
|
128
|
+
return settings_hint, config_hint, allowlist, default_engine, engine_backend
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _should_run_interactive() -> bool:
|
|
132
|
+
if os.environ.get("TAKOPI_NO_INTERACTIVE"):
|
|
133
|
+
return False
|
|
134
|
+
return sys.stdin.isatty() and sys.stdout.isatty()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _setup_needs_config(setup: SetupResult) -> bool:
|
|
138
|
+
config_titles = {"create a config", "configure telegram"}
|
|
139
|
+
return any(issue.title in config_titles for issue in setup.issues)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _run_auto_router(
|
|
143
|
+
*,
|
|
144
|
+
default_engine_override: str | None,
|
|
145
|
+
transport_override: str | None,
|
|
146
|
+
final_notify: bool,
|
|
147
|
+
debug: bool,
|
|
148
|
+
onboard: bool,
|
|
149
|
+
) -> None:
|
|
150
|
+
setup_logging_fn = cast(
|
|
151
|
+
Callable[..., None],
|
|
152
|
+
_resolve_cli_attr("setup_logging") or setup_logging,
|
|
153
|
+
)
|
|
154
|
+
resolve_setup_engine_fn = cast(
|
|
155
|
+
Callable[
|
|
156
|
+
[str | None],
|
|
157
|
+
tuple[
|
|
158
|
+
TakopiSettings | None,
|
|
159
|
+
Path | None,
|
|
160
|
+
list[str] | None,
|
|
161
|
+
str,
|
|
162
|
+
EngineBackend,
|
|
163
|
+
],
|
|
164
|
+
],
|
|
165
|
+
_resolve_cli_attr("_resolve_setup_engine") or _resolve_setup_engine,
|
|
166
|
+
)
|
|
167
|
+
resolve_transport_id_fn = cast(
|
|
168
|
+
Callable[[str | None], str],
|
|
169
|
+
_resolve_cli_attr("_resolve_transport_id") or _resolve_transport_id,
|
|
170
|
+
)
|
|
171
|
+
get_transport_fn = cast(
|
|
172
|
+
Callable[..., Any],
|
|
173
|
+
_resolve_cli_attr("get_transport") or get_transport,
|
|
174
|
+
)
|
|
175
|
+
should_run_interactive_fn = cast(
|
|
176
|
+
Callable[[], bool],
|
|
177
|
+
_resolve_cli_attr("_should_run_interactive") or _should_run_interactive,
|
|
178
|
+
)
|
|
179
|
+
setup_needs_config_fn = cast(
|
|
180
|
+
Callable[[SetupResult], bool],
|
|
181
|
+
_resolve_cli_attr("_setup_needs_config") or _setup_needs_config,
|
|
182
|
+
)
|
|
183
|
+
config_path_display_fn = cast(
|
|
184
|
+
Callable[[Path], str],
|
|
185
|
+
_resolve_cli_attr("_config_path_display") or _config_path_display,
|
|
186
|
+
)
|
|
187
|
+
fail_missing_config_fn = cast(
|
|
188
|
+
Callable[[Path], None],
|
|
189
|
+
_resolve_cli_attr("_fail_missing_config") or _fail_missing_config,
|
|
190
|
+
)
|
|
191
|
+
load_settings_fn = cast(
|
|
192
|
+
Callable[[], tuple[TakopiSettings, Path]],
|
|
193
|
+
_resolve_cli_attr("load_settings") or load_settings,
|
|
194
|
+
)
|
|
195
|
+
build_runtime_spec_fn = cast(
|
|
196
|
+
Callable[..., Any],
|
|
197
|
+
_resolve_cli_attr("build_runtime_spec") or build_runtime_spec,
|
|
198
|
+
)
|
|
199
|
+
acquire_config_lock_fn = cast(
|
|
200
|
+
Callable[[Path, str | None], LockHandle],
|
|
201
|
+
_resolve_cli_attr("acquire_config_lock") or acquire_config_lock,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
if debug:
|
|
205
|
+
os.environ.setdefault("TAKOPI_LOG_FILE", "debug.log")
|
|
206
|
+
setup_logging_fn(debug=debug)
|
|
207
|
+
lock_handle: LockHandle | None = None
|
|
208
|
+
try:
|
|
209
|
+
(
|
|
210
|
+
settings_hint,
|
|
211
|
+
config_hint,
|
|
212
|
+
allowlist,
|
|
213
|
+
default_engine,
|
|
214
|
+
engine_backend,
|
|
215
|
+
) = resolve_setup_engine_fn(default_engine_override)
|
|
216
|
+
transport_id = resolve_transport_id_fn(transport_override)
|
|
217
|
+
transport_backend = get_transport_fn(transport_id, allowlist=allowlist)
|
|
218
|
+
except ConfigError as exc:
|
|
219
|
+
typer.echo(f"error: {exc}", err=True)
|
|
220
|
+
raise typer.Exit(code=1) from exc
|
|
221
|
+
if onboard:
|
|
222
|
+
if not should_run_interactive_fn():
|
|
223
|
+
typer.echo("error: --onboard requires a TTY", err=True)
|
|
224
|
+
raise typer.Exit(code=1)
|
|
225
|
+
if not anyio.run(partial(transport_backend.interactive_setup, force=True)):
|
|
226
|
+
raise typer.Exit(code=1)
|
|
227
|
+
(
|
|
228
|
+
settings_hint,
|
|
229
|
+
config_hint,
|
|
230
|
+
allowlist,
|
|
231
|
+
default_engine,
|
|
232
|
+
engine_backend,
|
|
233
|
+
) = resolve_setup_engine_fn(default_engine_override)
|
|
234
|
+
setup = transport_backend.check_setup(
|
|
235
|
+
engine_backend,
|
|
236
|
+
transport_override=transport_override,
|
|
237
|
+
)
|
|
238
|
+
if not setup.ok:
|
|
239
|
+
if setup_needs_config_fn(setup) and should_run_interactive_fn():
|
|
240
|
+
if setup.config_path.exists():
|
|
241
|
+
display = config_path_display_fn(setup.config_path)
|
|
242
|
+
run_onboard = typer.confirm(
|
|
243
|
+
f"config at {display} is missing/invalid for "
|
|
244
|
+
f"{transport_backend.id}, run onboarding now?",
|
|
245
|
+
default=False,
|
|
246
|
+
)
|
|
247
|
+
if run_onboard and anyio.run(
|
|
248
|
+
partial(transport_backend.interactive_setup, force=True)
|
|
249
|
+
):
|
|
250
|
+
(
|
|
251
|
+
settings_hint,
|
|
252
|
+
config_hint,
|
|
253
|
+
allowlist,
|
|
254
|
+
default_engine,
|
|
255
|
+
engine_backend,
|
|
256
|
+
) = resolve_setup_engine_fn(default_engine_override)
|
|
257
|
+
setup = transport_backend.check_setup(
|
|
258
|
+
engine_backend,
|
|
259
|
+
transport_override=transport_override,
|
|
260
|
+
)
|
|
261
|
+
elif anyio.run(partial(transport_backend.interactive_setup, force=False)):
|
|
262
|
+
(
|
|
263
|
+
settings_hint,
|
|
264
|
+
config_hint,
|
|
265
|
+
allowlist,
|
|
266
|
+
default_engine,
|
|
267
|
+
engine_backend,
|
|
268
|
+
) = resolve_setup_engine_fn(default_engine_override)
|
|
269
|
+
setup = transport_backend.check_setup(
|
|
270
|
+
engine_backend,
|
|
271
|
+
transport_override=transport_override,
|
|
272
|
+
)
|
|
273
|
+
if not setup.ok:
|
|
274
|
+
if setup_needs_config_fn(setup):
|
|
275
|
+
fail_missing_config_fn(setup.config_path)
|
|
276
|
+
else:
|
|
277
|
+
first = setup.issues[0]
|
|
278
|
+
typer.echo(f"error: {first.title}", err=True)
|
|
279
|
+
raise typer.Exit(code=1)
|
|
280
|
+
try:
|
|
281
|
+
settings, config_path = load_settings_fn()
|
|
282
|
+
if transport_override and transport_override != settings.transport:
|
|
283
|
+
settings = settings.model_copy(update={"transport": transport_override})
|
|
284
|
+
spec = build_runtime_spec_fn(
|
|
285
|
+
settings=settings,
|
|
286
|
+
config_path=config_path,
|
|
287
|
+
default_engine_override=default_engine_override,
|
|
288
|
+
reserved=RESERVED_CHAT_COMMANDS,
|
|
289
|
+
)
|
|
290
|
+
if settings.transport == "telegram":
|
|
291
|
+
transport_config = settings.transports.telegram
|
|
292
|
+
else:
|
|
293
|
+
transport_config = settings.transport_config(
|
|
294
|
+
settings.transport, config_path=config_path
|
|
295
|
+
)
|
|
296
|
+
lock_token = transport_backend.lock_token(
|
|
297
|
+
transport_config=transport_config,
|
|
298
|
+
_config_path=config_path,
|
|
299
|
+
)
|
|
300
|
+
lock_handle = acquire_config_lock_fn(config_path, lock_token)
|
|
301
|
+
runtime = spec.to_runtime(config_path=config_path)
|
|
302
|
+
transport_backend.build_and_run(
|
|
303
|
+
final_notify=final_notify,
|
|
304
|
+
default_engine_override=default_engine_override,
|
|
305
|
+
config_path=config_path,
|
|
306
|
+
transport_config=transport_config,
|
|
307
|
+
runtime=runtime,
|
|
308
|
+
)
|
|
309
|
+
except ConfigError as exc:
|
|
310
|
+
typer.echo(f"error: {exc}", err=True)
|
|
311
|
+
raise typer.Exit(code=1) from exc
|
|
312
|
+
except KeyboardInterrupt:
|
|
313
|
+
logger.info("shutdown.interrupted")
|
|
314
|
+
raise typer.Exit(code=130) from None
|
|
315
|
+
finally:
|
|
316
|
+
if lock_handle is not None:
|
|
317
|
+
lock_handle.release()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _print_version_and_exit() -> None:
|
|
321
|
+
typer.echo(__version__)
|
|
322
|
+
raise typer.Exit()
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _version_callback(value: bool) -> None:
|
|
326
|
+
if value:
|
|
327
|
+
_print_version_and_exit()
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def app_main(
|
|
331
|
+
ctx: typer.Context,
|
|
332
|
+
version: bool = typer.Option(
|
|
333
|
+
False,
|
|
334
|
+
"--version",
|
|
335
|
+
help="Show the version and exit.",
|
|
336
|
+
callback=_version_callback,
|
|
337
|
+
is_eager=True,
|
|
338
|
+
),
|
|
339
|
+
final_notify: bool = typer.Option(
|
|
340
|
+
True,
|
|
341
|
+
"--final-notify/--no-final-notify",
|
|
342
|
+
help="Send the final response as a new message (not an edit).",
|
|
343
|
+
),
|
|
344
|
+
onboard: bool = typer.Option(
|
|
345
|
+
False,
|
|
346
|
+
"--onboard/--no-onboard",
|
|
347
|
+
help="Run the interactive setup wizard before starting.",
|
|
348
|
+
),
|
|
349
|
+
transport: str | None = typer.Option(
|
|
350
|
+
None,
|
|
351
|
+
"--transport",
|
|
352
|
+
help="Override the transport backend id.",
|
|
353
|
+
),
|
|
354
|
+
debug: bool = typer.Option(
|
|
355
|
+
False,
|
|
356
|
+
"--debug/--no-debug",
|
|
357
|
+
help="Log engine JSONL, Telegram requests, and rendered messages.",
|
|
358
|
+
),
|
|
359
|
+
) -> None:
|
|
360
|
+
"""Takopi CLI."""
|
|
361
|
+
if ctx.invoked_subcommand is None:
|
|
362
|
+
run_auto_router = cast(
|
|
363
|
+
Callable[..., None],
|
|
364
|
+
_resolve_cli_attr("_run_auto_router") or _run_auto_router,
|
|
365
|
+
)
|
|
366
|
+
run_auto_router(
|
|
367
|
+
default_engine_override=None,
|
|
368
|
+
transport_override=transport,
|
|
369
|
+
final_notify=final_notify,
|
|
370
|
+
debug=debug,
|
|
371
|
+
onboard=onboard,
|
|
372
|
+
)
|
|
373
|
+
raise typer.Exit()
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def make_engine_cmd(engine_id: str) -> Callable[..., None]:
|
|
377
|
+
def _cmd(
|
|
378
|
+
final_notify: bool = typer.Option(
|
|
379
|
+
True,
|
|
380
|
+
"--final-notify/--no-final-notify",
|
|
381
|
+
help="Send the final response as a new message (not an edit).",
|
|
382
|
+
),
|
|
383
|
+
onboard: bool = typer.Option(
|
|
384
|
+
False,
|
|
385
|
+
"--onboard/--no-onboard",
|
|
386
|
+
help="Run the interactive setup wizard before starting.",
|
|
387
|
+
),
|
|
388
|
+
transport: str | None = typer.Option(
|
|
389
|
+
None,
|
|
390
|
+
"--transport",
|
|
391
|
+
help="Override the transport backend id.",
|
|
392
|
+
),
|
|
393
|
+
debug: bool = typer.Option(
|
|
394
|
+
False,
|
|
395
|
+
"--debug/--no-debug",
|
|
396
|
+
help="Log engine JSONL, Telegram requests, and rendered messages.",
|
|
397
|
+
),
|
|
398
|
+
) -> None:
|
|
399
|
+
run_auto_router = cast(
|
|
400
|
+
Callable[..., None],
|
|
401
|
+
_resolve_cli_attr("_run_auto_router") or _run_auto_router,
|
|
402
|
+
)
|
|
403
|
+
run_auto_router(
|
|
404
|
+
default_engine_override=engine_id,
|
|
405
|
+
transport_override=transport,
|
|
406
|
+
final_notify=final_notify,
|
|
407
|
+
debug=debug,
|
|
408
|
+
onboard=onboard,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
_cmd.__name__ = f"run_{engine_id}"
|
|
412
|
+
return _cmd
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _resolve_cli_attr(name: str) -> object | None:
|
|
416
|
+
cli_module = sys.modules.get("takopi.cli")
|
|
417
|
+
if cli_module is None:
|
|
418
|
+
return None
|
|
419
|
+
return getattr(cli_module, name, None)
|