coderouter-cli 2.9.3__py3-none-any.whl → 2.10.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.
- coderouter/cli.py +125 -0
- coderouter/config/schemas.py +51 -1
- coderouter/ingress/launcher_routes.py +796 -3
- coderouter/launcher_devices.py +518 -0
- coderouter/translation/anthropic.py +26 -5
- coderouter/vscode_init.py +573 -0
- {coderouter_cli-2.9.3.dist-info → coderouter_cli-2.10.0.dist-info}/METADATA +5 -2
- {coderouter_cli-2.9.3.dist-info → coderouter_cli-2.10.0.dist-info}/RECORD +11 -9
- {coderouter_cli-2.9.3.dist-info → coderouter_cli-2.10.0.dist-info}/WHEEL +0 -0
- {coderouter_cli-2.9.3.dist-info → coderouter_cli-2.10.0.dist-info}/entry_points.txt +0 -0
- {coderouter_cli-2.9.3.dist-info → coderouter_cli-2.10.0.dist-info}/licenses/LICENSE +0 -0
coderouter/cli.py
CHANGED
|
@@ -336,6 +336,78 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
336
336
|
),
|
|
337
337
|
)
|
|
338
338
|
|
|
339
|
+
# v2.10-A: `coderouter vscode-init` — scaffold a VSCode workspace so
|
|
340
|
+
# Claude Code launched from the integrated terminal auto-points at
|
|
341
|
+
# CodeRouter (no manual env-var juggling). Optionally writes .envrc
|
|
342
|
+
# for direnv users. Cline / Roo / Continue are covered by the cheat
|
|
343
|
+
# sheet printed on completion and docs/guides/vscode.md — this
|
|
344
|
+
# command deliberately does NOT touch those extensions' settings
|
|
345
|
+
# (their schemas change with their own release cadence).
|
|
346
|
+
vscode_init = sub.add_parser(
|
|
347
|
+
"vscode-init",
|
|
348
|
+
help="Scaffold VSCode workspace settings for CodeRouter (v2.10-A).",
|
|
349
|
+
description=(
|
|
350
|
+
"Write .vscode/settings.json (terminal.integrated.env.*) so a "
|
|
351
|
+
"Claude Code session launched from VSCode's integrated terminal "
|
|
352
|
+
"auto-points at CodeRouter. Optionally emit a direnv .envrc. "
|
|
353
|
+
"Idempotent: safe to re-run. Conflict-aware: refuses to overwrite "
|
|
354
|
+
"differing existing values without --force."
|
|
355
|
+
),
|
|
356
|
+
)
|
|
357
|
+
vscode_init.add_argument(
|
|
358
|
+
"--target",
|
|
359
|
+
default=".",
|
|
360
|
+
metavar="PATH",
|
|
361
|
+
help="Workspace root (default: current directory).",
|
|
362
|
+
)
|
|
363
|
+
vscode_init.add_argument(
|
|
364
|
+
"--port",
|
|
365
|
+
type=int,
|
|
366
|
+
default=None,
|
|
367
|
+
metavar="PORT",
|
|
368
|
+
help=(
|
|
369
|
+
"CodeRouter port for ANTHROPIC_BASE_URL. Defaults to 8088 "
|
|
370
|
+
"(matches every docs / quickstart example). Note that "
|
|
371
|
+
"`coderouter serve` alone defaults to 4000; if you serve on "
|
|
372
|
+
"4000, pass --port 4000 here too."
|
|
373
|
+
),
|
|
374
|
+
)
|
|
375
|
+
vscode_init.add_argument(
|
|
376
|
+
"--profile",
|
|
377
|
+
default=None,
|
|
378
|
+
metavar="NAME",
|
|
379
|
+
help=(
|
|
380
|
+
"Preload CODEROUTER_MODE=<profile> so the VSCode terminal "
|
|
381
|
+
"routes to a non-default profile without a per-request header."
|
|
382
|
+
),
|
|
383
|
+
)
|
|
384
|
+
vscode_init.add_argument(
|
|
385
|
+
"--with-envrc",
|
|
386
|
+
action="store_true",
|
|
387
|
+
help=(
|
|
388
|
+
"Also write .envrc (direnv). Run `direnv allow` once after "
|
|
389
|
+
"generation. Tip: keep secrets in a separate .envrc.local."
|
|
390
|
+
),
|
|
391
|
+
)
|
|
392
|
+
vscode_init.add_argument(
|
|
393
|
+
"--dry-run",
|
|
394
|
+
action="store_true",
|
|
395
|
+
help=(
|
|
396
|
+
"Compute all changes and print unified diffs, but do not write "
|
|
397
|
+
"any files. Byte-identical to the write path minus the final "
|
|
398
|
+
"os.replace."
|
|
399
|
+
),
|
|
400
|
+
)
|
|
401
|
+
vscode_init.add_argument(
|
|
402
|
+
"--force",
|
|
403
|
+
action="store_true",
|
|
404
|
+
help=(
|
|
405
|
+
"Overwrite existing conflicting values. Without this flag, a "
|
|
406
|
+
"conflict is reported with a diff and the file is left "
|
|
407
|
+
"untouched (exit 2)."
|
|
408
|
+
),
|
|
409
|
+
)
|
|
410
|
+
|
|
339
411
|
return parser
|
|
340
412
|
|
|
341
413
|
|
|
@@ -432,6 +504,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
432
504
|
if args.command == "replay":
|
|
433
505
|
return _run_replay(args)
|
|
434
506
|
|
|
507
|
+
if args.command == "vscode-init":
|
|
508
|
+
return _run_vscode_init(args)
|
|
509
|
+
|
|
435
510
|
print(f"unknown command: {args.command}", file=sys.stderr)
|
|
436
511
|
return 2
|
|
437
512
|
|
|
@@ -765,6 +840,56 @@ def _run_replay(args: argparse.Namespace) -> int:
|
|
|
765
840
|
return 0
|
|
766
841
|
|
|
767
842
|
|
|
843
|
+
def _run_vscode_init(args: argparse.Namespace) -> int:
|
|
844
|
+
"""v2.10-A: drive :func:`coderouter.vscode_init.run_vscode_init`.
|
|
845
|
+
|
|
846
|
+
Kept small and testable — the actual scaffolding logic lives in
|
|
847
|
+
:mod:`coderouter.vscode_init` so tests can exercise it without
|
|
848
|
+
argparse in the way. This wrapper's job is:
|
|
849
|
+
|
|
850
|
+
* resolve the ``--target`` argument to a real directory
|
|
851
|
+
* map ``--port`` (default None) to the module's ``DEFAULT_PORT``
|
|
852
|
+
* translate exceptions (missing target directory) to a friendly
|
|
853
|
+
stderr message + exit 1
|
|
854
|
+
* print the formatted result and propagate the module's exit code
|
|
855
|
+
"""
|
|
856
|
+
from coderouter.vscode_init import (
|
|
857
|
+
DEFAULT_PORT,
|
|
858
|
+
exit_code_for,
|
|
859
|
+
format_result,
|
|
860
|
+
run_vscode_init,
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
target = Path(args.target).expanduser()
|
|
864
|
+
if not target.is_dir():
|
|
865
|
+
print(
|
|
866
|
+
f"vscode-init: target directory does not exist: {target}",
|
|
867
|
+
file=sys.stderr,
|
|
868
|
+
)
|
|
869
|
+
return 1
|
|
870
|
+
|
|
871
|
+
port = args.port if args.port is not None else DEFAULT_PORT
|
|
872
|
+
|
|
873
|
+
try:
|
|
874
|
+
result = run_vscode_init(
|
|
875
|
+
target,
|
|
876
|
+
port=port,
|
|
877
|
+
profile=args.profile,
|
|
878
|
+
with_envrc=args.with_envrc,
|
|
879
|
+
dry_run=args.dry_run,
|
|
880
|
+
force=args.force,
|
|
881
|
+
)
|
|
882
|
+
except FileNotFoundError as exc:
|
|
883
|
+
# Raised only when target vanishes between the pre-check above
|
|
884
|
+
# and the module's own check — unlikely in practice, but map
|
|
885
|
+
# it to the same shape as the pre-check for consistency.
|
|
886
|
+
print(f"vscode-init: {exc}", file=sys.stderr)
|
|
887
|
+
return 1
|
|
888
|
+
|
|
889
|
+
print(format_result(result, dry_run=args.dry_run, port=port))
|
|
890
|
+
return exit_code_for(result)
|
|
891
|
+
|
|
892
|
+
|
|
768
893
|
def _run_check_env(arg_value: str) -> int:
|
|
769
894
|
"""v1.6.3: filesystem / git security checks for `.env`.
|
|
770
895
|
|
coderouter/config/schemas.py
CHANGED
|
@@ -394,7 +394,7 @@ class ProviderConfig(BaseModel):
|
|
|
394
394
|
default=False,
|
|
395
395
|
description="If true, only used when ALLOW_PAID=true (plan.md §2.3).",
|
|
396
396
|
)
|
|
397
|
-
timeout_s: float = Field(default=30.0, ge=1.0, le=
|
|
397
|
+
timeout_s: float = Field(default=30.0, ge=1.0, le=86400.0)
|
|
398
398
|
|
|
399
399
|
# v2.6 language-tax track: path to a LOCAL ``tokenizer.json`` for this
|
|
400
400
|
# provider's model, used to measure the CJK over-count vs the char/4
|
|
@@ -1460,6 +1460,49 @@ class LauncherOptionProfile(BaseModel):
|
|
|
1460
1460
|
)
|
|
1461
1461
|
|
|
1462
1462
|
|
|
1463
|
+
class LauncherBenchConfig(BaseModel):
|
|
1464
|
+
"""The ``launcher.bench:`` block — defaults for the bench sweep feature.
|
|
1465
|
+
|
|
1466
|
+
設計 §4.1。デバイス構成スイープ(起動→readiness→外部ベンチ→停止→次)の
|
|
1467
|
+
既定値を providers.yaml から供給する。未設定(``LauncherConfig.bench`` が
|
|
1468
|
+
None)ならスイープ UI はハードコード既定を使う(完全後方互換)。すべての
|
|
1469
|
+
フィールドに ``default`` があるので、既存 YAML は無改変で通過する。
|
|
1470
|
+
"""
|
|
1471
|
+
|
|
1472
|
+
model_config = ConfigDict(extra="forbid")
|
|
1473
|
+
|
|
1474
|
+
command_template: str = Field(
|
|
1475
|
+
default="llmbench run --model local-openai --runs {runs}",
|
|
1476
|
+
description=(
|
|
1477
|
+
"外部ベンチコマンドのテンプレ。``{port}`` ``{config}`` "
|
|
1478
|
+
"``{base_url}`` ``{results_dir}`` ``{runs}`` を単純置換で展開して "
|
|
1479
|
+
"argv 化する(``str.format`` ではないので JSON 波括弧で誤爆しない)。"
|
|
1480
|
+
),
|
|
1481
|
+
)
|
|
1482
|
+
runs: int = Field(
|
|
1483
|
+
default=5,
|
|
1484
|
+
ge=1,
|
|
1485
|
+
le=1000,
|
|
1486
|
+
description="1 構成あたりのベンチ実行回数。``{runs}`` に展開される。",
|
|
1487
|
+
)
|
|
1488
|
+
results_dir: str | None = Field(
|
|
1489
|
+
default=None,
|
|
1490
|
+
description=(
|
|
1491
|
+
"llmbench の results/ ディレクトリ。相対はサーバ CWD 基準。"
|
|
1492
|
+
"設定時のみスイープ結果 JSON を読み比較サマリを付与する。"
|
|
1493
|
+
),
|
|
1494
|
+
)
|
|
1495
|
+
readiness_timeout_s: float = Field(
|
|
1496
|
+
default=300.0,
|
|
1497
|
+
ge=5.0,
|
|
1498
|
+
le=3600.0,
|
|
1499
|
+
description=(
|
|
1500
|
+
"スイープの各構成でサーバが ready になるのを待つ最大秒数。"
|
|
1501
|
+
"大きな GGUF ロードを考慮した既定 5 分。"
|
|
1502
|
+
),
|
|
1503
|
+
)
|
|
1504
|
+
|
|
1505
|
+
|
|
1463
1506
|
class SwapModelSpec(BaseModel):
|
|
1464
1507
|
"""One entry in ``launcher.swap.models`` (docs/designs/launcher-model-swap.md).
|
|
1465
1508
|
|
|
@@ -1863,6 +1906,13 @@ class LauncherConfig(BaseModel):
|
|
|
1863
1906
|
"behavior."
|
|
1864
1907
|
),
|
|
1865
1908
|
)
|
|
1909
|
+
bench: LauncherBenchConfig | None = Field(
|
|
1910
|
+
default=None,
|
|
1911
|
+
description=(
|
|
1912
|
+
"デバイス構成ベンチスイープ(設計 §4)の既定値。None(既定)なら "
|
|
1913
|
+
"スイープ UI はハードコード既定を使う=完全後方互換。"
|
|
1914
|
+
),
|
|
1915
|
+
)
|
|
1866
1916
|
|
|
1867
1917
|
@model_validator(mode="after")
|
|
1868
1918
|
def _check_auto_restart_backoff_ordered(self) -> LauncherConfig:
|