feat(codex-link): AITURK work routing setting and beta.40
- Default routing rule (user decision, 15 Oct 2026): repeating/scheduled work goes to the AITURK IDE bot as a cron job; coding stays in Codex (itself or its subagents); 'Codex' or 'sen yap' keeps everything in Codex. - Settings > Codex Bağlama: choice (recommended / send large work / never / custom text) with a text box and restore-default; written to bridge.json aiturk_hat. - Bridge re-reads aiturk_hat when bridge.json changes: no restart. IDE apply keeps aiturk_hat instead of wiping it. - Bridge version 20, IDE 1.0.0-beta.40. Jev untouched.
This commit is contained in:
1 parent
a7ba300448
commit
557e619499
16 files changed
+388
-26
No files matched your search
@@ -0,0 +1,82 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
/** "AITURK iş hattı" choice, stored in the Codex bridge's `bridge.json` under `aiturk_hat`.
|
||||
* The bridge re-reads the field when the file changes, so no restart is needed. */
|
||||
export type IsHattiSecim = 'onerilen' | 'buyuk' | 'kapali' | 'ozel'
|
||||
|
||||
export interface IsHattiSettings {
|
||||
secim: IsHattiSecim
|
||||
talimat: string
|
||||
}
|
||||
|
||||
const SECIMLER: IsHattiSecim[] = ['onerilen', 'buyuk', 'kapali', 'ozel']
|
||||
const MAX_TALIMAT = 20000
|
||||
|
||||
export function readIsHatti(raw: null | string): IsHattiSettings {
|
||||
try {
|
||||
const hat = raw ? JSON.parse(raw)?.aiturk_hat : null
|
||||
|
||||
if (!hat || typeof hat !== 'object') {
|
||||
return { secim: 'onerilen', talimat: '' }
|
||||
}
|
||||
|
||||
const talimat = typeof hat.talimat === 'string' ? hat.talimat : ''
|
||||
|
||||
if (SECIMLER.includes(hat.secim)) {
|
||||
return { secim: hat.secim, talimat }
|
||||
}
|
||||
|
||||
// Written before this screen existed: same meaning the bridge gives it.
|
||||
return { secim: hat.enabled === false ? 'kapali' : talimat.trim() ? 'ozel' : 'onerilen', talimat }
|
||||
} catch {
|
||||
return { secim: 'onerilen', talimat: '' }
|
||||
}
|
||||
}
|
||||
|
||||
export function writeIsHatti(raw: null | string, value: unknown): string {
|
||||
const v = value as Partial<IsHattiSettings> | null
|
||||
|
||||
if (!v || !SECIMLER.includes(v.secim as IsHattiSecim)) throw new Error('Invalid choice')
|
||||
const talimat = typeof v.talimat === 'string' ? v.talimat : ''
|
||||
|
||||
if (talimat.length > MAX_TALIMAT) throw new Error('Instruction too long')
|
||||
if (v.secim === 'ozel' && !talimat.trim()) throw new Error('Custom instruction is empty')
|
||||
let config: Record<string, unknown> = {}
|
||||
|
||||
try {
|
||||
const parsed = raw ? JSON.parse(raw) : {}
|
||||
config = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}
|
||||
} catch {
|
||||
throw new Error('bridge.json is not valid JSON')
|
||||
}
|
||||
|
||||
const old = config.aiturk_hat && typeof config.aiturk_hat === 'object' ? (config.aiturk_hat as Record<string, unknown>) : {}
|
||||
const hat: Record<string, unknown> = { ...old, secim: v.secim, talimat }
|
||||
delete hat.enabled
|
||||
|
||||
return `${JSON.stringify({ ...config, aiturk_hat: hat }, null, 2)}\n`
|
||||
}
|
||||
|
||||
export function createIsHattiStore(directory: string) {
|
||||
const file = path.join(directory, 'bridge.json')
|
||||
const read = () => (fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : null)
|
||||
|
||||
return {
|
||||
get: (): IsHattiSettings => readIsHatti(read()),
|
||||
save(value: unknown): IsHattiSettings {
|
||||
const text = writeIsHatti(read(), value)
|
||||
fs.mkdirSync(directory, { recursive: true })
|
||||
const tmp = `${file}.${process.pid}.tmp`
|
||||
|
||||
try {
|
||||
fs.writeFileSync(tmp, text, 'utf8')
|
||||
fs.renameSync(tmp, file)
|
||||
} finally {
|
||||
if (fs.existsSync(tmp)) fs.unlinkSync(tmp)
|
||||
}
|
||||
|
||||
return readIsHatti(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user