coderouter-cli 2.13.0__py3-none-any.whl → 2.15.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/vscode_init.py CHANGED
@@ -24,9 +24,16 @@ The design contract:
24
24
  until a human debugged it.
25
25
  * Nothing an operator wrote is destroyed without a copy: before any
26
26
  existing file is rewritten, its previous bytes (and mode) land in
27
- ``<name>.bak``. ``--force`` on ``.envrc`` is a whole-file replace,
28
- not a merge, so this is the only thing standing between the user and
29
- a lost ``source_env_if_exists .envrc.local`` line.
27
+ ``<name>.bak``, which ``coderouter rollback`` can swap back.
28
+ * Since v2.14.0, ``.envrc`` is edited through a marker-delimited block
29
+ (``# BEGIN coderouter-managed`` … ``# END coderouter-managed``): only
30
+ that block is rewritten and every other line survives byte-for-byte.
31
+ A re-run over an already-fenced file therefore needs no ``--force``;
32
+ ``--force`` now means "adopt a file I did not write". Before v2.14.0
33
+ this was a whole-file replace, which is how the lost
34
+ ``source_env_if_exists .envrc.local`` line (H-11) happened — see the
35
+ "marker-delimited managed block in .envrc" section below for the full
36
+ rationale and the conflict rules.
30
37
  * A freshly generated ``.envrc`` is created 0600 — it carries
31
38
  ``ANTHROPIC_AUTH_TOKEN`` and sits next to the operator's real
32
39
  secrets. An existing file's mode is preserved across rewrites
@@ -395,7 +402,19 @@ def _apply_envrc(
395
402
  force: bool,
396
403
  dry_run: bool,
397
404
  ) -> FileOutcome:
398
- new_text = _render_envrc(base_url=base_url, profile=profile)
405
+ """Write (or refresh) the managed block in ``.envrc``.
406
+
407
+ Three cases, and only the third can refuse:
408
+
409
+ * **No file** — create it with the fenced block.
410
+ * **Fenced file** — replace the block, keep everything outside it
411
+ byte-for-byte. No ``--force`` needed; this is not destructive.
412
+ * **Unfenced file we did not write** — refuse, unless ``--force``,
413
+ which now *appends* the block rather than replacing the file. The
414
+ operator's lines survive either way.
415
+ """
416
+ block = _render_envrc_block(base_url=base_url, profile=profile)
417
+
399
418
  old_text = ""
400
419
  if path.exists():
401
420
  try:
@@ -407,32 +426,76 @@ def _apply_envrc(
407
426
  reason=f"cannot read existing file: {exc}",
408
427
  )
409
428
 
429
+ if not old_text:
430
+ new_text = block + "\n"
431
+ else:
432
+ split = _split_managed_block(old_text)
433
+ if split is None and _looks_like_our_legacy_output(
434
+ old_text, base_url=base_url, profile=profile
435
+ ):
436
+ # Our own pre-v2.14.0 output: adopt it into the fence silently.
437
+ split = ("", "")
438
+ if split is None:
439
+ outside = "" # --force adopts the file wholesale; see below
440
+ if not force:
441
+ would = old_text.rstrip("\n") + "\n\n" + block + "\n"
442
+ return FileOutcome(
443
+ path=path,
444
+ action="conflict",
445
+ diff=_text_diff(old_text, would, path),
446
+ reason=(
447
+ ".envrc exists and carries no "
448
+ f"`{_ENVRC_BEGIN}` fence, so CodeRouter did not write it. "
449
+ "Re-run with --force to APPEND the managed block "
450
+ "(your existing lines are kept — unlike before "
451
+ "v2.14.0, --force no longer replaces the whole file); "
452
+ f"the previous contents are still saved to "
453
+ f"{_backup_path(path).name}."
454
+ ),
455
+ )
456
+ # --force on an unfenced file means "adopt it, mine wins". The
457
+ # block is APPENDED, not substituted for the file: direnv applies
458
+ # exports in order, so a duplicate the operator already had is
459
+ # shadowed rather than deleted, and their other lines survive. The
460
+ # conflict scan is skipped here on purpose — refusing would leave
461
+ # --force with nothing it could actually do.
462
+ new_text = old_text.rstrip("\n") + "\n\n" + block + "\n"
463
+ else:
464
+ before, after = split
465
+ outside = before + after
466
+ new_text = (
467
+ before.rstrip("\n") + ("\n\n" if before.strip() else "")
468
+ ) + block + ("\n\n" + after.lstrip("\n") if after.strip() else "\n")
469
+
470
+ conflicts = _unmanaged_conflicts(outside)
471
+ if conflicts:
472
+ # direnv applies exports in order, so a duplicate outside the
473
+ # fence would silently win or lose depending on placement.
474
+ # Refusing is the only answer that cannot surprise anyone.
475
+ return FileOutcome(
476
+ path=path,
477
+ action="conflict",
478
+ diff=_text_diff(old_text, new_text, path),
479
+ reason=(
480
+ "these variables are already exported outside the managed "
481
+ f"block: {', '.join(conflicts)}. CodeRouter will not write "
482
+ "a second export for a value you own — direnv would apply "
483
+ "whichever comes last. Remove your line, or move it inside "
484
+ f"`{_ENVRC_BEGIN}` … `{_ENVRC_END}`."
485
+ ),
486
+ )
487
+
410
488
  if new_text == old_text:
411
489
  return FileOutcome(path=path, action="unchanged")
412
490
 
413
491
  diff = _text_diff(old_text, new_text, path)
414
492
 
415
- if old_text and not force:
416
- return FileOutcome(
417
- path=path,
418
- action="conflict",
419
- diff=diff,
420
- reason=(
421
- ".envrc already exists and its contents differ. "
422
- "Re-run with --force to replace the WHOLE file with the "
423
- "generated contents (this is not a merge — custom lines "
424
- "such as `source_env_if_exists .envrc.local` are dropped); "
425
- f"the previous contents are saved to {_backup_path(path).name}. "
426
- "(Tip: keep secrets in a separate .envrc.local and "
427
- "``source_env_if_exists`` from .envrc.)"
428
- ),
429
- )
430
-
431
493
  reason = ""
432
494
  if not dry_run:
433
- # H-11: --force replaces the whole file, so stash the old bytes
434
- # (mode included) before they are gone. Inside the dry_run guard
435
- # on purpose — --dry-run must not touch the filesystem at all.
495
+ # H-11: keep the old bytes (mode included) even though the write is
496
+ # now surgical. A bug in the fence logic would take exactly this
497
+ # path. Inside the dry_run guard on purpose — --dry-run must not
498
+ # touch the filesystem at all.
436
499
  if old_text:
437
500
  backup = _make_backup(path)
438
501
  reason = (
@@ -463,6 +526,86 @@ def _apply_envrc(
463
526
  )
464
527
 
465
528
 
529
+ # ---------------------------------------------------------------------------
530
+ # v2.14.0 — marker-delimited managed block in .envrc
531
+ # ---------------------------------------------------------------------------
532
+ #
533
+ # Until now, ``--force`` on ``.envrc`` was a whole-file replace: it wrote the
534
+ # generated contents over whatever was there, dropping any line the operator
535
+ # had added. That is how H-11 happened, and "we back it up first" only helps
536
+ # somebody who knows to look for the backup.
537
+ #
538
+ # The fix is the one codex-router uses on ``~/.codex/config.toml``: fence the
539
+ # part we own between markers, rewrite only that, and leave everything else
540
+ # byte-for-byte. Once the fence exists, a re-run is no longer destructive, so
541
+ # it needs no ``--force`` at all — ``--force`` shrinks back to its honest
542
+ # meaning of "yes, adopt a file I did not write".
543
+
544
+ _ENVRC_BEGIN = "# BEGIN coderouter-managed"
545
+ _ENVRC_END = "# END coderouter-managed"
546
+
547
+ # The variables the managed block owns. A value for one of these sitting
548
+ # OUTSIDE the block is a conflict we refuse rather than silently shadow —
549
+ # direnv applies exports in order, so whichever came last would win and the
550
+ # operator would have no idea which one is live.
551
+ _MANAGED_ENVRC_VARS = ("ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", "CODEROUTER_MODE")
552
+
553
+
554
+ def _split_managed_block(text: str) -> tuple[str, str] | None:
555
+ """Split ``text`` around the managed block.
556
+
557
+ Returns ``(before, after)`` with the block (and its markers) removed,
558
+ or ``None`` when the file carries no fence. A file with a begin marker
559
+ and no end marker also returns ``None`` — a half-written fence is not
560
+ something to guess at, and the caller turns that into a refusal.
561
+ """
562
+ start = text.find(_ENVRC_BEGIN)
563
+ if start == -1:
564
+ return None
565
+ end = text.find(_ENVRC_END, start)
566
+ if end == -1:
567
+ return None
568
+ return text[:start], text[end + len(_ENVRC_END) :].lstrip("\n")
569
+
570
+
571
+ def _render_envrc_block(*, base_url: str, profile: str | None) -> str:
572
+ """The fenced block CodeRouter owns, markers included."""
573
+ lines = [
574
+ _ENVRC_BEGIN,
575
+ "# Managed by `coderouter vscode-init`. Edits inside this block are",
576
+ "# overwritten on the next run; put your own lines outside it.",
577
+ f'export ANTHROPIC_BASE_URL="{base_url}"',
578
+ f'export ANTHROPIC_AUTH_TOKEN="{DEFAULT_TOKEN}"',
579
+ ]
580
+ if profile:
581
+ lines.append(f'export CODEROUTER_MODE="{profile}"')
582
+ lines.append(_ENVRC_END)
583
+ return "\n".join(lines)
584
+
585
+
586
+ def _unmanaged_conflicts(outside_text: str) -> list[str]:
587
+ """Managed variables the operator exports outside our block."""
588
+ found: list[str] = []
589
+ for line in outside_text.splitlines():
590
+ stripped = line.strip()
591
+ if stripped.startswith("#"):
592
+ continue
593
+ for var in _MANAGED_ENVRC_VARS:
594
+ if stripped.startswith((f"export {var}=", f"{var}=")) and var not in found:
595
+ found.append(var)
596
+ return found
597
+
598
+
599
+ def _looks_like_our_legacy_output(text: str, *, base_url: str, profile: str | None) -> bool:
600
+ """True when the file is byte-identical to a pre-v2.14.0 generation.
601
+
602
+ An operator upgrading has an unfenced ``.envrc`` that we wrote — asking
603
+ them to pass ``--force`` to adopt our own previous output would be
604
+ rude, and appending a second block would double the exports.
605
+ """
606
+ return text == _render_envrc(base_url=base_url, profile=profile)
607
+
608
+
466
609
  def _render_envrc(*, base_url: str, profile: str | None) -> str:
467
610
  """Build the .envrc contents. Kept split for test targeting."""
468
611
  lines = [
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: coderouter-cli
3
- Version: 2.13.0
3
+ Version: 2.15.0
4
4
  Summary: Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible.
5
5
  Project-URL: Homepage, https://github.com/zephel01/CodeRouter
6
6
  Project-URL: Repository, https://github.com/zephel01/CodeRouter
@@ -1,10 +1,11 @@
1
1
  coderouter/__init__.py,sha256=ghdjPrLtnRzY8fyQ4CJZI1UJKADyNTLtA3G7se8H7Ns,696
2
2
  coderouter/__main__.py,sha256=-LCgxJnvgUV240HjQKv7ly-mn2NuKHpC4nCpvTHjeSU,130
3
- coderouter/cli.py,sha256=TyI29d8-Zc8SClcKn92Oj8DGyQRxMpBSWTxLiFTH3Qo,37043
3
+ coderouter/cli.py,sha256=Nbx1ur_HnssFtuM48yPELwhY40veVNnTAfK7OVF2uVc,42400
4
4
  coderouter/cli_stats.py,sha256=CCjzc1G4hTRHZ2gG1XhxhDpUkJnnl3NXbcbp1T18jpg,29894
5
5
  coderouter/cost.py,sha256=32h6uzb4nxh2eA5d2Hn3kD9yJbtis6CFDAbeIy5KRkM,7431
6
+ coderouter/credentials.py,sha256=R_PdrcA941_jJX_U8hxrjPDTCVVzLd4dsw2c00KwAwE,12991
6
7
  coderouter/doctor.py,sha256=wmPunlvqm2s7x3VGLjexqHPLD2oejwE3Mu04V8y-xn4,89869
7
- coderouter/doctor_apply.py,sha256=UqRvzRqENHWqsL3OCsuSotNdxCMw5rP2LnCWMBLmZow,28033
8
+ coderouter/doctor_apply.py,sha256=tZT3bEd1G6t67YRhf1TGY4NlSvZuJJbnEqN-3AKy8Hc,29972
8
9
  coderouter/env_security.py,sha256=FEBZnXfJ0xE39kmMMn39zk0W_DRRnmcB_REmP9f4xWo,14796
9
10
  coderouter/errors.py,sha256=Xmq67lheyw8iv3Ox39jh2c4tvNI5RcUR4QkoxVDN6l4,1130
10
11
  coderouter/gguf_introspect.py,sha256=Scu0JjUfLHj7T7TL0HQIm1AZcGYrXsjkaMMSN9HkTro,16121
@@ -13,21 +14,23 @@ coderouter/language_tax.py,sha256=LTbE3tIfoJuV2O3T0NixRKhzq_dEOTUuPEerJv2q9uk,93
13
14
  coderouter/launcher_devices.py,sha256=DAV34Q1crGptmj1zOAk21YQzlxQNAt5Ud7qcldTD95w,29406
14
15
  coderouter/launcher_speculative.py,sha256=h0RTGsD1Y56sJVywDJwybrT75_Ta3GF1Jz_B1d42aPg,12931
15
16
  coderouter/launcher_swap.py,sha256=tMW5ZElDdf9T1VQGsUkvfLiSCdj_oXN_UtkV7DE8ySY,24175
16
- coderouter/logging.py,sha256=5rfi4_R4aoeeUYDc642dgs61wA0aBX7wvubyB-k3Uyo,58074
17
+ coderouter/logging.py,sha256=nQlMFKJ-Q9I-yripOw8QBjMZRRHHK2IAGwBvgTQp6w8,64116
17
18
  coderouter/output_filters.py,sha256=0ry_rPiS_kC-FnHgaNVP6v7e6Al2djxzu9vBzZ8kEkE,25314
19
+ coderouter/rollback.py,sha256=Bqxx1MALTDLYc0BtGMEMqRyiZIWFoAZJH_RBQw-M5ow,7675
20
+ coderouter/secret_redaction.py,sha256=Rg71gqKN6jnybZ6UvCvXIvnThI9uRBvmOqC2grSUSUk,23159
18
21
  coderouter/token_estimation.py,sha256=7klQYA4FIg1vtjQGWAhwiBhiLwyz8KTetrLVXFJLGIc,16957
19
22
  coderouter/token_estimation_accurate.py,sha256=GTfzrBVnvAGjeVzmzAeUdOYZvWZKLAxcxPpFiJGlzjk,4609
20
- coderouter/vscode_init.py,sha256=fW0chCDTngvWQuJgQ7aqYmE6hnZO3hCk8mAiEkZ9FCo,26741
23
+ coderouter/vscode_init.py,sha256=ZEROtETsHjyBkLzZKPnF-zsBpLJx4ph0vmgyty6kNYQ,33433
21
24
  coderouter/adapters/__init__.py,sha256=7dIDSZ-FE_0iSqLSDc_lK1idRdLTKcM2hP9tCJipgPI,463
22
- coderouter/adapters/anthropic_native.py,sha256=-FGTQFlFL5Z2IZms4NUGPcNkIN-fVvQrCY8e8nxW17s,24777
23
- coderouter/adapters/base.py,sha256=ykKMaiIVVGr23oFFXilK2iP_YhI8-CtsEiJyFXaC9bE,10396
24
- coderouter/adapters/openai_compat.py,sha256=wcrd_UpV7N6ISpUyKvdFPGU-YjCrX5oMqGpjAFEeWfg,20356
25
+ coderouter/adapters/anthropic_native.py,sha256=JxOaU-ymU1K3Tnvhufi_li1qChW69XtUszKJ2DdFlDQ,29936
26
+ coderouter/adapters/base.py,sha256=qW0QMbO08Dw2ZDruL9rCvCxA0FXhKnSodlK9g-8Y350,13535
27
+ coderouter/adapters/openai_compat.py,sha256=Kl5AACqOHxTPUn8c-4IFu4ug9_E3loLKTm2yYmvalho,23872
25
28
  coderouter/adapters/registry.py,sha256=XNc1wmFv_oZlqKS0rA693TRDdbq1sZ-OIcofXOVOtq0,3680
26
29
  coderouter/config/__init__.py,sha256=FODEn74fN-qZnt4INPSHswqhOlEgpL6-_onxsitSx8g,274
27
30
  coderouter/config/capability_registry.py,sha256=RZgzHF54dXy0CCDocRCz2ee21qXGbbgTSKTkRI9iLL4,16936
28
31
  coderouter/config/env_file.py,sha256=CoMK27fuAXm-NtoLzXb8yN2E-wDFjHQuFwiIlmgTBQw,10356
29
- coderouter/config/loader.py,sha256=vk1LFm-OCiyq2GB5G53g7kTnrJgmQjkZPqgr9X13wpc,11294
30
- coderouter/config/schemas.py,sha256=J05km7-YXgNxy6guKUDcNsuMyJ21SnDIurjx8Rz_hSg,136672
32
+ coderouter/config/loader.py,sha256=7FoJXWaO_vN9FAjUStJDwI4aAYcUQQgMJtq5BNy8Pnc,12419
33
+ coderouter/config/schemas.py,sha256=0NM5KtVGsjH8BPBeBfgM-ItfOYQKhyI-P_3EbJjIaps,146436
31
34
  coderouter/data/__init__.py,sha256=uNyfD9jaCvTWsBAWtaw1Fr25OSxzv3psGMfBjT1z0Cc,328
32
35
  coderouter/data/model-capabilities.yaml,sha256=S9jt6SC6-3s2-icZ_n-a14iEMnc2yB1C2R6q-N_tZWQ,19309
33
36
  coderouter/guards/__init__.py,sha256=5qliYBqygvVPneej7nx0uSjxDKsz7t8VzvrDgVBJlvU,1170
@@ -42,29 +45,31 @@ coderouter/guards/memory_pressure.py,sha256=9Xm-b7rv9Td5kPNmAFoL3sNmaOR5zT8NLdn6
42
45
  coderouter/guards/self_healing.py,sha256=XeUlgFWGqMQ5E9YvvzR2CsHRJOAJEyDChqRN0xmZ0vo,16841
43
46
  coderouter/guards/tool_loop.py,sha256=EzeMcmU7BLeTW2jsRVevU81l5rhWcn1oUr7EpzgXjVM,15209
44
47
  coderouter/ingress/__init__.py,sha256=WQsCH2CGJCAhy0mS6GSEdeYZRkkQu2OHDsP4CJWTLug,155
45
- coderouter/ingress/anthropic_routes.py,sha256=1ThXzx8VgxRV7UCi3NF5ey7kbr0ADPKTXyQuYS5gK1w,27063
48
+ coderouter/ingress/anthropic_routes.py,sha256=wlOb7ChWHMvuERR0lj8TF4MfPis2ZKegceoak4eREes,31131
46
49
  coderouter/ingress/app.py,sha256=ZOx7dSvMxaWS4CzXOOEwgzFuycAVgAeWKCN6lFNF-F0,22433
47
- coderouter/ingress/dashboard_routes.py,sha256=1SqNNrVTfQiZcx5n2MvjAzWirittX0e61tCt-MKp4Ag,25617
50
+ coderouter/ingress/dashboard_routes.py,sha256=nPW5_pJe27-hzqCs4sqVVn0BrgXB_7Wcd4qZ6cirvWo,26736
48
51
  coderouter/ingress/launcher_routes.py,sha256=uP_Otp9JE0fydUt1YP-wYcnS8Kj5RPLNIFf88YpfkPM,141535
49
- coderouter/ingress/metrics_routes.py,sha256=M22dwOGn24P05Ge4W3c7d7mYytSGWjIR-pPSPOAiHJY,3965
50
- coderouter/ingress/openai_routes.py,sha256=TrugsQNJfNPgKKLzfT1aXzrs4emWYGE4XebEBuaZoWk,12505
52
+ coderouter/ingress/metrics_routes.py,sha256=4s_f9CY8nVIdQBfXxyUMlN7r5_cXHOaAu7PvNbUuoxg,4609
53
+ coderouter/ingress/openai_routes.py,sha256=9DHVtGe1mpcUBH3ziDL4RGpEA-LzjyANpBIN3Kfbpxc,14193
54
+ coderouter/ingress/ui_auth.py,sha256=uCWB5IaMQRNFYdY39FQ5DDVhsoRQJoqimZNSAu_QK_Q,3742
51
55
  coderouter/metrics/__init__.py,sha256=7Es351DPS7yLM0yVF_F0eesmiD83n7Zzhie44chht38,1465
52
- coderouter/metrics/collector.py,sha256=wdGuX5yOBWEHCz1KVIfaUBZP5NXW3U34yyYlLGwgoCE,61212
53
- coderouter/metrics/prometheus.py,sha256=THYkvrcpQK3ZNW-BV0h3O-PoT2ppNZ0dapQuu1nyHwE,24410
56
+ coderouter/metrics/collector.py,sha256=-744UrUS6_aEY7hbwuvoX0Nn7wuOsQ57IIVr52nPe2M,63268
57
+ coderouter/metrics/prometheus.py,sha256=jcOThXTRCMIYS1loCiCoPKMGoA6zFkmgpcaitu8Ea4I,26517
54
58
  coderouter/plugins/__init__.py,sha256=76hMLe5dV_ilripHXzWn3HSYoIALjzlw4EJVyI-GyIM,1974
55
59
  coderouter/plugins/base.py,sha256=HACgppdfPyQRlHWXgwSvUeImaG_KRB0uLjhhQr1Ohyk,7267
56
60
  coderouter/plugins/loader.py,sha256=KLnKgO6_GE7OJulwo292cTYPvxqWZlLL8S0k_O3ZDcg,7139
57
61
  coderouter/plugins/registry.py,sha256=8UDmRpHnRdB-ES_xpprvrvbDlhIhonOvFFXobR6MGJA,3739
58
- coderouter/routing/__init__.py,sha256=g2vhutbozRx5QBThReqwPN3imk5qXdpDiaogILd3IRc,257
62
+ coderouter/routing/__init__.py,sha256=oLQXPvKfrwy0OiGYNXngM9G_OxmJVLShv-nqyDaqSOs,441
59
63
  coderouter/routing/adaptive.py,sha256=G2o377twGSjbUh65wiIFx6klnpFGjsD_nI3oDvcBwhY,21257
60
64
  coderouter/routing/auto_router.py,sha256=y4v0c8u5F9f98Vmhx1vRcKPiOgAvpzbFqr6TIh058h0,13341
61
65
  coderouter/routing/budget.py,sha256=PblmVKJGs_BwNa9uDHAA8hmZ4XIVKv38mHAeU0V3OMs,8451
62
66
  coderouter/routing/capability.py,sha256=rRQhzTgQYFTFDNYcSLKvq3xjdw6B1w7T-3jS7PTMI14,27864
63
- coderouter/routing/fallback.py,sha256=fqwjz5hTNsQDEf45nYcVTwGJwz2mFxQ5ZmZwdvFSKsI,156394
67
+ coderouter/routing/fallback.py,sha256=Hzy-_2_zK64GUqGGcOZP-RgOq8wZ4PdBpVu4OBTx8dM,172282
68
+ coderouter/routing/fallback_trace.py,sha256=0BysXY1CZqSvVTuePbgmHbu1AKkHLMX7hsuG6rKlDdE,19787
64
69
  coderouter/state/__init__.py,sha256=XoGcPmmBQSiZWML2S0juSveQ78xfhtdeCliNnVyzu7E,1088
65
- coderouter/state/audit_log.py,sha256=n7vuDsTfd5iuNUJhlRQPakJ2tMSsT9EfgPCzs1GEaac,10941
70
+ coderouter/state/audit_log.py,sha256=CEFNJht2WZ8mqesI8CzeuL63NfzFzAZ7wF88auyl6l0,11444
66
71
  coderouter/state/replay.py,sha256=Z_YHKroTKZdrL8qObFxcoLOAQWWXZvXFdLfxzvBhEJg,11230
67
- coderouter/state/request_log.py,sha256=mNxQJ9IDGpUtT5rZbCo_-CTyhxmvig7C2Gfvdn9rcss,8612
72
+ coderouter/state/request_log.py,sha256=JlEvkWHwGtoLesnqr0XJy9CyXQDX4ZRHFMTPJ_0FegQ,8898
68
73
  coderouter/state/store.py,sha256=h-rsMJq8GILsOfCP94nI40cuHaj4Vqycsm9UNN77REI,7445
69
74
  coderouter/state/suggest_rules.py,sha256=FvdhEvao5NvdKp9zs8AkcoFKHY4yqqXY2HekvSjpDFA,16670
70
75
  coderouter/translation/__init__.py,sha256=PYXN7XVEwpG1uC8RLy6fvnGbzEZhhrEuUapH8IYOtG8,1788
@@ -72,8 +77,8 @@ coderouter/translation/anthropic.py,sha256=ZQQGS9itN_3o7IKD68lKr5gR_Kem32Jsd7OcG
72
77
  coderouter/translation/convert.py,sha256=VrstDAMsUpcQ1VgMTn_-ETp1OjGX8v0ZGhCkcd4icyU,53037
73
78
  coderouter/translation/tool_repair.py,sha256=1p8rmdyyscGX2g0DDOYitMnLv-nKfR1SLuLaRB4z7GM,64244
74
79
  coderouter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- coderouter_cli-2.13.0.dist-info/METADATA,sha256=J6h8klUNsTIhWoMjrax70AR9nCFXYK_rFP2tbpChn8I,16897
76
- coderouter_cli-2.13.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
77
- coderouter_cli-2.13.0.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
78
- coderouter_cli-2.13.0.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
79
- coderouter_cli-2.13.0.dist-info/RECORD,,
80
+ coderouter_cli-2.15.0.dist-info/METADATA,sha256=1TbuS8h0n05w9EzQ25lzLSF38CH05QLjz1tLjY-3nR0,16897
81
+ coderouter_cli-2.15.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
82
+ coderouter_cli-2.15.0.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
83
+ coderouter_cli-2.15.0.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
84
+ coderouter_cli-2.15.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.31.0
2
+ Generator: hatchling 1.32.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any