Compare commits

..
Author SHA1 Message Date
yilsemandClaude Opus 5.5 b347abe1f6 release(ide): ship beta.46 with original subscription WebSocket
Server CI and Windows package / verify (push) In progress
Bridge version 25 so the installed IDE replaces the running splitter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeNgWNzPjzUKQuS32RUYXx
2026-10-20 22:38:51 +03:00
yilsemandClaude Opus 5.5 be14cf2923 fix(codex-link): tunnel subscription WebSocket to chatgpt.com unchanged
Server CI and Windows package / windows-package (push) Successful in 10m51s
Server CI and Windows package / verify (push) Successful in 6m28s
Codex Baglama rejected every Responses WebSocket with 426, so Codex's own
subscription models fell back to HTTP and re-sent the whole conversation on
every step. The splitter now tunnels the WebSocket of Codex's own models to
chatgpt.com byte for byte (model known from x-codex-routing-hint). Added
models, remembered transfers and transfer history in a message still get
426 / a close frame and stay on the existing HTTP path, so added-model and
transition handling is unchanged. bridge.json "abonelik_websocket": false
restores the old refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeNgWNzPjzUKQuS32RUYXx
2026-10-20 22:17:57 +03:00
6 changed files with 402 additions and 11 deletions

No files matched your search

@@ -27,6 +27,17 @@ Run: `python bridge.py --home <data dir> --hermes-home <agent home>`. The IDE wr
Tests: `python -m unittest discover` in this directory.
## Version 25: subscription WebSocket stays original (IDE beta.46)
User decision (20 October 2026): "Codex orjinal aynen kalacak, sadece ek combo ve model ekleyeceğiz."
Codex opens a Responses WebSocket for its own models. The splitter used to refuse it with 426, so
subscription chats fell back to HTTP and re-sent the whole conversation on every step. Now:
- Codex's own model (`x-codex-routing-hint: model=...`): the WebSocket is tunnelled to chatgpt.com byte for byte.
- Added model, remembered transfer, or transfer history inside a message: 426 / close frame; that chat stays
on the existing HTTP path for the life of the Codex process, so added-model and transition handling is unchanged.
- `bridge.json` `"abonelik_websocket": false` restores the old refusal.
## Version 24: portable model switches and summary coverage (IDE beta.45)
Cross-provider requests replace encrypted reasoning and every typed encrypted
+223 -7
View File
@@ -11,6 +11,8 @@ Codex'te tek adres ayari (`openai_base_url`) oldugu icin butun istekler buraya g
(ide/..., 9r/..., model_map) o surece HIC gitmez.
* Model listesi -> kopru ayaktaysa ek modelleri eklenmis liste; degilse chatgpt.com'un listesi.
* Geri kalan her sey -> chatgpt.com'a, oldugu gibi.
* Surekli baglanti (WebSocket) -> Codex'in kendi modeliyse chatgpt.com'a bayt bayt tunel (Codex'in ozgun hizli yolu).
Ek model ya da gecis gecmisi gorulurse o sohbet 426 ile HTTP'ye birakilir; ek model/gecis mantigi HTTP'de kalir.
Karisik gecmiste yerel aktarim hazirligi inference'dan ayridir. Abonelik
inference'i her zaman CHATGPT'ye; ek model inference'i KOPRU'ye gider.
@@ -21,6 +23,11 @@ Yerel denetim: GET /aiturk/codex-baglama/status, POST .../stop, POST .../refresh
"""
import gzip
import hashlib
import socket
import ssl
import struct
import urllib.parse
import zlib
import http.client
import http.server
import json
@@ -35,7 +42,7 @@ import gecis_context
import terminal_stream
HERE = os.path.dirname(os.path.abspath(__file__))
SURUM = "24" # AITURK IDE'nin bekledigi kopru surumu (codex-link-config.ts CODEX_BRIDGE_VERSION)
SURUM = "25" # AITURK IDE'nin bekledigi kopru surumu (codex-link-config.ts CODEX_BRIDGE_VERSION)
try:
with open(os.path.abspath(__file__), "rb") as _f:
KOD = hashlib.sha256(_f.read()).hexdigest()[:8]
@@ -160,6 +167,88 @@ def model_oku(govde, kodlama):
return None
# --- Surekli baglanti (WebSocket) ---------------------------------------------------------------------------
# Kullanici karari (20 Ekim 2026): "Codex orjinal aynen kalacakti, sadece ek combo ve model ekleyecektik."
# Codex abonelik modelinde chatgpt.com'a WebSocket acar; reddedilirse HTTP'ye duser ve her adimda butun sohbeti
# yeniden gonderir. Abonelik sohbeti bu yuzden chatgpt.com'a dokunulmadan tunellenir. Ek model ya da gecis
# gecmisi olan sohbet ilk anda 426 alir ve HTTP'de kalir (bugunku ek model/gecis davranisi aynen).
WS_HTTP = {} # session-id -> zaman: bu Codex surecinde HTTP'de kalacak sohbetler
ws_kilit = threading.Lock()
def ws_ipucu_model(baslik):
"""Codex'in el sikismadaki model ipucu (x-codex-routing-hint: model=gpt-6.1-sol)."""
for parca in (baslik or "").replace(",", ";").split(";"):
k, _, v = parca.partition("=")
if k.strip().lower() == "model":
return v.strip()
return ""
def ws_http_isaretle(sid):
if not sid:
return
with ws_kilit:
WS_HTTP[sid] = time.time()
while len(WS_HTTP) > 4096:
del WS_HTTP[next(iter(WS_HTTP))]
def ws_cerceve_oku(oku):
"""Tek cerceve: (ham_bayt, fin, rsv1, opcode, acik_yuk) ya da baglanti bittiyse None."""
bas = oku(2)
if len(bas) < 2:
return None
b0, b1 = bas[0], bas[1]
n, ek = b1 & 0x7F, b""
if n == 126:
ek = oku(2)
if len(ek) < 2:
return None
n = struct.unpack(">H", ek)[0]
elif n == 127:
ek = oku(8)
if len(ek) < 8:
return None
n = struct.unpack(">Q", ek)[0]
maske = oku(4) if b1 & 0x80 else b""
if b1 & 0x80 and len(maske) < 4:
return None
yuk = oku(n) if n else b""
if len(yuk) < n:
return None
ham = bas + ek + maske + yuk
if maske and n:
m = (maske * (n // 4 + 1))[:n]
yuk = (int.from_bytes(yuk, "big") ^ int.from_bytes(m, "big")).to_bytes(n, "big")
return ham, bool(b0 & 0x80), bool(b0 & 0x40), b0 & 0x0F, yuk
def ws_kapat_cercevesi(kod=1011, neden="AITURK: HTTP yoluna geciliyor"):
yuk = struct.pack(">H", kod) + neden.encode("utf-8")[:120]
return bytes([0x88, len(yuk)]) + yuk
def ws_mesaj_engeli(veri, hesap=""):
"""Bu mesaj chatgpt.com'a tunelden gidemez mi? Neden metni ya da None (gidebilir)."""
try:
m = json.loads(veri)
except (ValueError, UnicodeDecodeError):
return None
if not isinstance(m, dict) or m.get("type") != "response.create":
return None
if ek_model_mu(m.get("model")):
return "ek model"
if gecis_context.foreign(m) or gecis_context.foreign_history(m):
return "gecis gecmisi"
thread = str(m.get("prompt_cache_key") or "")
if thread:
scope = hashlib.sha256((str(hesap or "") + "|" + thread).encode()).hexdigest()
if ek_model_mu(TRANSFERS.get(scope) or ""):
return "gecis gecmisi"
return None
ISTEK_ATLA = {"host", "content-length", "accept-encoding", "transfer-encoding", "connection"}
YANIT_ATLA = {"host", "content-length", "accept-encoding", "transfer-encoding", "connection", "content-encoding"}
@@ -491,6 +580,138 @@ class H(http.server.BaseHTTPRequestHandler):
return None, True
return result["value"], started
def _ws_ret(self, n, neden):
"""426: Codex bekletmeden HTTP yoluna duser (ek model ve gecisler bu yolda islenir)."""
log(f"[{n} WS] HTTP yoluna birakildi: {neden}")
self.send_response(426)
self.send_header("Content-Length", "0")
self.send_header("Connection", "close")
self.end_headers()
self.close_connection = True
def _ws(self, n):
model = ws_ipucu_model(self.headers.get("x-codex-routing-hint"))
sid = self.headers.get("session-id") or self.headers.get("thread-id") or ""
hesap = self.headers.get("chatgpt-account-id") or ""
thread = self.headers.get("thread-id") or sid
scope = hashlib.sha256((str(hesap) + "|" + thread).encode()).hexdigest() if thread else ""
if CFG.get("abonelik_websocket") is False:
return self._ws_ret(n, "ayarla kapali")
if not self.path.split("?", 1)[0].rstrip("/").endswith("/responses"):
return self._ws_ret(n, "yol")
if not model:
return self._ws_ret(n, "model ipucu yok")
if ek_model_mu(model):
return self._ws_ret(n, f"ek model {model}")
with ws_kilit:
http_de = sid in WS_HTTP
if http_de:
return self._ws_ret(n, "sohbet HTTP yolunda")
if scope and ek_model_mu(TRANSFERS.get(scope) or ""):
return self._ws_ret(n, "gecis gecmisi")
u = urllib.parse.urlsplit(CHATGPT)
guvenli = u.scheme == "https"
try:
ust = socket.create_connection((u.hostname, u.port or (443 if guvenli else 80)), timeout=20)
if guvenli:
ust = ssl.create_default_context().wrap_socket(ust, server_hostname=u.hostname)
satirlar = [f"GET {self.path} HTTP/1.1", f"Host: {u.netloc}"]
satirlar += [f"{k}: {v}" for k, v in self.headers.items() if k.lower() != "host"]
ust.sendall(("\r\n".join(satirlar) + "\r\n\r\n").encode("latin-1"))
cevap = b""
while b"\r\n\r\n" not in cevap:
parca = ust.recv(65536)
if not parca or len(cevap) > 65536:
raise OSError("el sikisma cevabi eksik")
cevap += parca
bas, _, artik = cevap.partition(b"\r\n\r\n")
durum = int(bas.split(b" ", 2)[1])
except (OSError, ValueError, IndexError) as e:
return self._ws_ret(n, f"chatgpt.com acilamadi: {type(e).__name__}: {e}")
if durum != 101:
ust.close()
return self._ws_ret(n, f"chatgpt.com {durum}")
uzanti = ""
for satir in bas.split(b"\r\n")[1:]:
k, _, v = satir.decode("latin-1").partition(":")
if k.strip().lower() == "sec-websocket-extensions":
uzanti = v.strip().lower()
sikistirma = "permessage-deflate" in uzanti
baglamsiz = "client_no_context_takeover" in uzanti
self.close_connection = True
yaz_kilit = threading.Lock()
bitti = threading.Event()
ust.settimeout(None)
self.connection.settimeout(None)
def istemciye(veri):
with yaz_kilit:
self.wfile.write(veri)
self.wfile.flush()
def kapat():
bitti.set()
for soket in (ust, self.connection):
try:
soket.shutdown(socket.SHUT_RDWR)
except OSError:
pass
istemciye(bas + b"\r\n\r\n" + artik)
log(f"[{n} WS {model}] chatgpt.com'a dogrudan tunel acildi")
def asagi():
try:
while True:
parca = ust.recv(65536)
if not parca:
break
istemciye(parca)
except OSError:
pass
finally:
kapat()
threading.Thread(target=asagi, daemon=True).start()
acici = zlib.decompressobj(-zlib.MAX_WBITS) if sikistirma else None
ham_parcalar, yukler, sikisik, mesaj = [], [], False, 0
try:
while not bitti.is_set():
c = ws_cerceve_oku(self.rfile.read)
if c is None:
break
ham, fin, rsv1, op, yuk = c
if op >= 8:
ust.sendall(ham)
continue
if op in (1, 2):
ham_parcalar, yukler, sikisik = [ham], [yuk], rsv1
else:
ham_parcalar.append(ham)
yukler.append(yuk)
if not fin:
continue
veri = b"".join(yukler)
if sikisik and acici is not None:
if baglamsiz:
acici = zlib.decompressobj(-zlib.MAX_WBITS)
veri = acici.decompress(veri + b"\x00\x00\xff\xff")
neden = ws_mesaj_engeli(veri, hesap)
if neden:
ws_http_isaretle(sid)
log(f"[{n} WS {model}] {neden}: sohbet HTTP yoluna aktarildi ({mesaj} mesaj tunelden gitti)")
istemciye(ws_kapat_cercevesi())
break
ust.sendall(b"".join(ham_parcalar))
ham_parcalar, yukler = [], []
mesaj += 1
except (OSError, zlib.error) as e:
log(f"[{n} WS {model}] baglanti hatasi: {type(e).__name__}: {e}")
finally:
kapat()
ust.close()
log(f"[{n} WS {model}] kapandi, {mesaj} mesaj")
def _isle(self):
self._native_return_scope = None
self._failure_key = None
@@ -498,12 +719,7 @@ class H(http.server.BaseHTTPRequestHandler):
sira[0] += 1
n = sira[0]
if "websocket" in (self.headers.get("Upgrade") or "").lower():
# Bugunku calisan durum: surekli acik baglanti reddedilir, Codex olagan yola duser.
self.send_response(426)
self.send_header("Content-Length", "0")
self.send_header("Connection", "close")
self.end_headers()
return
return self._ws(n)
govde = self._oku()
yol = self.path
if yol.startswith("/aiturk/codex-baglama/"):
@@ -6,10 +6,13 @@ import hashlib
import http.client
import http.server
import json
import socket
import struct
import tempfile
import threading
import time
import unittest
import zlib
import bridge
@@ -57,6 +60,70 @@ def zstd(veri):
return zstandard.ZstdCompressor().compress(veri)
def ws_cerceve(veri, op=1, rsv1=False, maske=b"\x01\x02\x03\x04"):
"""Istemci (Codex) cercevesi: maskeli."""
b0 = 0x80 | (0x40 if rsv1 else 0) | op
n = len(veri)
bas = bytes([b0, 0x80 | n]) if n < 126 else bytes([b0, 0x80 | 126]) + struct.pack(">H", n) if n < 65536 \
else bytes([b0, 0x80 | 127]) + struct.pack(">Q", n)
return bas + maske + bytes(c ^ maske[i % 4] for i, c in enumerate(veri))
def sikistir(sik, veri):
return (sik.compress(veri) + sik.flush(zlib.Z_SYNC_FLUSH))[:-4]
class SahteWs:
"""Sahte chatgpt.com WebSocket ucu: el sikismayi ve gelen ham cerceveleri kaydeder, her mesaja ozetle cevap verir."""
def __init__(self, durum=101, uzanti=""):
self.durum, self.uzanti, self.ham, self.baslik, self.baglanti = durum, uzanti, [], {}, 0
self.dinle = socket.socket()
self.dinle.bind(("127.0.0.1", 0))
self.dinle.listen(8)
self.port = self.dinle.getsockname()[1]
threading.Thread(target=self._kabul, daemon=True).start()
def _kabul(self):
while True:
try:
s, _ = self.dinle.accept()
except OSError:
return
self.baglanti += 1
threading.Thread(target=self._bag, args=(s,), daemon=True).start()
def _bag(self, s):
f = s.makefile("rb")
satirlar = []
while True:
satir = f.readline().decode("latin-1").rstrip("\r\n")
if not satir:
break
satirlar.append(satir)
for satir in satirlar[1:]:
k, _, v = satir.partition(":")
self.baslik[k.strip().lower()] = v.strip()
if self.durum != 101:
s.sendall(b"HTTP/1.1 %d Hayir\r\nContent-Length: 0\r\n\r\n" % self.durum)
s.close()
return
cevap = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n"
if self.uzanti:
cevap += f"Sec-WebSocket-Extensions: {self.uzanti}\r\n"
s.sendall((cevap + "\r\n").encode())
ac = zlib.decompressobj(-zlib.MAX_WBITS)
while True:
c = bridge.ws_cerceve_oku(f.read)
if c is None or c[3] == 8:
break
self.ham.append(c[0])
veri = ac.decompress(c[4] + b"\x00\x00\xff\xff") if c[2] else c[4]
yuk = b"yanit:" + hashlib.sha256(veri).hexdigest().encode()
s.sendall(bytes([0x81, len(yuk)]) + yuk)
s.close()
class Ayirici(unittest.TestCase):
def setUp(self):
self.ozgun = sunucu(duz(200, b'event: response.completed\ndata: {"ok":1}\n\n', "text/event-stream"))
@@ -72,6 +139,7 @@ class Ayirici(unittest.TestCase):
self.transfer_old = bridge.TRANSFER_PATH, dict(bridge.TRANSFERS)
bridge.TRANSFER_PATH = self.gecici.name + "/transfers.json"
bridge.TRANSFERS.clear()
bridge.WS_HTTP.clear()
bridge.CHATGPT = f"http://127.0.0.1:{self.ozgun.server_address[1]}"
bridge.KOPRU = f"http://127.0.0.1:{self.kopru.server_address[1]}"
bridge.LOGDIR = self.gecici.name
@@ -209,11 +277,107 @@ class Ayirici(unittest.TestCase):
d = json.loads(self.iste("GET", "/aiturk/codex-baglama/status")[1])
self.assertEqual((d["surum"], d["ide_model"], d["kopru"], d["ayirici"]), (bridge.SURUM, 147, "calisiyor", True))
def test_a_persistent_connection_is_refused_the_way_it_is_today(self):
def test_a_persistent_connection_without_a_model_hint_is_refused_the_way_it_is_today(self):
durum, _, _ = self.iste("GET", "/backend-api/codex/responses", None, {"Upgrade": "websocket", "Connection": "Upgrade"})
self.assertEqual(durum, 426)
self.assertEqual(self.ozgun.gelen + self.kopru.gelen, [])
# --- Surekli baglanti: abonelik modeli chatgpt.com'a tunellenir, ek model/gecis HTTP'de kalir ---------------
def ws_ust(self, durum=101, uzanti=""):
ust = SahteWs(durum, uzanti)
bridge.CHATGPT = f"http://127.0.0.1:{ust.port}"
return ust
def ws_ac(self, model="gpt-6.1-sol", sid="sohbet-1", uzanti=True):
k = socket.create_connection(("127.0.0.1", self.on.server_address[1]), timeout=10)
baslik = ["GET /backend-api/codex/responses HTTP/1.1", "Host: 127.0.0.1", "Connection: Upgrade",
"Upgrade: websocket", "Sec-WebSocket-Version: 13", "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==",
"authorization: Bearer sahte", "chatgpt-account-id: hesap", f"session-id: {sid}", f"thread-id: {sid}",
"openai-beta: responses_websockets=2026-02-06"]
if model:
baslik.append(f"x-codex-routing-hint: model={model}")
if uzanti:
baslik.append("sec-websocket-extensions: permessage-deflate; client_max_window_bits")
k.sendall(("\r\n".join(baslik) + "\r\n\r\n").encode())
cevap = b""
while b"\r\n\r\n" not in cevap:
parca = k.recv(4096)
if not parca:
break
cevap += parca
return k, int(cevap.split(b" ", 2)[1]), cevap.partition(b"\r\n\r\n")[2]
def test_codex_own_model_websocket_is_tunnelled_to_chatgpt_byte_for_byte(self):
ust = self.ws_ust()
k, durum, _ = self.ws_ac()
self.assertEqual(durum, 101)
mesaj = json.dumps({"type": "response.create", "model": "gpt-6.1-sol", "input": [{"type": "message", "role": "user",
"content": [{"type": "input_text", "text": "selam " * 5000}]}]}).encode()
cerceve = ws_cerceve(mesaj)
k.sendall(cerceve)
cevap = bridge.ws_cerceve_oku(k.makefile("rb").read)
self.assertEqual(cevap[4], b"yanit:" + hashlib.sha256(mesaj).hexdigest().encode())
self.assertEqual(ust.ham[0], cerceve) # Codex'in cercevesi aynen gitti
self.assertEqual(ust.baslik["authorization"], "Bearer sahte")
self.assertEqual(ust.baslik["openai-beta"], "responses_websockets=2026-02-06")
self.assertEqual(self.kopru.gelen, [])
k.close()
def test_added_model_websocket_falls_back_to_http_at_once(self):
ust = self.ws_ust()
k, durum, _ = self.ws_ac(model="ide/claude-claude-haiku-5-5")
self.assertEqual(durum, 426)
self.assertEqual(ust.baglanti, 0)
k.close()
def test_switching_to_an_added_model_on_an_open_tunnel_moves_the_chat_to_http(self):
ust = self.ws_ust()
k, durum, _ = self.ws_ac(sid="sohbet-gecis")
self.assertEqual(durum, 101)
k.sendall(ws_cerceve(json.dumps({"type": "response.create", "model": "ide/agy-gemini-3.8-flash-low", "input": []}).encode()))
kapanis = bridge.ws_cerceve_oku(k.makefile("rb").read)
self.assertEqual(kapanis[3], 8) # kapanis cercevesi
self.assertEqual(ust.ham, []) # ek model istegi chatgpt.com'a gitmedi
k.close()
k2, durum2, _ = self.ws_ac(sid="sohbet-gecis")
self.assertEqual(durum2, 426) # ayni sohbet artik HTTP yolunda
k2.close()
def test_compressed_message_with_transfer_history_is_detected(self):
ust = self.ws_ust(uzanti="permessage-deflate")
k, durum, _ = self.ws_ac(sid="sohbet-sikisik")
self.assertEqual(durum, 101)
sik = zlib.compressobj(wbits=-zlib.MAX_WBITS)
ilk = json.dumps({"type": "response.create", "model": "gpt-6.1-sol", "input": [{"type": "message", "role": "user",
"content": [{"type": "input_text", "text": "ilk"}]}]}).encode()
k.sendall(ws_cerceve(sikistir(sik, ilk), rsv1=True))
okur = k.makefile("rb").read
self.assertEqual(bridge.ws_cerceve_oku(okur)[3], 1)
ikinci = json.dumps({"type": "response.create", "model": "gpt-6.1-sol",
"input": [{"type": "message", "role": "assistant", "id": "chatcmpl-abc", "content": []}]}).encode()
k.sendall(ws_cerceve(sikistir(sik, ikinci), rsv1=True))
self.assertEqual(bridge.ws_cerceve_oku(okur)[3], 8)
self.assertEqual(len(ust.ham), 1)
k.close()
def test_chat_with_a_remembered_transfer_stays_on_http(self):
self.ws_ust()
scope = hashlib.sha256(b"hesap|sohbet-eski").hexdigest()
bridge.TRANSFERS[scope] = "ide/Jail-Omni"
k, durum, _ = self.ws_ac(sid="sohbet-eski")
self.assertEqual(durum, 426)
k.close()
def test_chatgpt_refusing_the_tunnel_or_setting_off_falls_back_to_http(self):
ust = self.ws_ust(durum=401)
k, durum, _ = self.ws_ac(sid="sohbet-red")
self.assertEqual((durum, ust.baglanti), (426, 1))
k.close()
bridge.CFG["abonelik_websocket"] = False
k, durum, _ = self.ws_ac(sid="sohbet-kapali")
self.assertEqual((durum, ust.baglanti), (426, 1))
k.close()
def test_which_side_a_model_is_on(self):
for slug, ek in (("ide/Opus-5", True), ("9r/ag/gemini", True), ("gpt-6-astra", False), ("gpt-6.1-sol", False),
("", False), (None, False), ("idea", False)):
+1 -1
View File
@@ -27,7 +27,7 @@ export const CODEX_LINK_REFRESH_URL = `${CODEX_LINK_ORIGIN}/aiturk/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
// another version is restarted so an updated IDE never keeps an old bridge alive.
export const CODEX_BRIDGE_VERSION = '24'
export const CODEX_BRIDGE_VERSION = '25'
export const CODEX_LINK_LINE = `openai_base_url = "${CODEX_LINK_ORIGIN}/backend-api/codex" ${CODEX_LINK_MARKER}`
export const CODEX_LINK_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "aiturk-ide",
"productName": "AITURK IDE",
"private": true,
"version": "1.0.0-beta.45",
"version": "1.0.0-beta.46",
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
"author": "AITURK / TurkServis",
"repository": {
+1 -1
View File
@@ -65,7 +65,7 @@
},
"apps/desktop": {
"name": "aiturk-ide",
"version": "1.0.0-beta.45",
"version": "1.0.0-beta.46",
"license": "MIT",
"dependencies": {
"@assistant-ui/core": "0.2.23",