Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e660768c | ||
|
|
bfd0ff196d | ||
|
|
1e0f2f9aea | ||
|
|
b560950f82 |
@@ -26,9 +26,42 @@ from resim_context import resimleri_sadelestir
|
|||||||
import ide_catalog
|
import ide_catalog
|
||||||
import ozet_kopru
|
import ozet_kopru
|
||||||
|
|
||||||
SURUM = "1"
|
SURUM = "4"
|
||||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# Codex sends request bodies zstd-compressed. Python gained zstd in 3.14; for older
|
||||||
|
# interpreters the matching `zstandard` build ships next to this file.
|
||||||
|
_VENDOR = os.path.join(HERE, "vendor", "cp%d%d-win_amd64" % sys.version_info[:2])
|
||||||
|
if os.path.isdir(_VENDOR):
|
||||||
|
sys.path.insert(0, _VENDOR)
|
||||||
|
|
||||||
|
|
||||||
|
def _zstd_decompress(body):
|
||||||
|
try:
|
||||||
|
from compression import zstd as _z
|
||||||
|
return _z.decompress(body)
|
||||||
|
except ImportError:
|
||||||
|
import zstandard
|
||||||
|
return zstandard.ZstdDecompressor().decompressobj().decompress(body)
|
||||||
|
|
||||||
|
|
||||||
|
def _zstd_ready():
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
from compression import zstd as _z
|
||||||
|
sample = _z.compress(b"aiturk")
|
||||||
|
except ImportError:
|
||||||
|
import zstandard
|
||||||
|
sample = zstandard.ZstdCompressor().compress(b"aiturk")
|
||||||
|
return _zstd_decompress(sample) == b"aiturk"
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Without zstd every Codex request would fail; the status endpoint then reports
|
||||||
|
# ok=false so that the IDE never points Codex at this bridge.
|
||||||
|
ZSTD_OK = _zstd_ready()
|
||||||
|
|
||||||
|
|
||||||
def _arg(name, env):
|
def _arg(name, env):
|
||||||
"""--name <value> from the command line, else the environment variable."""
|
"""--name <value> from the command line, else the environment variable."""
|
||||||
@@ -468,10 +501,7 @@ def decompress(body, enc):
|
|||||||
if enc == "deflate":
|
if enc == "deflate":
|
||||||
import zlib; return zlib.decompress(body)
|
import zlib; return zlib.decompress(body)
|
||||||
if enc == "zstd":
|
if enc == "zstd":
|
||||||
try:
|
return _zstd_decompress(body)
|
||||||
from compression import zstd as _z; return _z.decompress(body)
|
|
||||||
except ImportError:
|
|
||||||
import zstandard; return zstandard.ZstdDecompressor().decompressobj().decompress(body)
|
|
||||||
if enc == "br":
|
if enc == "br":
|
||||||
import brotli; return brotli.decompress(body)
|
import brotli; return brotli.decompress(body)
|
||||||
return body
|
return body
|
||||||
@@ -533,9 +563,18 @@ class H(http.server.BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
def _control(self, path):
|
def _control(self, path):
|
||||||
"""Local status/stop for AITURK IDE. Loopback only; carries no secrets."""
|
"""Local status/stop for AITURK IDE. Loopback only; carries no secrets."""
|
||||||
|
if self.command == "POST" and path.rstrip("/").endswith("/refresh"):
|
||||||
|
# The IDE asks for this after a sign-in or a settings change, so the model
|
||||||
|
# list does not wait for the next periodic refresh.
|
||||||
|
try:
|
||||||
|
load_ide_catalog()
|
||||||
|
except Exception as e:
|
||||||
|
log(f"katalog yenileme hatasi: {e}")
|
||||||
|
path = "/aiturk/codex-baglama/status"
|
||||||
|
self.command = "GET"
|
||||||
if self.command == "GET" and path.rstrip("/").endswith("/status"):
|
if self.command == "GET" and path.rstrip("/").endswith("/status"):
|
||||||
durum, _, sayi = (IDE_DURUM[0] or "kapali, 0 model").partition(", ")
|
durum, _, sayi = (IDE_DURUM[0] or "kapali, 0 model").partition(", ")
|
||||||
return self._send_json(200, {"ok": True, "surum": SURUM, "ide_durum": durum,
|
return self._send_json(200, {"ok": ZSTD_OK, "surum": SURUM, "zstd": ZSTD_OK, "ide_durum": durum,
|
||||||
"ide_model": int((sayi.split() or ["0"])[0]),
|
"ide_model": int((sayi.split() or ["0"])[0]),
|
||||||
"ozet": bool((CFG.get("ozet_kopru") or {}).get("enabled", False))})
|
"ozet": bool((CFG.get("ozet_kopru") or {}).get("enabled", False))})
|
||||||
if self.command == "POST" and path.rstrip("/").endswith("/stop"):
|
if self.command == "POST" and path.rstrip("/").endswith("/stop"):
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ anahtar IDE'nin kendi .env dosyasindan her yenilemede okunur, bridge.json'a yazi
|
|||||||
Ac/kapat: %LOCALAPPDATA%\\TurkServis\\AITURK-IDE\\codex-baglama.json
|
Ac/kapat: %LOCALAPPDATA%\\TurkServis\\AITURK-IDE\\codex-baglama.json
|
||||||
{"enabled": true, "kapsam": "ide"}
|
{"enabled": true, "kapsam": "ide"}
|
||||||
Dosya yoksa ACIK sayilir (kullanici karari, 9 Ekim 2026: varsayilan acik).
|
Dosya yoksa ACIK sayilir (kullanici karari, 9 Ekim 2026: varsayilan acik).
|
||||||
kapsam "ide" : IDE ayarinda tanimli modeller + adlandirilmis combolar (varsayilan)
|
kapsam "ide" : IDE ayarinda tanimli modeller + 9Router'a eklenmis saglayicilarin modelleri +
|
||||||
|
adlandirilmis combolar (varsayilan)
|
||||||
kapsam "hepsi" : gecitteki butun sohbet modelleri (1000+; Codex listesi cok buyur)
|
kapsam "hepsi" : gecitteki butun sohbet modelleri (1000+; Codex listesi cok buyur)
|
||||||
"""
|
"""
|
||||||
import json, os, re, urllib.request
|
import json, os, re, urllib.request
|
||||||
@@ -84,23 +85,33 @@ def ide_yapilandirma(agent=AGENT):
|
|||||||
return base.rstrip("/"), varsayilan, adlar
|
return base.rstrip("/"), varsayilan, adlar
|
||||||
|
|
||||||
|
|
||||||
|
DOKUZ_ISARET = "9R · "
|
||||||
|
|
||||||
|
|
||||||
|
def _isaret(ad):
|
||||||
|
"""Gecidin dogrudan 9Router'dan gelen tekil modele koydugu kisa isaret korunur (kullanici karari,
|
||||||
|
9 Ekim 2026: "9Router" yazisi uzun, hic yazmamak da sorun; "9R" olarak kisa gorunsun)."""
|
||||||
|
return DOKUZ_ISARET if (ad or "").startswith(DOKUZ_ISARET) else ""
|
||||||
|
|
||||||
|
|
||||||
def kisa_ad(ad):
|
def kisa_ad(ad):
|
||||||
"""Codex secicisinde gorunecek ad: yalniz kullanicinin ekledigi saglayici + model adi
|
"""Codex secicisinde gorunecek ad: yalniz kullanicinin ekledigi saglayici + model adi
|
||||||
(kullanici istegi, 9 Ekim 2026). "OmniRoute · " / "9Router · " gibi gecit onekleri ve aradaki
|
(kullanici istegi, 9 Ekim 2026). "OmniRoute · " / "9Router · " gibi gecit onekleri ve aradaki
|
||||||
saglayici halkalari gosterilmez: "nano/qwen/qwen3.8-27b" -> "nano/qwen3.8-27b",
|
saglayici halkalari gosterilmez: "nano/qwen/qwen3.8-27b" -> "nano/qwen3.8-27b",
|
||||||
"OmniRoute · ag/gemini-3.8-flash" -> "ag/gemini-3.8-flash", combo adi oldugu gibi kalir."""
|
"OmniRoute · ag/gemini-3.8-flash" -> "ag/gemini-3.8-flash", combo adi oldugu gibi kalir."""
|
||||||
|
on = _isaret(ad)
|
||||||
ad = (ad or "").split(" · ")[-1].strip()
|
ad = (ad or "").split(" · ")[-1].strip()
|
||||||
parcalar = [p for p in ad.split("/") if p]
|
parcalar = [p for p in ad.split("/") if p]
|
||||||
if len(parcalar) <= 2:
|
if len(parcalar) <= 2:
|
||||||
return "/".join(parcalar) or ad
|
return on + ("/".join(parcalar) or ad)
|
||||||
return parcalar[0] + "/" + parcalar[-1]
|
return on + parcalar[0] + "/" + parcalar[-1]
|
||||||
|
|
||||||
|
|
||||||
def adlandir(girdiler, dolu=()):
|
def adlandir(girdiler, dolu=()):
|
||||||
"""[(kimlik, tam_ad)] -> {kimlik: gorunen_ad}. Kisa adlar cakisirsa aradaki halkalar geri konur;
|
"""[(kimlik, tam_ad)] -> {kimlik: gorunen_ad}. Kisa adlar cakisirsa aradaki halkalar geri konur;
|
||||||
yine cakisirsa kimligin sonu eklenir. `dolu`: baska kaynaktan gelen, zaten kullanilan adlar."""
|
yine cakisirsa kimligin sonu eklenir. `dolu`: baska kaynaktan gelen, zaten kullanilan adlar."""
|
||||||
kisa = {k: kisa_ad(a) for k, a in girdiler}
|
kisa = {k: kisa_ad(a) for k, a in girdiler}
|
||||||
tam = {k: (a or "").split(" · ")[-1].strip() for k, a in girdiler}
|
tam = {k: _isaret(a) + (a or "").split(" · ")[-1].strip() for k, a in girdiler}
|
||||||
sayac = {}
|
sayac = {}
|
||||||
for v in list(kisa.values()) + list(dolu):
|
for v in list(kisa.values()) + list(dolu):
|
||||||
sayac[v.lower()] = sayac.get(v.lower(), 0) + 1
|
sayac[v.lower()] = sayac.get(v.lower(), 0) + 1
|
||||||
@@ -127,11 +138,26 @@ def sec(data, tanimli, kapsam="ide"):
|
|||||||
gorunen = ts.get("display_name") or mid
|
gorunen = ts.get("display_name") or mid
|
||||||
kisa = gorunen.split(" · ")[-1].strip()
|
kisa = gorunen.split(" · ")[-1].strip()
|
||||||
combo = "/" not in kisa # saglayici/model bicimi degil: adlandirilmis combo ya da takma ad
|
combo = "/" not in kisa # saglayici/model bicimi degil: adlandirilmis combo ya da takma ad
|
||||||
if kapsam != "hepsi" and not combo and kisa.lower() not in tanimli and mid.lower() not in tanimli:
|
# 9Router'dan gelen combolar Codex'te gosterilmez (kullanici karari, 9 Ekim 2026): dogrudan
|
||||||
|
# 9Router kaynagi ve gecidin "9router" saglayicisi uzerinden gelen kopyalari.
|
||||||
|
if combo and (ts.get("source") == "9router" or mid.lower().startswith("9router-")):
|
||||||
|
continue
|
||||||
|
# 9Router'a eklenmis saglayicilarin tekil modelleri her zaman eklenir (kullanici karari, 9 Ekim 2026:
|
||||||
|
# "9Router'in combosu olmasin dedim, 9Router'in modeli olsun"). Ayni modelin OmniRoute uzerinden
|
||||||
|
# gelen kopyasi yalniz IDE ayarinda tanimliysa eklenir; boylece her model listede bir kez durur.
|
||||||
|
dokuz_model = not combo and ts.get("source") == "9router"
|
||||||
|
if kapsam != "hepsi" and not combo and not dokuz_model and kisa.lower() not in tanimli and mid.lower() not in tanimli:
|
||||||
continue
|
continue
|
||||||
gorulen.add(mid)
|
gorulen.add(mid)
|
||||||
secilen.append({"id": mid, "ad": gorunen, "sira": ts.get("order") or 0})
|
secilen.append({"id": mid, "ad": gorunen, "sira": ts.get("order") or 0})
|
||||||
secilen.sort(key=lambda s: (s["sira"], s["id"]))
|
secilen.sort(key=lambda s: (s["sira"], s["id"]))
|
||||||
|
# Gecit ayni adin ikinci yoluna "ad (2)" der; birincisi listede yoksa sayac anlamsizdir.
|
||||||
|
adlar = {s["ad"].split(" · ")[-1].strip().lower() for s in secilen}
|
||||||
|
for s in secilen:
|
||||||
|
taban = re.sub(r" \(\d+\)$", "", s["ad"])
|
||||||
|
if taban != s["ad"] and taban.split(" · ")[-1].strip().lower() not in adlar:
|
||||||
|
adlar.add(taban.split(" · ")[-1].strip().lower())
|
||||||
|
s["ad"] = taban
|
||||||
return [{"id": s["id"], "ad": s["ad"]} for s in secilen]
|
return [{"id": s["id"], "ad": s["ad"]} for s in secilen]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import json, os, tempfile, unittest
|
|||||||
import ide_catalog as c
|
import ide_catalog as c
|
||||||
|
|
||||||
|
|
||||||
def girdi(mid, ad, yetenek=("chat",), sira=0):
|
def girdi(mid, ad, yetenek=("chat",), sira=0, kaynak="omniroute"):
|
||||||
return {"id": mid, "turkservis": {"display_name": ad, "capabilities": list(yetenek), "order": sira}}
|
return {"id": mid, "turkservis": {"display_name": ad, "capabilities": list(yetenek), "order": sira, "source": kaynak}}
|
||||||
|
|
||||||
|
|
||||||
KATALOG = [
|
KATALOG = [
|
||||||
@@ -53,13 +53,39 @@ class Secim(unittest.TestCase):
|
|||||||
def test_ide_kapsami(self):
|
def test_ide_kapsami(self):
|
||||||
s = c.sec(KATALOG, ["cc/claude-opus-5-5"], "ide")
|
s = c.sec(KATALOG, ["cc/claude-opus-5-5"], "ide")
|
||||||
# Tanimli model + adlandirilmis combolar; tanimsiz saglayici modeli ve gorsel modeli yok.
|
# Tanimli model + adlandirilmis combolar; tanimsiz saglayici modeli ve gorsel modeli yok.
|
||||||
self.assertEqual([x["id"] for x in s], ["9router-cc-opus", "Ajan-Arac", "9router-Takim-Agir-6e65dba5", "9router-Takim-Agir"])
|
self.assertEqual([x["id"] for x in s], ["9router-cc-opus", "Ajan-Arac"])
|
||||||
|
|
||||||
|
def test_9router_combolari_gosterilmez(self):
|
||||||
|
katalog = [girdi("9router-Takim-Agir-6e65dba5", "Takim-Agir", kaynak="9router", sira=1),
|
||||||
|
girdi("9router-Takim-Agir", "Takim-Agir (2)", sira=2),
|
||||||
|
girdi("Takim-Agir", "Takim-Agir (3)", sira=3),
|
||||||
|
girdi("Ajan-Arac", "Ajan-Arac", sira=4),
|
||||||
|
girdi("9router-nano-x", "nano/x", kaynak="9router", sira=5)]
|
||||||
|
s = c.sec(katalog, ["nano/x"], "ide")
|
||||||
|
# 9Router combosu ve gecidin 9router saglayicisindaki kopyasi yok; OmniRoute'un kendi combosu
|
||||||
|
# sayacsiz adiyla kalir. 9Router'in tekil modeli (combo degil) etkilenmez.
|
||||||
|
self.assertEqual([(x["id"], x["ad"]) for x in s],
|
||||||
|
[("Takim-Agir", "Takim-Agir"), ("Ajan-Arac", "Ajan-Arac"), ("9router-nano-x", "nano/x")])
|
||||||
|
|
||||||
|
def test_9router_saglayici_modelleri_tanimsiz_da_olsa_eklenir(self):
|
||||||
|
katalog = [girdi("9router-nano-x-aaaa1111", "nano/x", kaynak="9router", sira=1),
|
||||||
|
girdi("9router-nano-x", "nano/x (2)", sira=2),
|
||||||
|
girdi("9router-ocg-y-bbbb2222", "ocg/y", kaynak="9router", sira=3),
|
||||||
|
girdi("vag-z", "vag/z", sira=4),
|
||||||
|
girdi("9router-ses", "nano/ses", ("tts",), kaynak="9router", sira=5),
|
||||||
|
girdi("9router-Takim-Agir-6e65dba5", "Takim-Agir", kaynak="9router", sira=6)]
|
||||||
|
s = c.sec(katalog, [], "ide")
|
||||||
|
# 9Router modelleri IDE ayarinda adi gecmese de gelir; OmniRoute kopyasi, OmniRoute'un kendi
|
||||||
|
# saglayici modeli, sohbet olmayan model ve 9Router combosu gelmez.
|
||||||
|
self.assertEqual([(x["id"], x["ad"]) for x in s],
|
||||||
|
[("9router-nano-x-aaaa1111", "nano/x"), ("9router-ocg-y-bbbb2222", "ocg/y")])
|
||||||
|
|
||||||
def test_gorunen_ad_yalniz_saglayici_ve_model(self):
|
def test_gorunen_ad_yalniz_saglayici_ve_model(self):
|
||||||
for tam, beklenen in [("OmniRoute · ag/gemini-3.8-flash", "ag/gemini-3.8-flash"),
|
for tam, beklenen in [("OmniRoute · ag/gemini-3.8-flash", "ag/gemini-3.8-flash"),
|
||||||
("nano/qwen/qwen3.8-27b-obliterated:thinking", "nano/qwen3.8-27b-obliterated:thinking"),
|
("nano/qwen/qwen3.8-27b-obliterated:thinking", "nano/qwen3.8-27b-obliterated:thinking"),
|
||||||
("9Router · Nous-Research/inclusionai/ling-3.1-flash", "Nous-Research/ling-3.1-flash"),
|
("9Router · Nous-Research/inclusionai/ling-3.1-flash", "Nous-Research/ling-3.1-flash"),
|
||||||
("cf/@cf/qwen/qwq-32b", "cf/qwq-32b"),
|
("cf/@cf/qwen/qwq-32b", "cf/qwq-32b"),
|
||||||
|
("9R · nano/qwen/qwen3.8-27b", "9R · nano/qwen3.8-27b"), ("9R · ag/gemini", "9R · ag/gemini"),
|
||||||
("OmniRoute · Ajan-Arac", "Ajan-Arac"), ("cc/claude-opus-5-5", "cc/claude-opus-5-5")]:
|
("OmniRoute · Ajan-Arac", "Ajan-Arac"), ("cc/claude-opus-5-5", "cc/claude-opus-5-5")]:
|
||||||
self.assertEqual(c.kisa_ad(tam), beklenen, tam)
|
self.assertEqual(c.kisa_ad(tam), beklenen, tam)
|
||||||
|
|
||||||
@@ -74,7 +100,7 @@ class Secim(unittest.TestCase):
|
|||||||
|
|
||||||
def test_hepsi_kapsami_yalniz_sohbet(self):
|
def test_hepsi_kapsami_yalniz_sohbet(self):
|
||||||
s = c.sec(KATALOG, [], "hepsi")
|
s = c.sec(KATALOG, [], "hepsi")
|
||||||
self.assertEqual(len(s), 5)
|
self.assertEqual(len(s), 3) # iki 9Router combosu hicbir kapsamda gosterilmez
|
||||||
self.assertNotIn("gorsel", [x["id"] for x in s])
|
self.assertNotIn("gorsel", [x["id"] for x in s])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""Codex compresses request bodies with zstd; the bridge must be able to read them."""
|
||||||
|
import ast
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
tree = ast.parse(Path(__file__).with_name("bridge.py").read_text(encoding="utf-8"))
|
||||||
|
wanted = {"_zstd_decompress", "_zstd_ready", "decompress"}
|
||||||
|
env = {}
|
||||||
|
exec(compile(ast.Module(body=[n for n in tree.body if isinstance(n, ast.FunctionDef) and n.name in wanted],
|
||||||
|
type_ignores=[]), "bridge.py", "exec"), env)
|
||||||
|
|
||||||
|
|
||||||
|
class Zstd(unittest.TestCase):
|
||||||
|
def test_self_check_matches_reality(self):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
from compression import zstd as z
|
||||||
|
packed = z.compress(b"govde" * 50)
|
||||||
|
except ImportError:
|
||||||
|
import zstandard
|
||||||
|
packed = zstandard.ZstdCompressor().compress(b"govde" * 50)
|
||||||
|
except ImportError:
|
||||||
|
self.assertFalse(env["_zstd_ready"]()) # no zstd here: the bridge must say so
|
||||||
|
return
|
||||||
|
self.assertTrue(env["_zstd_ready"]())
|
||||||
|
self.assertEqual(env["decompress"](packed, "zstd"), b"govde" * 50)
|
||||||
|
|
||||||
|
def test_other_encodings(self):
|
||||||
|
import gzip
|
||||||
|
self.assertEqual(env["decompress"](gzip.compress(b"x"), "gzip"), b"x")
|
||||||
|
self.assertEqual(env["decompress"](b"duz", None), b"duz")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2016, Gregory Szorc
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
+217
@@ -0,0 +1,217 @@
|
|||||||
|
# Copyright (c) 2017-present, Gregory Szorc
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# This software may be modified and distributed under the terms
|
||||||
|
# of the BSD license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
# ruff: noqa: F403, F405
|
||||||
|
|
||||||
|
"""Python interface to the Zstandard (zstd) compression library."""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
# This module serves 2 roles:
|
||||||
|
#
|
||||||
|
# 1) Export the C or CFFI "backend" through a central module.
|
||||||
|
# 2) Implement additional functionality built on top of C or CFFI backend.
|
||||||
|
import builtins
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 12):
|
||||||
|
from collections.abc import Buffer
|
||||||
|
else:
|
||||||
|
from typing import ByteString as Buffer
|
||||||
|
|
||||||
|
# Some Python implementations don't support C extensions. That's why we have
|
||||||
|
# a CFFI implementation in the first place. The code here import one of our
|
||||||
|
# "backends" then re-exports the symbols from this module. For convenience,
|
||||||
|
# we support falling back to the CFFI backend if the C extension can't be
|
||||||
|
# imported. But for performance reasons, we only do this on unknown Python
|
||||||
|
# implementation. Notably, for CPython we require the C extension by default.
|
||||||
|
# Because someone will inevitably want special behavior, the behavior is
|
||||||
|
# configurable via an environment variable. A potentially better way to handle
|
||||||
|
# this is to import a special ``__importpolicy__`` module or something
|
||||||
|
# defining a variable and `setup.py` could write the file with whatever
|
||||||
|
# policy was specified at build time. Until someone needs it, we go with
|
||||||
|
# the hacky but simple environment variable approach.
|
||||||
|
_module_policy = os.environ.get(
|
||||||
|
"PYTHON_ZSTANDARD_IMPORT_POLICY", "default"
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
if _module_policy == "default":
|
||||||
|
if platform.python_implementation() in ("CPython",):
|
||||||
|
from .backend_c import * # type: ignore
|
||||||
|
|
||||||
|
backend = "cext"
|
||||||
|
elif platform.python_implementation() in ("PyPy",):
|
||||||
|
from .backend_cffi import * # type: ignore
|
||||||
|
|
||||||
|
backend = "cffi"
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
from .backend_c import *
|
||||||
|
|
||||||
|
backend = "cext"
|
||||||
|
except ImportError:
|
||||||
|
from .backend_cffi import *
|
||||||
|
|
||||||
|
backend = "cffi"
|
||||||
|
elif _module_policy == "cffi_fallback":
|
||||||
|
try:
|
||||||
|
from .backend_c import *
|
||||||
|
|
||||||
|
backend = "cext"
|
||||||
|
except ImportError:
|
||||||
|
from .backend_cffi import *
|
||||||
|
|
||||||
|
backend = "cffi"
|
||||||
|
elif _module_policy == "rust":
|
||||||
|
from .backend_rust import * # type: ignore
|
||||||
|
|
||||||
|
backend = "rust"
|
||||||
|
elif _module_policy == "cext":
|
||||||
|
from .backend_c import *
|
||||||
|
|
||||||
|
backend = "cext"
|
||||||
|
elif _module_policy == "cffi":
|
||||||
|
from .backend_cffi import *
|
||||||
|
|
||||||
|
backend = "cffi"
|
||||||
|
else:
|
||||||
|
raise ImportError(
|
||||||
|
"unknown module import policy: %s; use default, cffi_fallback, "
|
||||||
|
"cext, or cffi" % _module_policy
|
||||||
|
)
|
||||||
|
|
||||||
|
# Keep this in sync with python-zstandard.h, rust-ext/src/lib.rs, and debian/changelog.
|
||||||
|
__version__ = "0.25.0"
|
||||||
|
|
||||||
|
_MODE_CLOSED = 0
|
||||||
|
_MODE_READ = 1
|
||||||
|
_MODE_WRITE = 2
|
||||||
|
|
||||||
|
|
||||||
|
def open(
|
||||||
|
filename,
|
||||||
|
mode="rb",
|
||||||
|
cctx=None,
|
||||||
|
dctx=None,
|
||||||
|
encoding=None,
|
||||||
|
errors=None,
|
||||||
|
newline=None,
|
||||||
|
closefd=None,
|
||||||
|
):
|
||||||
|
"""Create a file object with zstd (de)compression.
|
||||||
|
|
||||||
|
The object returned from this function will be a
|
||||||
|
:py:class:`ZstdDecompressionReader` if opened for reading in binary mode,
|
||||||
|
a :py:class:`ZstdCompressionWriter` if opened for writing in binary mode,
|
||||||
|
or an ``io.TextIOWrapper`` if opened for reading or writing in text mode.
|
||||||
|
|
||||||
|
:param filename:
|
||||||
|
``bytes``, ``str``, or ``os.PathLike`` defining a file to open or a
|
||||||
|
file object (with a ``read()`` or ``write()`` method).
|
||||||
|
:param mode:
|
||||||
|
``str`` File open mode. Accepts any of the open modes recognized by
|
||||||
|
``open()``.
|
||||||
|
:param cctx:
|
||||||
|
``ZstdCompressor`` to use for compression. If not specified and file
|
||||||
|
is opened for writing, the default ``ZstdCompressor`` will be used.
|
||||||
|
:param dctx:
|
||||||
|
``ZstdDecompressor`` to use for decompression. If not specified and file
|
||||||
|
is opened for reading, the default ``ZstdDecompressor`` will be used.
|
||||||
|
:param encoding:
|
||||||
|
``str`` that defines text encoding to use when file is opened in text
|
||||||
|
mode.
|
||||||
|
:param errors:
|
||||||
|
``str`` defining text encoding error handling mode.
|
||||||
|
:param newline:
|
||||||
|
``str`` defining newline to use in text mode.
|
||||||
|
:param closefd:
|
||||||
|
``bool`` whether to close the file when the returned object is closed.
|
||||||
|
Only used if a file object is passed. If a filename is specified, the
|
||||||
|
opened file is always closed when the returned object is closed.
|
||||||
|
"""
|
||||||
|
normalized_mode = mode.replace("t", "")
|
||||||
|
|
||||||
|
if normalized_mode in ("r", "rb"):
|
||||||
|
dctx = dctx or ZstdDecompressor()
|
||||||
|
open_mode = "r"
|
||||||
|
raw_open_mode = "rb"
|
||||||
|
elif normalized_mode in ("w", "wb", "a", "ab", "x", "xb"):
|
||||||
|
cctx = cctx or ZstdCompressor()
|
||||||
|
open_mode = "w"
|
||||||
|
raw_open_mode = normalized_mode
|
||||||
|
if not raw_open_mode.endswith("b"):
|
||||||
|
raw_open_mode = raw_open_mode + "b"
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid mode: {!r}".format(mode))
|
||||||
|
|
||||||
|
if hasattr(os, "PathLike"):
|
||||||
|
types = (str, bytes, os.PathLike)
|
||||||
|
else:
|
||||||
|
types = (str, bytes)
|
||||||
|
|
||||||
|
if isinstance(filename, types): # type: ignore
|
||||||
|
inner_fh = builtins.open(filename, raw_open_mode)
|
||||||
|
closefd = True
|
||||||
|
elif hasattr(filename, "read") or hasattr(filename, "write"):
|
||||||
|
inner_fh = filename
|
||||||
|
closefd = bool(closefd)
|
||||||
|
else:
|
||||||
|
raise TypeError(
|
||||||
|
"filename must be a str, bytes, file or PathLike object"
|
||||||
|
)
|
||||||
|
|
||||||
|
if open_mode == "r":
|
||||||
|
fh = dctx.stream_reader(inner_fh, closefd=closefd)
|
||||||
|
elif open_mode == "w":
|
||||||
|
fh = cctx.stream_writer(inner_fh, closefd=closefd)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("logic error in zstandard.open() handling open mode")
|
||||||
|
|
||||||
|
if "b" not in normalized_mode:
|
||||||
|
return io.TextIOWrapper(
|
||||||
|
fh, encoding=encoding, errors=errors, newline=newline
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return fh
|
||||||
|
|
||||||
|
|
||||||
|
def compress(data: Buffer, level: int = 3) -> bytes:
|
||||||
|
"""Compress source data using the zstd compression format.
|
||||||
|
|
||||||
|
This performs one-shot compression using basic/default compression
|
||||||
|
settings.
|
||||||
|
|
||||||
|
This method is provided for convenience and is equivalent to calling
|
||||||
|
``ZstdCompressor(level=level).compress(data)``.
|
||||||
|
|
||||||
|
If you find yourself calling this function in a tight loop,
|
||||||
|
performance will be greater if you construct a single ``ZstdCompressor``
|
||||||
|
and repeatedly call ``compress()`` on it.
|
||||||
|
"""
|
||||||
|
cctx = ZstdCompressor(level=level)
|
||||||
|
|
||||||
|
return cctx.compress(data)
|
||||||
|
|
||||||
|
|
||||||
|
def decompress(data: Buffer, max_output_size: int = 0) -> bytes:
|
||||||
|
"""Decompress a zstd frame into its original data.
|
||||||
|
|
||||||
|
This performs one-shot decompression using basic/default compression
|
||||||
|
settings.
|
||||||
|
|
||||||
|
This method is provided for convenience and is equivalent to calling
|
||||||
|
``ZstdDecompressor().decompress(data, max_output_size=max_output_size)``.
|
||||||
|
|
||||||
|
If you find yourself calling this function in a tight loop, performance
|
||||||
|
will be greater if you construct a single ``ZstdDecompressor`` and
|
||||||
|
repeatedly call ``decompress()`` on it.
|
||||||
|
"""
|
||||||
|
dctx = ZstdDecompressor()
|
||||||
|
|
||||||
|
return dctx.decompress(data, max_output_size=max_output_size)
|
||||||
+481
@@ -0,0 +1,481 @@
|
|||||||
|
# Copyright (c) 2016-present, Gregory Szorc
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# This software may be modified and distributed under the terms
|
||||||
|
# of the BSD license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import (
|
||||||
|
IO,
|
||||||
|
BinaryIO,
|
||||||
|
ByteString,
|
||||||
|
Generator,
|
||||||
|
Iterable,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
|
Tuple,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
|
||||||
|
FLUSH_BLOCK: int
|
||||||
|
FLUSH_FRAME: int
|
||||||
|
|
||||||
|
COMPRESSOBJ_FLUSH_FINISH: int
|
||||||
|
COMPRESSOBJ_FLUSH_BLOCK: int
|
||||||
|
|
||||||
|
CONTENTSIZE_UNKNOWN: int
|
||||||
|
CONTENTSIZE_ERROR: int
|
||||||
|
|
||||||
|
MAX_COMPRESSION_LEVEL: int
|
||||||
|
|
||||||
|
COMPRESSION_RECOMMENDED_INPUT_SIZE: int
|
||||||
|
COMPRESSION_RECOMMENDED_OUTPUT_SIZE: int
|
||||||
|
|
||||||
|
DECOMPRESSION_RECOMMENDED_INPUT_SIZE: int
|
||||||
|
DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE: int
|
||||||
|
|
||||||
|
BLOCKSIZELOG_MAX: int
|
||||||
|
BLOCKSIZE_MAX: int
|
||||||
|
|
||||||
|
WINDOWLOG_MIN: int
|
||||||
|
WINDOWLOG_MAX: int
|
||||||
|
|
||||||
|
CHAINLOG_MIN: int
|
||||||
|
CHAINLOG_MAX: int
|
||||||
|
HASHLOG_MIN: int
|
||||||
|
HASHLOG_MAX: int
|
||||||
|
MINMATCH_MIN: int
|
||||||
|
MINMATCH_MAX: int
|
||||||
|
SEARCHLOG_MIN: int
|
||||||
|
SEARCHLOG_MAX: int
|
||||||
|
SEARCHLENGTH_MIN: int
|
||||||
|
SEARCHLENGTH_MAX: int
|
||||||
|
TARGETLENGTH_MIN: int
|
||||||
|
TARGETLENGTH_MAX: int
|
||||||
|
LDM_MINMATCH_MIN: int
|
||||||
|
LDM_MINMATCH_MAX: int
|
||||||
|
LDM_BUCKETSIZELOG_MAX: int
|
||||||
|
|
||||||
|
STRATEGY_FAST: int
|
||||||
|
STRATEGY_DFAST: int
|
||||||
|
STRATEGY_GREEDY: int
|
||||||
|
STRATEGY_LAZY: int
|
||||||
|
STRATEGY_LAZY2: int
|
||||||
|
STRATEGY_BTLAZY2: int
|
||||||
|
STRATEGY_BTOPT: int
|
||||||
|
STRATEGY_BTULTRA: int
|
||||||
|
STRATEGY_BTULTRA2: int
|
||||||
|
|
||||||
|
DICT_TYPE_AUTO: int
|
||||||
|
DICT_TYPE_RAWCONTENT: int
|
||||||
|
DICT_TYPE_FULLDICT: int
|
||||||
|
|
||||||
|
FORMAT_ZSTD1: int
|
||||||
|
FORMAT_ZSTD1_MAGICLESS: int
|
||||||
|
|
||||||
|
ZSTD_VERSION: Tuple[int, int, int]
|
||||||
|
FRAME_HEADER: bytes
|
||||||
|
MAGIC_NUMBER: int
|
||||||
|
|
||||||
|
backend: str
|
||||||
|
backend_features: Set[str]
|
||||||
|
__version__: str
|
||||||
|
|
||||||
|
class ZstdError(Exception): ...
|
||||||
|
|
||||||
|
class BufferSegment(object):
|
||||||
|
offset: int
|
||||||
|
def __len__(self) -> int: ...
|
||||||
|
def tobytes(self) -> bytes: ...
|
||||||
|
|
||||||
|
class BufferSegments(object):
|
||||||
|
def __len__(self) -> int: ...
|
||||||
|
def __getitem__(self, i: int) -> BufferSegment: ...
|
||||||
|
|
||||||
|
class BufferWithSegments(object):
|
||||||
|
size: int
|
||||||
|
def __init__(self, data: ByteString, segments: ByteString): ...
|
||||||
|
def __len__(self) -> int: ...
|
||||||
|
def __getitem__(self, i: int) -> BufferSegment: ...
|
||||||
|
def segments(self): ...
|
||||||
|
def tobytes(self) -> bytes: ...
|
||||||
|
|
||||||
|
class BufferWithSegmentsCollection(object):
|
||||||
|
def __init__(self, *args): ...
|
||||||
|
def __len__(self) -> int: ...
|
||||||
|
def __getitem__(self, i: int) -> BufferSegment: ...
|
||||||
|
def size(self) -> int: ...
|
||||||
|
|
||||||
|
class ZstdCompressionParameters(object):
|
||||||
|
@staticmethod
|
||||||
|
def from_level(
|
||||||
|
level: int, source_size: int = ..., dict_size: int = ..., **kwargs
|
||||||
|
) -> "ZstdCompressionParameters": ...
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
format: int = ...,
|
||||||
|
compression_level: int = ...,
|
||||||
|
window_log: int = ...,
|
||||||
|
hash_log: int = ...,
|
||||||
|
chain_log: int = ...,
|
||||||
|
search_log: int = ...,
|
||||||
|
min_match: int = ...,
|
||||||
|
target_length: int = ...,
|
||||||
|
strategy: int = ...,
|
||||||
|
write_content_size: int = ...,
|
||||||
|
write_checksum: int = ...,
|
||||||
|
write_dict_id: int = ...,
|
||||||
|
job_size: int = ...,
|
||||||
|
overlap_log: int = ...,
|
||||||
|
force_max_window: int = ...,
|
||||||
|
enable_ldm: int = ...,
|
||||||
|
ldm_hash_log: int = ...,
|
||||||
|
ldm_min_match: int = ...,
|
||||||
|
ldm_bucket_size_log: int = ...,
|
||||||
|
ldm_hash_rate_log: int = ...,
|
||||||
|
threads: int = ...,
|
||||||
|
): ...
|
||||||
|
@property
|
||||||
|
def format(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def compression_level(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def window_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def hash_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def chain_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def search_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def min_match(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def target_length(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def strategy(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def write_content_size(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def write_checksum(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def write_dict_id(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def job_size(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def overlap_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def force_max_window(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def enable_ldm(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def ldm_hash_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def ldm_min_match(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def ldm_bucket_size_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def ldm_hash_rate_log(self) -> int: ...
|
||||||
|
@property
|
||||||
|
def threads(self) -> int: ...
|
||||||
|
def estimated_compression_context_size(self) -> int: ...
|
||||||
|
|
||||||
|
class CompressionParameters(ZstdCompressionParameters): ...
|
||||||
|
|
||||||
|
class ZstdCompressionDict(object):
|
||||||
|
k: int
|
||||||
|
d: int
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
data: ByteString,
|
||||||
|
dict_type: int = ...,
|
||||||
|
k: int = ...,
|
||||||
|
d: int = ...,
|
||||||
|
): ...
|
||||||
|
def __len__(self) -> int: ...
|
||||||
|
def dict_id(self) -> int: ...
|
||||||
|
def as_bytes(self) -> bytes: ...
|
||||||
|
def precompute_compress(
|
||||||
|
self,
|
||||||
|
level: int = ...,
|
||||||
|
compression_params: ZstdCompressionParameters = ...,
|
||||||
|
): ...
|
||||||
|
|
||||||
|
class ZstdCompressionObj(object):
|
||||||
|
def compress(self, data: ByteString) -> bytes: ...
|
||||||
|
def flush(self, flush_mode: int = ...) -> bytes: ...
|
||||||
|
|
||||||
|
class ZstdCompressionChunker(object):
|
||||||
|
def compress(self, data: ByteString): ...
|
||||||
|
def flush(self): ...
|
||||||
|
def finish(self): ...
|
||||||
|
|
||||||
|
class ZstdCompressionReader(BinaryIO):
|
||||||
|
def __enter__(self) -> "ZstdCompressionReader": ...
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb): ...
|
||||||
|
def readable(self) -> bool: ...
|
||||||
|
def writable(self) -> bool: ...
|
||||||
|
def seekable(self) -> bool: ...
|
||||||
|
def readline(self, limit: int = ...) -> bytes: ...
|
||||||
|
def readlines(self, hint: int = ...) -> List[bytes]: ...
|
||||||
|
def write(self, data: ByteString): ...
|
||||||
|
def writelines(self, data: Iterable[bytes]): ...
|
||||||
|
def isatty(self) -> bool: ...
|
||||||
|
def flush(self): ...
|
||||||
|
def close(self): ...
|
||||||
|
@property
|
||||||
|
def closed(self) -> bool: ...
|
||||||
|
def tell(self) -> int: ...
|
||||||
|
def readall(self) -> bytes: ...
|
||||||
|
def __iter__(self): ...
|
||||||
|
def __next__(self): ...
|
||||||
|
def next(self): ...
|
||||||
|
def read(self, size: int = ...) -> bytes: ...
|
||||||
|
def read1(self, size: int = ...) -> bytes: ...
|
||||||
|
def readinto(self, b) -> int: ...
|
||||||
|
def readinto1(self, b) -> int: ...
|
||||||
|
|
||||||
|
class ZstdCompressionWriter(BinaryIO):
|
||||||
|
def __enter__(self) -> "ZstdCompressionWriter": ...
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb): ...
|
||||||
|
def memory_size(self) -> int: ...
|
||||||
|
def fileno(self) -> int: ...
|
||||||
|
def close(self): ...
|
||||||
|
@property
|
||||||
|
def closed(self) -> bool: ...
|
||||||
|
def isatty(self) -> bool: ...
|
||||||
|
def readable(self) -> bool: ...
|
||||||
|
def readline(self, size: int = ...) -> bytes: ...
|
||||||
|
def readlines(self, hint: int = ...) -> List[bytes]: ...
|
||||||
|
def seek(self, offset: int, whence: int = ...): ...
|
||||||
|
def seekable(self) -> bool: ...
|
||||||
|
def truncate(self, size: int = ...): ...
|
||||||
|
def writable(self) -> bool: ...
|
||||||
|
def writelines(self, lines: Iterable[bytes]): ...
|
||||||
|
def read(self, size: int = ...) -> bytes: ...
|
||||||
|
def readall(self) -> bytes: ...
|
||||||
|
def readinto(self, b): ...
|
||||||
|
def write(self, data: ByteString) -> int: ...
|
||||||
|
def flush(self, flush_mode: int = ...) -> int: ...
|
||||||
|
def tell(self) -> int: ...
|
||||||
|
|
||||||
|
class ZstdCompressor(object):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
level: int = ...,
|
||||||
|
dict_data: Optional[ZstdCompressionDict] = ...,
|
||||||
|
compression_params: Optional[ZstdCompressionParameters] = ...,
|
||||||
|
write_checksum: Optional[bool] = ...,
|
||||||
|
write_content_size: Optional[bool] = ...,
|
||||||
|
write_dict_id: Optional[bool] = ...,
|
||||||
|
threads: int = ...,
|
||||||
|
): ...
|
||||||
|
def memory_size(self) -> int: ...
|
||||||
|
def compress(self, data: ByteString) -> bytes: ...
|
||||||
|
def compressobj(self, size: int = ...) -> ZstdCompressionObj: ...
|
||||||
|
def chunker(
|
||||||
|
self, size: int = ..., chunk_size: int = ...
|
||||||
|
) -> ZstdCompressionChunker: ...
|
||||||
|
def copy_stream(
|
||||||
|
self,
|
||||||
|
ifh: IO[bytes],
|
||||||
|
ofh: IO[bytes],
|
||||||
|
size: int = ...,
|
||||||
|
read_size: int = ...,
|
||||||
|
write_size: int = ...,
|
||||||
|
) -> Tuple[int, int]: ...
|
||||||
|
def stream_reader(
|
||||||
|
self,
|
||||||
|
source: Union[IO[bytes], ByteString],
|
||||||
|
size: int = ...,
|
||||||
|
read_size: int = ...,
|
||||||
|
*,
|
||||||
|
closefd: bool = ...,
|
||||||
|
) -> ZstdCompressionReader: ...
|
||||||
|
def stream_writer(
|
||||||
|
self,
|
||||||
|
writer: IO[bytes],
|
||||||
|
size: int = ...,
|
||||||
|
write_size: int = ...,
|
||||||
|
write_return_read: bool = ...,
|
||||||
|
*,
|
||||||
|
closefd: bool = ...,
|
||||||
|
) -> ZstdCompressionWriter: ...
|
||||||
|
def read_to_iter(
|
||||||
|
self,
|
||||||
|
reader: Union[IO[bytes], ByteString],
|
||||||
|
size: int = ...,
|
||||||
|
read_size: int = ...,
|
||||||
|
write_size: int = ...,
|
||||||
|
) -> Generator[bytes, None, None]: ...
|
||||||
|
def frame_progression(self) -> Tuple[int, int, int]: ...
|
||||||
|
def multi_compress_to_buffer(
|
||||||
|
self,
|
||||||
|
data: Union[
|
||||||
|
BufferWithSegments,
|
||||||
|
BufferWithSegmentsCollection,
|
||||||
|
List[ByteString],
|
||||||
|
],
|
||||||
|
threads: int = ...,
|
||||||
|
) -> BufferWithSegmentsCollection: ...
|
||||||
|
|
||||||
|
class ZstdDecompressionObj(object):
|
||||||
|
def decompress(self, data: ByteString) -> bytes: ...
|
||||||
|
def flush(self, length: int = ...) -> bytes: ...
|
||||||
|
@property
|
||||||
|
def unused_data(self) -> bytes: ...
|
||||||
|
@property
|
||||||
|
def unconsumed_tail(self) -> bytes: ...
|
||||||
|
@property
|
||||||
|
def eof(self) -> bool: ...
|
||||||
|
|
||||||
|
class ZstdDecompressionReader(BinaryIO):
|
||||||
|
def __enter__(self) -> "ZstdDecompressionReader": ...
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb): ...
|
||||||
|
def readable(self) -> bool: ...
|
||||||
|
def writable(self) -> bool: ...
|
||||||
|
def seekable(self) -> bool: ...
|
||||||
|
def readline(self, size: int = ...): ...
|
||||||
|
def readlines(self, hint: int = ...): ...
|
||||||
|
def write(self, data: ByteString): ...
|
||||||
|
def writelines(self, lines: Iterable[bytes]): ...
|
||||||
|
def isatty(self) -> bool: ...
|
||||||
|
def flush(self): ...
|
||||||
|
def close(self): ...
|
||||||
|
@property
|
||||||
|
def closed(self) -> bool: ...
|
||||||
|
def tell(self) -> int: ...
|
||||||
|
def readall(self) -> bytes: ...
|
||||||
|
def __iter__(self): ...
|
||||||
|
def __next__(self): ...
|
||||||
|
def next(self): ...
|
||||||
|
def read(self, size: int = ...) -> bytes: ...
|
||||||
|
def readinto(self, b) -> int: ...
|
||||||
|
def read1(self, size: int = ...) -> bytes: ...
|
||||||
|
def readinto1(self, b) -> int: ...
|
||||||
|
def seek(self, pos: int, whence: int = ...) -> int: ...
|
||||||
|
|
||||||
|
class ZstdDecompressionWriter(BinaryIO):
|
||||||
|
def __enter__(self) -> "ZstdDecompressionWriter": ...
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb): ...
|
||||||
|
def memory_size(self) -> int: ...
|
||||||
|
def close(self): ...
|
||||||
|
@property
|
||||||
|
def closed(self) -> bool: ...
|
||||||
|
def fileno(self) -> int: ...
|
||||||
|
def flush(self): ...
|
||||||
|
def isatty(self) -> bool: ...
|
||||||
|
def readable(self) -> bool: ...
|
||||||
|
def readline(self, size: int = ...): ...
|
||||||
|
def readlines(self, hint: int = ...): ...
|
||||||
|
def seek(self, offset: int, whence: int = ...): ...
|
||||||
|
def seekable(self) -> bool: ...
|
||||||
|
def tell(self): ...
|
||||||
|
def truncate(self, size: int = ...): ...
|
||||||
|
def writable(self) -> bool: ...
|
||||||
|
def writelines(self, lines: Iterable[bytes]): ...
|
||||||
|
def read(self, size: int = ...): ...
|
||||||
|
def readall(self): ...
|
||||||
|
def readinto(self, b): ...
|
||||||
|
def write(self, data: ByteString) -> int: ...
|
||||||
|
|
||||||
|
class ZstdDecompressor(object):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
dict_data: Optional[ZstdCompressionDict] = ...,
|
||||||
|
max_window_size: int = ...,
|
||||||
|
format: int = ...,
|
||||||
|
): ...
|
||||||
|
def memory_size(self) -> int: ...
|
||||||
|
def decompress(
|
||||||
|
self,
|
||||||
|
data: ByteString,
|
||||||
|
max_output_size: int = ...,
|
||||||
|
read_across_frames: bool = ...,
|
||||||
|
allow_extra_data: bool = ...,
|
||||||
|
) -> bytes: ...
|
||||||
|
def stream_reader(
|
||||||
|
self,
|
||||||
|
source: Union[IO[bytes], ByteString],
|
||||||
|
read_size: int = ...,
|
||||||
|
read_across_frames: bool = ...,
|
||||||
|
*,
|
||||||
|
closefd=False,
|
||||||
|
) -> ZstdDecompressionReader: ...
|
||||||
|
def decompressobj(
|
||||||
|
self, write_size: int = ..., read_across_frames: bool = False
|
||||||
|
) -> ZstdDecompressionObj: ...
|
||||||
|
def read_to_iter(
|
||||||
|
self,
|
||||||
|
reader: Union[IO[bytes], ByteString],
|
||||||
|
read_size: int = ...,
|
||||||
|
write_size: int = ...,
|
||||||
|
skip_bytes: int = ...,
|
||||||
|
) -> Generator[bytes, None, None]: ...
|
||||||
|
def stream_writer(
|
||||||
|
self,
|
||||||
|
writer: IO[bytes],
|
||||||
|
write_size: int = ...,
|
||||||
|
write_return_read: bool = ...,
|
||||||
|
*,
|
||||||
|
closefd: bool = ...,
|
||||||
|
) -> ZstdDecompressionWriter: ...
|
||||||
|
def copy_stream(
|
||||||
|
self,
|
||||||
|
ifh: IO[bytes],
|
||||||
|
ofh: IO[bytes],
|
||||||
|
read_size: int = ...,
|
||||||
|
write_size: int = ...,
|
||||||
|
) -> Tuple[int, int]: ...
|
||||||
|
def decompress_content_dict_chain(
|
||||||
|
self, frames: list[ByteString]
|
||||||
|
) -> bytes: ...
|
||||||
|
def multi_decompress_to_buffer(
|
||||||
|
self,
|
||||||
|
frames: Union[
|
||||||
|
BufferWithSegments,
|
||||||
|
BufferWithSegmentsCollection,
|
||||||
|
List[ByteString],
|
||||||
|
],
|
||||||
|
decompressed_sizes: ByteString = ...,
|
||||||
|
threads: int = ...,
|
||||||
|
) -> BufferWithSegmentsCollection: ...
|
||||||
|
|
||||||
|
class FrameParameters(object):
|
||||||
|
content_size: int
|
||||||
|
window_size: int
|
||||||
|
dict_id: int
|
||||||
|
has_checksum: bool
|
||||||
|
|
||||||
|
def estimate_decompression_context_size() -> int: ...
|
||||||
|
def frame_content_size(data: ByteString) -> int: ...
|
||||||
|
def frame_header_size(data: ByteString) -> int: ...
|
||||||
|
def get_frame_parameters(
|
||||||
|
data: ByteString, format: Optional[int] = None
|
||||||
|
) -> FrameParameters: ...
|
||||||
|
def train_dictionary(
|
||||||
|
dict_size: int,
|
||||||
|
samples: list[ByteString],
|
||||||
|
k: int = ...,
|
||||||
|
d: int = ...,
|
||||||
|
f: int = ...,
|
||||||
|
split_point: float = ...,
|
||||||
|
accel: int = ...,
|
||||||
|
notifications: int = ...,
|
||||||
|
dict_id: int = ...,
|
||||||
|
level: int = ...,
|
||||||
|
steps: int = ...,
|
||||||
|
threads: int = ...,
|
||||||
|
) -> ZstdCompressionDict: ...
|
||||||
|
def open(
|
||||||
|
filename: Union[bytes, str, os.PathLike, BinaryIO],
|
||||||
|
mode: str = ...,
|
||||||
|
cctx: Optional[ZstdCompressor] = ...,
|
||||||
|
dctx: Optional[ZstdDecompressor] = ...,
|
||||||
|
encoding: Optional[str] = ...,
|
||||||
|
errors: Optional[str] = ...,
|
||||||
|
newline: Optional[str] = ...,
|
||||||
|
closefd: bool = ...,
|
||||||
|
): ...
|
||||||
|
def compress(data: ByteString, level: int = ...) -> bytes: ...
|
||||||
|
def decompress(data: ByteString, max_output_size: int = ...) -> bytes: ...
|
||||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+4478
File diff suppressed because it is too large
Load Diff
@@ -21,15 +21,15 @@ export const CODEX_LINK_PORT = 8795
|
|||||||
export const CODEX_LINK_ORIGIN = `http://127.0.0.1:${CODEX_LINK_PORT}`
|
export const CODEX_LINK_ORIGIN = `http://127.0.0.1:${CODEX_LINK_PORT}`
|
||||||
export const CODEX_LINK_STATUS_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama/status`
|
export const CODEX_LINK_STATUS_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama/status`
|
||||||
export const CODEX_LINK_STOP_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama/stop`
|
export const CODEX_LINK_STOP_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama/stop`
|
||||||
|
export const CODEX_LINK_REFRESH_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama/refresh`
|
||||||
|
|
||||||
/** The trailing comment that marks the line as ours. */
|
/** The trailing comment that marks the line as ours. */
|
||||||
export const CODEX_LINK_MARKER = '# AITURK IDE Codex Baglama'
|
export const CODEX_LINK_MARKER = '# AITURK IDE Codex Baglama'
|
||||||
// Must equal SURUM in assets/codex-bridge/bridge.py; a running bridge that reports
|
// Must equal SURUM in assets/codex-bridge/bridge.py; a running bridge that reports
|
||||||
// another version is restarted so an updated IDE never keeps an old bridge alive.
|
// another version is restarted so an updated IDE never keeps an old bridge alive.
|
||||||
export const CODEX_BRIDGE_VERSION = '1'
|
export const CODEX_BRIDGE_VERSION = '4'
|
||||||
export const CODEX_LINK_LINE = `openai_base_url = "${CODEX_LINK_ORIGIN}/backend-api/codex" ${CODEX_LINK_MARKER}`
|
export const CODEX_LINK_LINE = `openai_base_url = "${CODEX_LINK_ORIGIN}/backend-api/codex" ${CODEX_LINK_MARKER}`
|
||||||
|
|
||||||
export const CODEX_LINK_BACKUP_SUFFIX = '.aiturk-yedek'
|
|
||||||
export const CODEX_LINK_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
|
export const CODEX_LINK_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
|
||||||
export const CODEX_LINK_RUN_VALUE = 'AITURK-IDE-Codex-Baglama'
|
export const CODEX_LINK_RUN_VALUE = 'AITURK-IDE-Codex-Baglama'
|
||||||
export const CODEX_LINK_LAUNCHER_NAME = 'baslat.pyw'
|
export const CODEX_LINK_LAUNCHER_NAME = 'baslat.pyw'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'
|
|||||||
import { CODEX_BRIDGE_START_BUDGET_MS, type CodexLinkDeps, type CodexLinkPaths, createCodexLink } from './codex-link'
|
import { CODEX_BRIDGE_START_BUDGET_MS, type CodexLinkDeps, type CodexLinkPaths, createCodexLink } from './codex-link'
|
||||||
import {
|
import {
|
||||||
CODEX_LINK_LINE,
|
CODEX_LINK_LINE,
|
||||||
|
CODEX_LINK_REFRESH_URL,
|
||||||
CODEX_LINK_STATUS_URL,
|
CODEX_LINK_STATUS_URL,
|
||||||
CODEX_LINK_STOP_URL,
|
CODEX_LINK_STOP_URL,
|
||||||
codexBridgeArgs,
|
codexBridgeArgs,
|
||||||
@@ -20,8 +21,7 @@ const PATHS: CodexLinkPaths = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BRIDGE_JSON = `${PATHS.bridgeHome}\\bridge.json`
|
const BRIDGE_JSON = `${PATHS.bridgeHome}\\bridge.json`
|
||||||
const BACKUP = `${PATHS.codexConfig}.aiturk-yedek`
|
const STATUS_OK = { ok: true, surum: '4', ide_durum: 'tamam', ide_model: 7, ozet: true }
|
||||||
const STATUS_OK = { ok: true, surum: '1', ide_durum: 'tamam', ide_model: 7, ozet: true }
|
|
||||||
const USER_CONFIG = 'model = "gpt-6"\r\n\r\n[features]\r\nweb = true\r\n'
|
const USER_CONFIG = 'model = "gpt-6"\r\n\r\n[features]\r\nweb = true\r\n'
|
||||||
|
|
||||||
interface World {
|
interface World {
|
||||||
@@ -72,12 +72,6 @@ function createWorld(patch: Partial<World> & { files?: Map<string, string> } = {
|
|||||||
world.events.push(`write ${file}`)
|
world.events.push(`write ${file}`)
|
||||||
world.files.set(file, text)
|
world.files.set(file, text)
|
||||||
},
|
},
|
||||||
backupOnce: (source, target) => {
|
|
||||||
if (!world.files.has(target)) {
|
|
||||||
world.events.push(`backup ${target}`)
|
|
||||||
world.files.set(target, world.files.get(source) ?? '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
spawnBridge: (command, args, env) => {
|
spawnBridge: (command, args, env) => {
|
||||||
world.events.push(`spawn ${command} ${JSON.stringify(args)} ${JSON.stringify(env)}`)
|
world.events.push(`spawn ${command} ${JSON.stringify(args)} ${JSON.stringify(env)}`)
|
||||||
spawned = true
|
spawned = true
|
||||||
@@ -103,6 +97,12 @@ function createWorld(patch: Partial<World> & { files?: Map<string, string> } = {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (method === 'POST' && url === CODEX_LINK_REFRESH_URL) {
|
||||||
|
world.events.push('refresh')
|
||||||
|
|
||||||
|
return world.listening ? STATUS_OK : null
|
||||||
|
}
|
||||||
|
|
||||||
if (method === 'POST' && url === CODEX_LINK_STOP_URL) {
|
if (method === 'POST' && url === CODEX_LINK_STOP_URL) {
|
||||||
world.events.push('stop')
|
world.events.push('stop')
|
||||||
|
|
||||||
@@ -205,19 +205,17 @@ describe('applyCodexLink', () => {
|
|||||||
hermesHome: PATHS.hermesHome
|
hermesHome: PATHS.hermesHome
|
||||||
})
|
})
|
||||||
|
|
||||||
// bridge.json → start → verified answer → config.toml (backed up) → login item
|
// bridge.json → start → verified answer → config.toml → login item
|
||||||
expect(world.events).toEqual([
|
expect(world.events).toEqual([
|
||||||
`write ${BRIDGE_JSON}`,
|
`write ${BRIDGE_JSON}`,
|
||||||
`spawn ${PATHS.pythonw} ${JSON.stringify(args)} ${JSON.stringify(env)}`,
|
`spawn ${PATHS.pythonw} ${JSON.stringify(args)} ${JSON.stringify(env)}`,
|
||||||
'status ok',
|
'status ok',
|
||||||
`backup ${BACKUP}`,
|
|
||||||
`write ${PATHS.codexConfig}`,
|
`write ${PATHS.codexConfig}`,
|
||||||
'autostart set'
|
'autostart set'
|
||||||
])
|
])
|
||||||
|
|
||||||
expect(JSON.parse(world.files.get(BRIDGE_JSON) ?? '')).toEqual(codexBridgeConfig())
|
expect(JSON.parse(world.files.get(BRIDGE_JSON) ?? '')).toEqual(codexBridgeConfig())
|
||||||
expect(world.files.get(PATHS.codexConfig)).toBe(`${CODEX_LINK_LINE}\r\n${USER_CONFIG}`)
|
expect(world.files.get(PATHS.codexConfig)).toBe(`${CODEX_LINK_LINE}\r\n${USER_CONFIG}`)
|
||||||
expect(world.files.get(BACKUP)).toBe(USER_CONFIG)
|
|
||||||
expect(world.autostart).toBe(
|
expect(world.autostart).toBe(
|
||||||
`"${PATHS.pythonw}" "${PATHS.bridgeScript}" --home "${PATHS.bridgeHome}" --hermes-home "${PATHS.hermesHome}"`
|
`"${PATHS.pythonw}" "${PATHS.bridgeScript}" --home "${PATHS.bridgeHome}" --hermes-home "${PATHS.hermesHome}"`
|
||||||
)
|
)
|
||||||
@@ -231,7 +229,6 @@ describe('applyCodexLink', () => {
|
|||||||
expect(status).toMatchObject({ enabled: true, bridgeRunning: false, lastError: 'bridge-start-failed' })
|
expect(status).toMatchObject({ enabled: true, bridgeRunning: false, lastError: 'bridge-start-failed' })
|
||||||
expect(spawns(world)).toHaveLength(1)
|
expect(spawns(world)).toHaveLength(1)
|
||||||
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
||||||
expect(world.files.has(BACKUP)).toBe(false)
|
|
||||||
expect(world.autostart).toBeNull()
|
expect(world.autostart).toBeNull()
|
||||||
expect(writes(world)).toEqual([`write ${BRIDGE_JSON}`])
|
expect(writes(world)).toEqual([`write ${BRIDGE_JSON}`])
|
||||||
// The start poll is bounded by its one budget.
|
// The start poll is bounded by its one budget.
|
||||||
@@ -273,13 +270,13 @@ describe('applyCodexLink', () => {
|
|||||||
expect(world.files.get(PATHS.codexConfig)).toBe(`${CODEX_LINK_LINE}\r\n${USER_CONFIG}`)
|
expect(world.files.get(PATHS.codexConfig)).toBe(`${CODEX_LINK_LINE}\r\n${USER_CONFIG}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('never overwrites the first backup', async () => {
|
it('asks a running bridge to re-read the IDE session on every apply', async () => {
|
||||||
const { link, world } = createWorld({ listening: true })
|
const { link, world } = createWorld({ listening: true })
|
||||||
world.files.set(BACKUP, 'the original original')
|
|
||||||
|
|
||||||
await link.apply()
|
await link.apply({ sync: true })
|
||||||
|
|
||||||
expect(world.files.get(BACKUP)).toBe('the original original')
|
expect(world.events).toContain('refresh')
|
||||||
|
expect(spawns(world)).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('falls back to python.exe when pythonw.exe is missing', async () => {
|
it('falls back to python.exe when pythonw.exe is missing', async () => {
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import path from 'node:path'
|
|||||||
import {
|
import {
|
||||||
addCodexLinkLine,
|
addCodexLinkLine,
|
||||||
CODEX_BRIDGE_VERSION,
|
CODEX_BRIDGE_VERSION,
|
||||||
CODEX_LINK_BACKUP_SUFFIX,
|
|
||||||
CODEX_LINK_LAUNCHER_NAME,
|
CODEX_LINK_LAUNCHER_NAME,
|
||||||
|
CODEX_LINK_REFRESH_URL,
|
||||||
CODEX_LINK_RUN_KEY,
|
CODEX_LINK_RUN_KEY,
|
||||||
CODEX_LINK_RUN_VALUE,
|
CODEX_LINK_RUN_VALUE,
|
||||||
CODEX_LINK_STATUS_URL,
|
CODEX_LINK_STATUS_URL,
|
||||||
@@ -66,8 +66,6 @@ export interface CodexLinkDeps {
|
|||||||
readText: (file: string) => null | string
|
readText: (file: string) => null | string
|
||||||
/** tmp + rename; creates the parent folder. */
|
/** tmp + rename; creates the parent folder. */
|
||||||
writeTextAtomic: (file: string, text: string) => void
|
writeTextAtomic: (file: string, text: string) => void
|
||||||
/** Copy `source` to `target` unless `target` already exists. */
|
|
||||||
backupOnce: (source: string, target: string) => void
|
|
||||||
/** Start the bridge detached and hidden; it must outlive the IDE. */
|
/** Start the bridge detached and hidden; it must outlive the IDE. */
|
||||||
spawnBridge: (command: string, args: string[], env: Record<string, string>) => void
|
spawnBridge: (command: string, args: string[], env: Record<string, string>) => void
|
||||||
/** Parsed JSON body, or null when nothing answered within `budgetMs`. */
|
/** Parsed JSON body, or null when nothing answered within `budgetMs`. */
|
||||||
@@ -306,7 +304,17 @@ export function createCodexLink(deps: CodexLinkDeps): CodexLink {
|
|||||||
bridge = NOT_RUNNING
|
bridge = NOT_RUNNING
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bridge.running) {
|
if (bridge.running) {
|
||||||
|
// Already up: have it re-read the IDE session and model list now (a sign-in
|
||||||
|
// since its last periodic refresh would otherwise show up minutes later).
|
||||||
|
try {
|
||||||
|
const refreshed = readProbe(await deps.request('POST', CODEX_LINK_REFRESH_URL, CODEX_BRIDGE_START_BUDGET_MS))
|
||||||
|
|
||||||
|
bridge = refreshed.running ? refreshed : bridge
|
||||||
|
} catch {
|
||||||
|
// The status it already gave stays valid.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
bridge = await startBridge(python)
|
bridge = await startBridge(python)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +331,6 @@ export function createCodexLink(deps: CodexLinkDeps): CodexLink {
|
|||||||
|
|
||||||
if (edit.changed) {
|
if (edit.changed) {
|
||||||
try {
|
try {
|
||||||
deps.backupOnce(paths.codexConfig, `${paths.codexConfig}${CODEX_LINK_BACKUP_SUFFIX}`)
|
|
||||||
deps.writeTextAtomic(paths.codexConfig, edit.text)
|
deps.writeTextAtomic(paths.codexConfig, edit.text)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
fail('config-write-failed', error)
|
fail('config-write-failed', error)
|
||||||
@@ -461,11 +468,6 @@ export function createNodeCodexLinkDeps(paths: CodexLinkPaths, log: (message: st
|
|||||||
exists: file => fs.existsSync(file),
|
exists: file => fs.existsSync(file),
|
||||||
readText: readTextLossless,
|
readText: readTextLossless,
|
||||||
writeTextAtomic,
|
writeTextAtomic,
|
||||||
backupOnce: (source, target) => {
|
|
||||||
if (!fs.existsSync(target)) {
|
|
||||||
fs.copyFileSync(source, target, fs.constants.COPYFILE_EXCL)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
spawnBridge: (command, args, env) => {
|
spawnBridge: (command, args, env) => {
|
||||||
fs.mkdirSync(paths.bridgeHome, { recursive: true })
|
fs.mkdirSync(paths.bridgeHome, { recursive: true })
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "aiturk-ide",
|
"name": "aiturk-ide",
|
||||||
"productName": "AITURK IDE",
|
"productName": "AITURK IDE",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0-beta.21",
|
"version": "1.0.0-beta.23",
|
||||||
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
|
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
|
||||||
"author": "AITURK / TurkServis",
|
"author": "AITURK / TurkServis",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -304,7 +304,9 @@
|
|||||||
"from": "assets/codex-bridge",
|
"from": "assets/codex-bridge",
|
||||||
"to": "codex-bridge",
|
"to": "codex-bridge",
|
||||||
"filter": [
|
"filter": [
|
||||||
"*.py",
|
"**/*.py",
|
||||||
|
"**/*.pyd",
|
||||||
|
"**/LICENSE",
|
||||||
"!test_*.py"
|
"!test_*.py"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1
-1
@@ -65,7 +65,7 @@
|
|||||||
},
|
},
|
||||||
"apps/desktop": {
|
"apps/desktop": {
|
||||||
"name": "aiturk-ide",
|
"name": "aiturk-ide",
|
||||||
"version": "1.0.0-beta.21",
|
"version": "1.0.0-beta.23",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@assistant-ui/core": "0.2.23",
|
"@assistant-ui/core": "0.2.23",
|
||||||
|
|||||||
Reference in New Issue
Block a user