- 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.
83 lines
2.7 KiB
TypeScript
83 lines
2.7 KiB
TypeScript
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)
|
||
}
|
||
}
|
||
}
|