diff --git a/bun.lock b/bun.lock index 56bb0f5c310..c1a64cf5a8b 100644 --- a/bun.lock +++ b/bun.lock @@ -499,6 +499,7 @@ "@solid-primitives/resize-observer": "2.1.3", "@solidjs/meta": "catalog:", "@solidjs/router": "catalog:", + "@typescript/native-preview": "catalog:", "dompurify": "3.3.1", "fuzzysort": "catalog:", "katex": "0.16.27", @@ -2780,7 +2781,7 @@ "fast-xml-builder": ["fast-xml-builder@1.1.4", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="], - "fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + "fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], "fastest-levenshtein": ["fastest-levenshtein@1.0.16", "", {}, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="], @@ -4402,8 +4403,6 @@ "@aws-sdk/token-providers/@aws-sdk/types": ["@aws-sdk/types@3.973.6", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw=="], - "@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@azure/core-http/@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="], "@azure/core-http/@azure/core-tracing": ["@azure/core-tracing@1.0.0-preview.13", "", { "dependencies": { "@opentelemetry/api": "^1.0.1", "tslib": "^2.2.0" } }, "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ=="], @@ -4532,8 +4531,6 @@ "@morphllm/morphsdk/diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="], - "@morphllm/morphsdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="], @@ -5260,8 +5257,6 @@ "@kilocode/cli/@morphllm/morphsdk/diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="], - "@kilocode/cli/@morphllm/morphsdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@kilocode/kilo-gateway/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.75", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gGaGZjkFpqcXJk6321JzhRl66pM2VxBlI470L8W4DQUW4S6iDT1R9L7awSzGB4Cn9toUl7DTV8BemaXZYXV4SA=="], "@kilocode/kilo-gateway/@opentui/core/@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.75", "", { "os": "darwin", "cpu": "x64" }, "sha512-tPlvqQI0whZ76amHydpJs5kN+QeWAIcFbI8RAtlAo9baj2EbxTDC+JGwgb9Fnt0/YQx831humbtaNDhV2Jt1bw=="], @@ -5448,6 +5443,8 @@ "archiver-utils/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "aws-sdk/xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], + "babel-plugin-module-resolver/glob/minimatch": ["minimatch@8.0.7", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="], "babel-plugin-module-resolver/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="], @@ -5702,10 +5699,32 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.11", "", { "dependencies": { "@smithy/types": "^4.13.1", "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" } }, "sha512-iitV/gZKQMvY9d7ovmyFnFuTHbBAtrmLnvaSb/3X8vOKyevwtpmEtyc8AdhVWZe0pI/1GsHxlEvQeOePFzy7KQ=="], + "@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-o1GX0+IPlFi/D8ei9y/jj3yucJWNfPnbB5appVBWevAyUdZA5KzQ2nK/hDxiu9olTZlFEFpf1m1Rn3FaGxHqsw=="], + "@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + "@electron/asar/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "@electron/rebuild/ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], @@ -6146,6 +6165,8 @@ "test-exclude/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + "@electron/rebuild/ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "@electron/rebuild/ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index edf33607314..b1b83350694 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -136,7 +136,8 @@ export const dict = { "provider.connect.oauth.code.invalid": "رمز التفويض غير صالح", "provider.connect.oauth.auto.visit.prefix": "قم بزيارة ", "provider.connect.oauth.auto.visit.link": "هذا الرابط", - "provider.connect.oauth.auto.visit.suffix": " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.", + "provider.connect.oauth.auto.visit.suffix": + " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.", "provider.connect.oauth.auto.confirmationCode": "رمز التأكيد", "provider.connect.toast.connected.title": "تم توصيل {{provider}}", "provider.connect.toast.connected.description": "نماذج {{provider}} متاحة الآن للاستخدام.", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index 5ca652a639b..4a2bca5f291 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -389,7 +389,8 @@ export const dict = { "toast.session.unshare.failed.description": "Une erreur s'est produite lors de l'annulation du partage de la session", "toast.session.listFailed.title": "Échec du chargement des sessions pour {{project}}", "toast.update.title": "Mise à jour disponible", - "toast.update.description": "Une nouvelle version d'Kilo ({{version}}) est maintenant disponible pour installation.", + "toast.update.description": + "Une nouvelle version d'Kilo ({{version}}) est maintenant disponible pour installation.", "toast.update.action.installRestart": "Installer et redémarrer", "toast.update.action.notYet": "Pas encore", "error.page.title": "Quelque chose s'est mal passé", @@ -523,7 +524,8 @@ export const dict = { "sidebar.workspaces.enable": "Activer les espaces de travail", "sidebar.workspaces.disable": "Désactiver les espaces de travail", "sidebar.gettingStarted.title": "Commencer", - "sidebar.gettingStarted.line1": "Kilo inclut des modèles gratuits pour que vous puissiez commencer immédiatement.", + "sidebar.gettingStarted.line1": + "Kilo inclut des modèles gratuits pour que vous puissiez commencer immédiatement.", "sidebar.gettingStarted.line2": "Connectez n'importe quel fournisseur pour utiliser des modèles, y compris Claude, GPT, Gemini etc.", "sidebar.project.recentSessions": "Sessions récentes", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index f07df4a2f20..b52bd580677 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -148,7 +148,8 @@ export const dict = { "provider.connect.oauth.code.invalid": "รหัสการอนุญาตไม่ถูกต้อง", "provider.connect.oauth.auto.visit.prefix": "เยี่ยมชม ", "provider.connect.oauth.auto.visit.link": "ลิงก์นี้", - "provider.connect.oauth.auto.visit.suffix": " และป้อนรหัสด้านล่างเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน Kilo", + "provider.connect.oauth.auto.visit.suffix": + " และป้อนรหัสด้านล่างเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน Kilo", "provider.connect.oauth.auto.confirmationCode": "รหัสยืนยัน", "provider.connect.toast.connected.title": "{{provider}} ที่เชื่อมต่อแล้ว", "provider.connect.toast.connected.description": "โมเดล {{provider}} พร้อมใช้งานแล้ว", diff --git a/packages/app/src/i18n/tr.ts b/packages/app/src/i18n/tr.ts index 242ce219217..10e72dc9851 100644 --- a/packages/app/src/i18n/tr.ts +++ b/packages/app/src/i18n/tr.ts @@ -143,7 +143,8 @@ export const dict = { "provider.connect.opencodeZen.visit.prefix": "", "provider.connect.opencodeZen.visit.link": "opencode.ai/zen", "provider.connect.opencodeZen.visit.suffix": " adresini ziyaret ederek API anahtarınızı alın.", - "provider.connect.oauth.code.visit.prefix": "Hesabınızı bağlamak ve Kilo'da {{provider}} modellerini kullanmak için ", + "provider.connect.oauth.code.visit.prefix": + "Hesabınızı bağlamak ve Kilo'da {{provider}} modellerini kullanmak için ", "provider.connect.oauth.code.visit.link": "bu bağlantıya", "provider.connect.oauth.code.visit.suffix": " tıklayarak yetkilendirme kodunuzu alın.", "provider.connect.oauth.code.label": "{{method}} yetkilendirme kodu", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index c71e66268b1..505cf2a530f 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -149,7 +149,8 @@ export const dict = { "provider.connect.oauth.code.invalid": "授權碼無效", "provider.connect.oauth.auto.visit.prefix": "造訪 ", "provider.connect.oauth.auto.visit.link": "此連結", - "provider.connect.oauth.auto.visit.suffix": " 並輸入以下程式碼,以連線你的帳戶並在 Kilo 中使用 {{provider}} 模型。", + "provider.connect.oauth.auto.visit.suffix": + " 並輸入以下程式碼,以連線你的帳戶並在 Kilo 中使用 {{provider}} 模型。", "provider.connect.oauth.auto.confirmationCode": "確認碼", "provider.connect.toast.connected.title": "{{provider}} 已連線", "provider.connect.toast.connected.description": "現在可以使用 {{provider}} 模型了。", diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index b9c8a31c9d5..e042cdaca65 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -22,7 +22,7 @@ import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { Button } from "@opencode-ai/ui/button" import { Icon } from "@opencode-ai/ui/icon" import { IconButton } from "@opencode-ai/ui/icon-button" -import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" +import { Tooltip } from "@opencode-ai/ui/tooltip" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" import { Dialog } from "@opencode-ai/ui/dialog" import { getFilename } from "@opencode-ai/util/path" @@ -1942,20 +1942,14 @@ export default function Layout(props: ParentProps) { fallback={ <>
- navigateWithSidebarReset(`/${base64Encode(p.worktree)}/session`)} > - - + {language.t("command.session.new")} +
<>
- - - +
{ return `file://${path}` }, openTab: (tab) => calls.push(`open:${tab}`), + setActive: (tab) => calls.push(`active:${tab}`), loadFile: (path) => calls.push(`load:${path}`), }) openReviewFile("src/a.ts") - expect(calls).toEqual(["show", "load:src/a.ts", "tab:src/a.ts", "open:file://src/a.ts"]) + expect(calls).toEqual(["show", "load:src/a.ts", "tab:src/a.ts", "open:file://src/a.ts", "active:file://src/a.ts"]) }) }) diff --git a/packages/app/src/pages/session/helpers.ts b/packages/app/src/pages/session/helpers.ts index 20f1d99a8be..60b26cdf47c 100644 --- a/packages/app/src/pages/session/helpers.ts +++ b/packages/app/src/pages/session/helpers.ts @@ -24,15 +24,20 @@ export const createOpenReviewFile = (input: { showAllFiles: () => void tabForPath: (path: string) => string openTab: (tab: string) => void + setActive: (tab: string) => void loadFile: (path: string) => any | Promise }) => { return (path: string) => { batch(() => { input.showAllFiles() const maybePromise = input.loadFile(path) - const openTab = () => input.openTab(input.tabForPath(path)) - if (maybePromise instanceof Promise) maybePromise.then(openTab) - else openTab() + const open = () => { + const tab = input.tabForPath(path) + input.openTab(tab) + input.setActive(tab) + } + if (maybePromise instanceof Promise) maybePromise.then(open) + else open() }) } } diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx index 192c7667d9d..1f756fc2454 100644 --- a/packages/app/src/pages/session/message-timeline.tsx +++ b/packages/app/src/pages/session/message-timeline.tsx @@ -1,4 +1,4 @@ -import { For, createEffect, createMemo, on, onCleanup, Show, startTransition, Index, type JSX } from "solid-js" +import { For, createEffect, createMemo, on, onCleanup, Show, Index, type JSX } from "solid-js" import { createStore, produce } from "solid-js/store" import { useNavigate, useParams } from "@solidjs/router" import { Button } from "@opencode-ai/ui/button" @@ -160,7 +160,7 @@ function createTimelineStaging(input: TimelineStageInput) { } const currentTotal = input.messages().length count = Math.min(currentTotal, count + input.config.batch) - startTransition(() => setState("count", count)) + setState("count", count) if (count >= currentTotal) { setState({ completedSession: sessionKey, activeSession: "" }) frame = undefined diff --git a/packages/app/src/pages/session/use-session-hash-scroll.ts b/packages/app/src/pages/session/use-session-hash-scroll.ts index 71b9a217d70..74b0ca83ce0 100644 --- a/packages/app/src/pages/session/use-session-hash-scroll.ts +++ b/packages/app/src/pages/session/use-session-hash-scroll.ts @@ -55,6 +55,7 @@ export const useSessionHashScroll = (input: { } const scrollToMessage = (message: UserMessage, behavior: ScrollBehavior = "smooth") => { + console.log({ message, behavior }) if (input.currentMessageId() !== message.id) input.setActiveMessage(message) const index = messageIndex().get(message.id) ?? -1 diff --git a/packages/desktop/src/i18n/de.ts b/packages/desktop/src/i18n/de.ts index 224e0b62d2c..6ef1167a362 100644 --- a/packages/desktop/src/i18n/de.ts +++ b/packages/desktop/src/i18n/de.ts @@ -45,7 +45,8 @@ export const dict = { "desktop.menu.help.shareFeedback": "Feedback teilen", "desktop.menu.help.reportBug": "Einen Fehler melden", "desktop.cli.error.unsupportedPlatform": "Die CLI-Installation wird nur unter macOS und Linux unterstützt.", - "desktop.cli.error.sidecarMissing": "Das Kilo CLI-Binary fehlt. Versuchen Sie, die Desktop-App neu zu installieren.", + "desktop.cli.error.sidecarMissing": + "Das Kilo CLI-Binary fehlt. Versuchen Sie, die Desktop-App neu zu installieren.", "desktop.cli.error.scriptWriteFailed": "Das CLI-Installationsskript konnte nicht vorbereitet werden.", "desktop.cli.error.scriptPermissionFailed": "Das CLI-Installationsskript konnte nicht ausführbar gemacht werden.", "desktop.cli.error.scriptRunFailed": "Das CLI-Installationsskript konnte nicht ausgeführt werden.", diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 6bde9ff754e..264ab6af389 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -179,8 +179,8 @@ for (const item of targets) { autoloadTsconfig: true, autoloadPackageJson: true, target: name.replace(pkg.name, "bun") as any, - outfile: `dist/${name}/bin/kilo`, - execArgv: [`--user-agent=kilo/${Script.version}`, "--use-system-ca", "--"], + outfile: `dist/${name}/bin/kilo`, // kilocode_change + execArgv: [`--user-agent=kilo/${Script.version}`, "--use-system-ca", "--"], // kilocode_change windows: {}, }, entrypoints: ["./src/index.ts", parserWorker, workerPath], diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 075b9ca5bb9..62690a6ff1e 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -31,6 +31,7 @@ import { import { Log } from "../util/log" import { pathToFileURL } from "bun" import { Filesystem } from "../util/filesystem" +import { Hash } from "../util/hash" import { ACPSessionManager } from "./session" import type { ACPConfig } from "./types" import { Provider } from "../provider/provider" @@ -283,7 +284,7 @@ export namespace ACP { const output = this.bashOutput(part) const content: ToolCallContent[] = [] if (output) { - const hash = String(Bun.hash(output)) + const hash = Hash.fast(output) if (part.tool === "bash") { if (this.bashSnapshots.get(part.callID) === hash) { await this.connection diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts index cce7f2dd954..53a5f4bdafc 100644 --- a/packages/opencode/src/cli/cmd/auth.ts +++ b/packages/opencode/src/cli/cmd/auth.ts @@ -13,6 +13,7 @@ import { Instance } from "../../project/instance" import type { Hooks } from "@kilocode/plugin" import { Process } from "../../util/process" import { text } from "node:stream/consumers" +import { setTimeout as sleep } from "node:timers/promises" type PluginAuth = NonNullable @@ -47,7 +48,7 @@ async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string, const method = plugin.auth.methods[index] // Handle prompts for all auth types - await Bun.sleep(10) + await sleep(10) const inputs: Record = {} if (method.prompts) { for (const prompt of method.prompts) { diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts index d83c4ed8a43..4b8a3e7d453 100644 --- a/packages/opencode/src/cli/cmd/debug/lsp.ts +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -3,6 +3,7 @@ import { bootstrap } from "../../bootstrap" import { cmd } from "../cmd" import { Log } from "../../../util/log" import { EOL } from "os" +import { setTimeout as sleep } from "node:timers/promises" export const LSPCommand = cmd({ command: "lsp", @@ -19,7 +20,7 @@ const DiagnosticsCommand = cmd({ async handler(args) { await bootstrap(process.cwd(), async () => { await LSP.touchFile(args.file, true) - await Bun.sleep(1000) + await sleep(1000) process.stdout.write(JSON.stringify(await LSP.diagnostics(), null, 2) + EOL) }) }, diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts index e7891f2ab26..cc7ac4c1892 100644 --- a/packages/opencode/src/cli/cmd/github.ts +++ b/packages/opencode/src/cli/cmd/github.ts @@ -28,6 +28,7 @@ import { Bus } from "../../bus" import { MessageV2 } from "../../session/message-v2" import { SessionPrompt } from "@/session/prompt" import { $ } from "bun" +import { setTimeout as sleep } from "node:timers/promises" type GitHubAuthor = { login: string @@ -353,7 +354,7 @@ export const GithubInstallCommand = cmd({ } retries++ - await Bun.sleep(1000) + await sleep(1000) } while (true) s.stop("Installed GitHub app") @@ -1384,7 +1385,7 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` } catch (e) { if (retries > 0) { console.log(`Retrying after ${delayMs}ms...`) - await Bun.sleep(delayMs) + await sleep(delayMs) return withRetry(fn, retries - 1, delayMs) } throw e diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 55e31778954..8a0df9a569c 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -6,6 +6,7 @@ import { cmd } from "./cmd" import { Flag } from "../../flag/flag" import { bootstrap } from "../bootstrap" import { EOL } from "os" +import { text as streamText } from "node:stream/consumers" import { Filesystem } from "../../util/filesystem" import { createKiloClient, type Message, type KiloClient, type ToolPart } from "@kilocode/sdk/v2" import { Server } from "../../server/server" @@ -351,7 +352,7 @@ export const RunCommand = cmd({ } } - if (!process.stdin.isTTY) message += "\n" + (await Bun.stdin.text()) + if (!process.stdin.isTTY) message += "\n" + (await streamText(process.stdin)) if (message.trim().length === 0 && !args.command) { UI.error("You must provide a message or a command") diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index fdadc1be9b6..007c5fe5f27 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -3,6 +3,7 @@ import { tui } from "./app" import { Rpc } from "@/util/rpc" import { type rpc } from "./worker" import path from "path" +import { text as streamText } from "node:stream/consumers" import { fileURLToPath } from "url" import { UI } from "@/cli/ui" import { Log } from "@/util/log" @@ -55,7 +56,7 @@ async function target() { } async function input(value?: string) { - const piped = process.stdin.isTTY ? undefined : await Bun.stdin.text() + const piped = process.stdin.isTTY ? undefined : await streamText(process.stdin) if (!value) return piped if (!piped) return value return piped + "\n" + value diff --git a/packages/opencode/src/cli/cmd/tui/worker.ts b/packages/opencode/src/cli/cmd/tui/worker.ts index 21997e881a2..f7560b7491f 100644 --- a/packages/opencode/src/cli/cmd/tui/worker.ts +++ b/packages/opencode/src/cli/cmd/tui/worker.ts @@ -10,6 +10,7 @@ import { GlobalBus } from "@/bus/global" import { createKiloClient, type Event } from "@kilocode/sdk/v2" import type { BunWebSocketData } from "hono/bun" import { Flag } from "@/flag/flag" +import { setTimeout as sleep } from "node:timers/promises" await Log.init({ print: process.argv.includes("--print-logs"), @@ -75,7 +76,7 @@ const startEventStream = (directory: string) => { ).catch(() => undefined) if (!events) { - await Bun.sleep(250) + await sleep(250) continue } @@ -84,7 +85,7 @@ const startEventStream = (directory: string) => { } if (!signal.aborted) { - await Bun.sleep(250) + await sleep(250) } } })().catch((error) => { diff --git a/packages/opencode/src/cli/ui.ts b/packages/opencode/src/cli/ui.ts index 41e6f672f63..9bbc7cf97d9 100644 --- a/packages/opencode/src/cli/ui.ts +++ b/packages/opencode/src/cli/ui.ts @@ -25,12 +25,12 @@ export namespace UI { export function println(...message: string[]) { print(...message) - Bun.stderr.write(EOL) + process.stderr.write(EOL) } export function print(...message: string[]) { blank = false - Bun.stderr.write(message.join(" ")) + process.stderr.write(message.join(" ")) } let blank = false @@ -45,7 +45,7 @@ export namespace UI { const result: string[] = [] const reset = "\x1b[0m" const left = { - fg: Bun.color("gray", "ansi") ?? "", + fg: "\x1b[90m", shadow: "\x1b[38;5;235m", bg: "\x1b[48;5;235m", } diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index e28c576586b..540e9976db1 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1376,7 +1376,7 @@ export namespace Config { if (!parsed.data.$schema && isFile) { parsed.data.$schema = "https://app.kilo.ai/config.json" // kilocode_change const updated = original.replace(/^\s*\{/, '{\n "$schema": "https://app.kilo.ai/config.json",') // kilocode_change - await Bun.write(options.path, updated).catch(() => {}) + await Filesystem.write(options.path, updated).catch(() => {}) } const data = parsed.data if (data.plugin && isFile) { @@ -1572,3 +1572,5 @@ export namespace Config { return state().then((x) => x.directories) } } +Filesystem.write +Filesystem.write diff --git a/packages/opencode/src/config/migrate-tui-config.ts b/packages/opencode/src/config/migrate-tui-config.ts index 30d7affb2f6..5122958c902 100644 --- a/packages/opencode/src/config/migrate-tui-config.ts +++ b/packages/opencode/src/config/migrate-tui-config.ts @@ -70,7 +70,7 @@ export async function migrateTuiConfig(input: MigrateInput) { if (extracted.keybinds !== undefined) payload.keybinds = extracted.keybinds if (tui) Object.assign(payload, tui) - const wrote = await Bun.write(target, JSON.stringify(payload, null, 2)) + const wrote = await Filesystem.write(target, JSON.stringify(payload, null, 2)) .then(() => true) .catch((error) => { log.warn("failed to write tui migration target", { from: file, to: target, error }) @@ -104,7 +104,7 @@ async function backupAndStripLegacy(file: string, source: string) { const hasBackup = await Filesystem.exists(backup) const backed = hasBackup ? true - : await Bun.write(backup, source) + : await Filesystem.write(backup, source) .then(() => true) .catch((error) => { log.warn("failed to backup source config during tui migration", { path: file, backup, error }) @@ -123,7 +123,7 @@ async function backupAndStripLegacy(file: string, source: string) { return applyEdits(acc, edits) }, source) - return Bun.write(file, text) + return Filesystem.write(file, text) .then(() => { log.info("stripped tui keys from server config", { path: file, backup }) return true diff --git a/packages/opencode/src/mcp/oauth-callback.ts b/packages/opencode/src/mcp/oauth-callback.ts index 120c21cf031..6132f6b7892 100644 --- a/packages/opencode/src/mcp/oauth-callback.ts +++ b/packages/opencode/src/mcp/oauth-callback.ts @@ -1,3 +1,4 @@ +import { createConnection } from "net" import { Log } from "../util/log" import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH } from "./oauth-provider" @@ -166,21 +167,12 @@ export namespace McpOAuthCallback { export async function isPortInUse(): Promise { return new Promise((resolve) => { - Bun.connect({ - hostname: "127.0.0.1", - port: OAUTH_CALLBACK_PORT, - socket: { - open(socket) { - socket.end() - resolve(true) - }, - error() { - resolve(false) - }, - data() {}, - close() {}, - }, - }).catch(() => { + const socket = createConnection(OAUTH_CALLBACK_PORT, "127.0.0.1") + socket.on("connect", () => { + socket.destroy() + resolve(true) + }) + socket.on("error", () => { resolve(false) }) }) diff --git a/packages/opencode/src/plugin/codex.ts b/packages/opencode/src/plugin/codex.ts index b8636b0f33c..f7f9e36fbff 100644 --- a/packages/opencode/src/plugin/codex.ts +++ b/packages/opencode/src/plugin/codex.ts @@ -4,6 +4,7 @@ import { Installation } from "../installation" import { Auth, OAUTH_DUMMY_KEY } from "../auth" import os from "os" import { ProviderTransform } from "@/provider/transform" +import { setTimeout as sleep } from "node:timers/promises" const log = Log.create({ service: "plugin.codex" }) @@ -367,6 +368,7 @@ export async function CodexAuthPlugin(input: PluginInput): Promise { "gpt-5.1-codex-max", "gpt-5.1-codex-mini", "gpt-5.2", + "gpt-5.4", "gpt-5.2-codex", "gpt-5.3-codex", "gpt-5.1-codex", @@ -608,7 +610,7 @@ export async function CodexAuthPlugin(input: PluginInput): Promise { return { type: "failed" as const } } - await Bun.sleep(interval + OAUTH_POLLING_SAFETY_MARGIN_MS) + await sleep(interval + OAUTH_POLLING_SAFETY_MARGIN_MS) } }, } diff --git a/packages/opencode/src/plugin/copilot.ts b/packages/opencode/src/plugin/copilot.ts index a4f0256877b..85708cfc66b 100644 --- a/packages/opencode/src/plugin/copilot.ts +++ b/packages/opencode/src/plugin/copilot.ts @@ -1,6 +1,7 @@ import type { Hooks, PluginInput } from "@kilocode/plugin" import { Installation } from "@/installation" import { iife } from "@/util/iife" +import { setTimeout as sleep } from "node:timers/promises" const CLIENT_ID = "Ov23li8tweQw6odWQebz" // Add a small safety buffer when polling to avoid hitting the server @@ -270,7 +271,7 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise { } if (data.error === "authorization_pending") { - await Bun.sleep(deviceData.interval * 1000 + OAUTH_POLLING_SAFETY_MARGIN_MS) + await sleep(deviceData.interval * 1000 + OAUTH_POLLING_SAFETY_MARGIN_MS) continue } @@ -286,13 +287,13 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise { newInterval = serverInterval * 1000 } - await Bun.sleep(newInterval + OAUTH_POLLING_SAFETY_MARGIN_MS) + await sleep(newInterval + OAUTH_POLLING_SAFETY_MARGIN_MS) continue } if (data.error) return { type: "failed" as const } - await Bun.sleep(deviceData.interval * 1000 + OAUTH_POLLING_SAFETY_MARGIN_MS) + await sleep(deviceData.interval * 1000 + OAUTH_POLLING_SAFETY_MARGIN_MS) continue } }, diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 950036bf57d..836c49996df 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -6,6 +6,7 @@ import { mapValues, mergeDeep, omit, pickBy, sortBy } from "remeda" import { NoSuchModelError, type Provider as SDK } from "ai" import { Log } from "../util/log" import { BunProc } from "../bun" +import { Hash } from "../util/hash" import { Plugin } from "../plugin" import { ModelsDev, @@ -816,7 +817,7 @@ export namespace Provider { const modelLoaders: { [providerID: string]: CustomModelLoader } = {} - const sdk = new Map() + const sdk = new Map() log.info("init") @@ -1111,7 +1112,7 @@ export namespace Provider { ...model.headers, } - const key = Bun.hash.xxHash32(JSON.stringify({ providerID: model.providerID, npm: model.api.npm, options })) + const key = Hash.fast(JSON.stringify({ providerID: model.providerID, npm: model.api.npm, options })) const existing = s.sdk.get(key) if (existing) return existing diff --git a/packages/opencode/src/shell/shell.ts b/packages/opencode/src/shell/shell.ts index 4a2446b8689..21879dd0bf5 100644 --- a/packages/opencode/src/shell/shell.ts +++ b/packages/opencode/src/shell/shell.ts @@ -3,6 +3,7 @@ import { lazy } from "@/util/lazy" import { Filesystem } from "@/util/filesystem" import path from "path" import { spawn, type ChildProcess } from "child_process" +import { setTimeout as sleep } from "node:timers/promises" const SIGKILL_TIMEOUT_MS = 200 @@ -22,13 +23,13 @@ export namespace Shell { try { process.kill(-pid, "SIGTERM") - await Bun.sleep(SIGKILL_TIMEOUT_MS) + await sleep(SIGKILL_TIMEOUT_MS) if (!opts?.exited?.()) { process.kill(-pid, "SIGKILL") } } catch (_e) { proc.kill("SIGTERM") - await Bun.sleep(SIGKILL_TIMEOUT_MS) + await sleep(SIGKILL_TIMEOUT_MS) if (!opts?.exited?.()) { proc.kill("SIGKILL") } diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 113efb5fd70..e4d0f5eb688 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -1,6 +1,7 @@ import { $ } from "bun" import path from "path" import fs from "fs/promises" +import { Filesystem } from "../util/filesystem" import { Log } from "../util/log" import { Flag } from "../flag/flag" import { Global } from "../global" @@ -271,13 +272,12 @@ export namespace Snapshot { const target = path.join(git, "info", "exclude") await fs.mkdir(path.join(git, "info"), { recursive: true }) if (!file) { - await Bun.write(target, "") + await Filesystem.write(target, "") return } - const text = await Bun.file(file) - .text() - .catch(() => "") - await Bun.write(target, text) + const text = await Filesystem.readText(file).catch(() => "") + + await Filesystem.write(target, text) } async function excludes() { diff --git a/packages/opencode/src/util/hash.ts b/packages/opencode/src/util/hash.ts new file mode 100644 index 00000000000..680e0f40bc8 --- /dev/null +++ b/packages/opencode/src/util/hash.ts @@ -0,0 +1,7 @@ +import { createHash } from "crypto" + +export namespace Hash { + export function fast(input: string | Buffer): string { + return createHash("sha1").update(input).digest("hex") + } +} diff --git a/packages/opencode/test/preload.ts b/packages/opencode/test/preload.ts index a5243736e2f..5ae45d30a57 100644 --- a/packages/opencode/test/preload.ts +++ b/packages/opencode/test/preload.ts @@ -3,6 +3,7 @@ import os from "os" import path from "path" import fs from "fs/promises" +import { setTimeout as sleep } from "node:timers/promises" import { afterAll } from "bun:test" // Set XDG env vars FIRST, before any src/ imports @@ -15,7 +16,7 @@ afterAll(async () => { typeof error === "object" && error !== null && "code" in error && error.code === "EBUSY" const rm = async (left: number): Promise => { Bun.gc(true) - await Bun.sleep(100) + await sleep(100) return fs.rm(dir, { recursive: true, force: true }).catch((error) => { if (!busy(error)) throw error if (left <= 1) throw error diff --git a/packages/opencode/test/pty/pty-output-isolation.test.ts b/packages/opencode/test/pty/pty-output-isolation.test.ts index 44858a0ed27..ec1bbd46901 100644 --- a/packages/opencode/test/pty/pty-output-isolation.test.ts +++ b/packages/opencode/test/pty/pty-output-isolation.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test" import { Instance } from "../../src/project/instance" import { Pty } from "../../src/pty" import { tmpdir } from "../fixture/fixture" +import { setTimeout as sleep } from "node:timers/promises" describe("pty", () => { test("does not leak output when websocket objects are reused", async () => { @@ -43,7 +44,7 @@ describe("pty", () => { // Output from a must never show up in b. Pty.write(a.id, "AAA\n") - await Bun.sleep(100) + await sleep(100) expect(outB.join("")).not.toContain("AAA") } finally { @@ -88,7 +89,7 @@ describe("pty", () => { } Pty.write(a.id, "AAA\n") - await Bun.sleep(100) + await sleep(100) expect(outB.join("")).not.toContain("AAA") } finally { @@ -128,7 +129,7 @@ describe("pty", () => { ctx.connId = 2 Pty.write(a.id, "AAA\n") - await Bun.sleep(100) + await sleep(100) expect(out.join("")).toContain("AAA") } finally { diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index 6768e72d95a..eba4a995053 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from "bun:test" import type { NamedError } from "@opencode-ai/util/error" import { APICallError } from "ai" +import { setTimeout as sleep } from "node:timers/promises" import { SessionRetry } from "../../src/session/retry" import { MessageV2 } from "../../src/session/message-v2" @@ -135,7 +136,7 @@ describe("session.message-v2.fromError", () => { new ReadableStream({ async pull(controller) { controller.enqueue("Hello,") - await Bun.sleep(10000) + await sleep(10000) controller.enqueue(" World!") controller.close() }, diff --git a/packages/ui/package.json b/packages/ui/package.json index 91d17ace965..a340e7f6db7 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -68,7 +68,8 @@ "solid-js": "catalog:", "solid-list": "catalog:", "strip-ansi": "7.1.2", - "virtua": "catalog:" + "virtua": "catalog:", + "@typescript/native-preview": "catalog:" }, "peerDependencies": {} } diff --git a/script/upstream/merge.ts b/script/upstream/merge.ts index da933f93597..ce5f3e87965 100644 --- a/script/upstream/merge.ts +++ b/script/upstream/merge.ts @@ -141,6 +141,22 @@ async function main() { const currentBranch = await git.getCurrentBranch() logger.info(`Current branch: ${currentBranch}`) + // Enable git rerere so conflict resolutions are recorded and reused across merges + if (!options.dryRun) { + await git.ensureRerere() + logger.info("git rerere enabled (resolutions will be recorded and reused automatically)") + + // Train rerere from past upstream merge commits so the cache is populated + // even on a fresh clone. This replays past merges to learn their resolutions. + logger.info("Training rerere cache from past merge history...") + const learned = await git.trainRerere("merge: upstream\\|Resolve merge conflict") + if (learned > 0) { + logger.success(`Learned ${learned} conflict resolution(s) from history`) + } else { + logger.info("No new resolutions to learn from history (cache already up to date)") + } + } + // Step 2: Fetch upstream logger.step(2, 8, "Fetching upstream...") @@ -362,6 +378,13 @@ async function main() { const keepOursResults = await resetToOurs(config.keepOurs, { dryRun: false, verbose: options.verbose }) logger.success(`Reset ${keepOursResults.length} files to Kilo's version`) + // Clean untracked build artifacts from Kilo-specific directories. + // These packages don't exist in upstream, so their .gitignore files are absent + // on the opencode branch. Artifacts like bin/, out/, .next/ etc. would otherwise + // be picked up by the git add -A below. + logger.info("Cleaning Kilo-specific directory artifacts...") + await git.cleanDirectories(config.kiloDirectories) + // Commit all transformations await git.stageAll() await git.commit(`refactor: kilo compat for ${targetVersion.tag}`) @@ -378,6 +401,14 @@ async function main() { logger.info("Conflicted files:") logger.list(mergeResult.conflicts) + // Check if git rerere already auto-resolved any conflicts from recorded history. + // rerere.autoupdate stages them automatically; we just log how many were handled. + const rerereResolved = await git.getRerereResolved() + if (rerereResolved.length > 0) { + logger.success(`git rerere auto-resolved ${rerereResolved.length} conflict(s) from recorded history:`) + logger.list(rerereResolved) + } + // Since we applied all branding transforms pre-merge, remaining conflicts should be minimal. // These are likely files with kilocode_change markers or actual logic differences. diff --git a/script/upstream/transforms/transform-package-json.ts b/script/upstream/transforms/transform-package-json.ts index 1e16cf8bd75..e8ea12e8ac9 100644 --- a/script/upstream/transforms/transform-package-json.ts +++ b/script/upstream/transforms/transform-package-json.ts @@ -8,7 +8,8 @@ * 3. Injecting Kilo-specific dependencies * 4. Preserving Kilo's version number * 5. Preserving overrides and patchedDependencies - * 6. Using "newest wins" strategy for dependency versions + * 6. Preserving Kilo's repository configuration + * 7. Using "newest wins" strategy for dependency versions */ import { $ } from "bun" @@ -333,7 +334,14 @@ export async function transformPackageJson(file: string, options: PackageJsonOpt } } - // 6. Handle workspaces for root package.json + // 6. Preserve repository (Kilo-specific, upstream doesn't have this) + const ourRepo = ourPkg.repository + if (ourRepo && JSON.stringify(pkg.repository) !== JSON.stringify(ourRepo)) { + pkg.repository = ourRepo + changes.push(`repository: preserved Kilo's repository configuration`) + } + + // 7. Handle workspaces for root package.json // Kilo has removed hosted platform packages (console/*, slack, etc.) // so we need to preserve Kilo's workspace configuration instead of taking upstream's const ourWorkspaces = ourPkg.workspaces as { packages?: string[]; catalog?: Record } | undefined @@ -534,7 +542,14 @@ export async function transformAllPackageJson(options: PackageJsonOptions = {}): } } - // 6. Handle workspaces for root package.json + // 6. Preserve repository (Kilo-specific, upstream doesn't have this) + const kiloRepo = kiloPkg.repository + if (kiloRepo && JSON.stringify(pkg.repository) !== JSON.stringify(kiloRepo)) { + pkg.repository = kiloRepo + changes.push(`repository: preserved Kilo's repository configuration`) + } + + // 7. Handle workspaces for root package.json // Kilo has removed hosted platform packages (console/*, slack, etc.) // so we need to preserve Kilo's workspace configuration instead of taking upstream's const kiloWorkspaces = kiloPkg.workspaces as diff --git a/script/upstream/utils/config.ts b/script/upstream/utils/config.ts index 19b1bc3c29a..b0fe7318a58 100644 --- a/script/upstream/utils/config.ts +++ b/script/upstream/utils/config.ts @@ -203,6 +203,10 @@ export const defaultConfig: MergeConfig = { "packages/opencode/test/kilocode", "packages/kilo-gateway", "packages/kilo-telemetry", + "packages/kilo-vscode", + "packages/kilo-ui", + "packages/kilo-docs", + "packages/kilo-i18n", "script/upstream", ], diff --git a/script/upstream/utils/git.ts b/script/upstream/utils/git.ts index 0daf573dcf2..cf4fef55ee5 100644 --- a/script/upstream/utils/git.ts +++ b/script/upstream/utils/git.ts @@ -215,6 +215,17 @@ export async function checkoutTheirs(files: string[]): Promise { } } +/** + * Remove untracked files and directories from specific directories. + * Used to clean build artifacts from Kilo-specific directories after checking + * out the upstream branch, where package-level .gitignore files don't exist. + */ +export async function cleanDirectories(dirs: string[]): Promise { + for (const dir of dirs) { + await $`git clean -fd ${dir}`.quiet().nothrow() + } +} + /** * Check if the "ours" version of a conflicted file contains kilocode_change markers. * Uses git stage :2: which is the "ours" side during a merge conflict. @@ -225,3 +236,105 @@ export async function oursHasKilocodeChanges(file: string): Promise { if (result.exitCode !== 0) return false return result.stdout.toString().includes("kilocode_change") } + +/** + * Enable git rerere (REuse REcorded REsolution) in the local repo config. + * Also enables autoupdate so resolved files are automatically staged. + */ +export async function ensureRerere(): Promise { + await $`git config rerere.enabled true`.quiet() + await $`git config rerere.autoupdate true`.quiet() +} + +/** + * Train the rerere cache from past merge commits in the repo history. + * Implements the same logic as git's contrib/rerere-train.sh: + * For each merge commit in the range, replay the merge to let rerere + * record the pre-image, then check out the resolved tree so rerere + * records the post-image (the resolution). + * + * Returns the number of resolutions learned. + */ +export async function trainRerere(grep: string): Promise { + // Save the current HEAD so we can restore it afterwards + const headResult = await $`git symbolic-ref -q HEAD`.quiet().nothrow() + const branch = headResult.exitCode === 0 ? headResult.stdout.toString().trim() : null + const originalHead = branch ?? (await $`git rev-parse --verify HEAD`.text()).trim() + + let learned = 0 + + try { + // Find all merge commits matching the grep pattern (merges have multiple parents) + const revList = await $`git rev-list --parents --all --grep=${grep}`.quiet().nothrow() + if (revList.exitCode !== 0 || !revList.stdout.toString().trim()) return 0 + + const lines = revList.stdout + .toString() + .trim() + .split("\n") + .filter((l) => l.trim()) + + for (const line of lines) { + const parts = line.trim().split(/\s+/) + if (parts.length < 3) continue // skip non-merges (need commit + at least 2 parents) + + const [commit, parent1, ...otherParents] = parts + + // Checkout the first parent + const coResult = await $`git checkout -q ${parent1}`.quiet().nothrow() + if (coResult.exitCode !== 0) continue + + // Attempt the merge - we expect it to fail with conflicts + const mergeResult = await $`git merge --no-gpg-sign ${otherParents}`.quiet().nothrow() + if (mergeResult.exitCode === 0) { + // Cleanly merged — no conflicts to learn from, reset and skip + await $`git reset -q --hard`.quiet().nothrow() + continue + } + + // Check if rerere recorded a pre-image (MERGE_RR exists and is non-empty) + const mergeRR = Bun.file(`${process.env.GIT_DIR || ".git"}/MERGE_RR`) + const hasMergeRR = await mergeRR.exists().catch(() => false) + if (!hasMergeRR) { + await $`git reset -q --hard`.quiet().nothrow() + continue + } + + // Record the conflict pre-image + await $`git rerere`.quiet().nothrow() + + // Apply the actual resolution by checking out the merge commit's tree + await $`git checkout -q ${commit} -- .`.quiet().nothrow() + + // Record the resolution post-image + await $`git rerere`.quiet().nothrow() + + learned++ + await $`git reset -q --hard`.quiet().nothrow() + } + } finally { + // Always restore original branch + if (branch) { + await $`git checkout ${branch.replace("refs/heads/", "")}`.quiet().nothrow() + } else { + await $`git checkout ${originalHead}`.quiet().nothrow() + } + } + + return learned +} + +/** + * Return files that git rerere has already auto-resolved. + * These files no longer have conflict markers but haven't been staged yet + * (unless rerere.autoupdate is true, in which case they're already staged). + */ +export async function getRerereResolved(): Promise { + const result = await $`git rerere status`.quiet().nothrow() + if (result.exitCode !== 0 || !result.stdout.toString().trim()) return [] + return result.stdout + .toString() + .trim() + .split("\n") + .filter((f) => f.length > 0) +}