/* Inject, Status, Settings modes — real API */
// ── Inject ────────────────────────────────────────────────────
function InjectMode({ onHome }) {
const [step, setStep] = React.useState('select');
const [project, setProject] = React.useState(null);
const [type, setType] = React.useState(null);
const [text, setText] = React.useState('');
const [saving, setSaving] = React.useState(false);
const fileRef = React.useRef(null);
const projectsQ = useApi(() => API.projects(), []);
const projects = (projectsQ.data?.projects || []).filter(p => !p.archived);
const saveText = async () => {
if (!text.trim()) return;
setSaving(true);
try {
await API.injectText(project, text);
setText('');
setStep('done');
} catch (e) {
alert('Ошибка: ' + e.message);
} finally {
setSaving(false);
}
};
const saveFile = async (file) => {
if (!file) return;
setSaving(true);
try {
const fd = new FormData();
fd.append('file', file);
await API.injectFile(project, fd);
setStep('done');
} catch (e) {
alert('Ошибка: ' + e.message);
} finally {
setSaving(false);
}
};
if (step === 'done') {
return (
);
}
if (step === 'select') {
return (
router · embedder} onHome={onHome} />
в какой проект
{projectsQ.loading
?
: projectsQ.error
?
: projects.length === 0
?
Нет проектов
: projects.map(p => (
{ setProject(p.name); setStep('type'); }}>
{p.name}
{p.parent_id && [sub]}
{p.mode} · {p.status}
))
}
Inject — одноразовая сессия. M1/M2 не задействованы.
);
}
if (step === 'type') {
return (
target: {project}
{onHome && }
Тип инъекции
);
}
if (step === 'file') {
return (
→ injected/
{onHome && }
Загрузка файла
e.target.files[0] && saveFile(e.target.files[0])} />
Выбери файл
любой формат · до 10 MB
);
}
// text instruction
return (
→ memory.md
{onHome && }
Инструкция
);
}
// ── Status ────────────────────────────────────────────────────
function StatusMode() {
const statusQ = useApi(() => API.status(), []);
const [lastCheck, setLastCheck] = React.useState(Date.now());
const reload = React.useCallback(() => {
statusQ.reload();
setLastCheck(Date.now());
}, [statusQ.reload]);
React.useEffect(() => {
const t = setInterval(reload, 30000);
return () => clearInterval(t);
}, [reload]);
const projects = (statusQ.data?.projects || []).slice().sort((a, b) => b.priority - a.priority);
const health = statusQ.data?.health || {};
const ultimate = statusQ.data?.ultimate_priority;
const activeN = projects.filter(p => p.status === 'active').length;
const allOk = health.M1 && health.router && health.embedder;
const dotSt = (ok) => ok ? '' : 'fail';
const statusColor = (s) =>
s === 'active' ? 'var(--ag-ok)' :
s === 'paused' ? 'var(--ag-warn)' :
s === 'done' ? 'var(--ag-info)' :
'var(--tg-theme-hint-color)';
return (
);
}
// ── Settings ──────────────────────────────────────────────────
function SettingsMode() {
const modeQ = useApi(() => API.settingsMode(), []);
const [setting, setSetting] = React.useState(null); // local optimistic
const [saving, setSaving] = React.useState(false);
const currentMode = setting ?? modeQ.data?.mode ?? 'balanced';
const modes = [
{ id: 'fast', title: 'Fast', desc: 'Быстрые ответы без рассуждений', icon: 'bolt', color: 'orange', reasoning: 0, ctx: 8192 },
{ id: 'balanced', title: 'Balanced', desc: 'Стандартная работа · оптимум', icon: 'spark', color: 'blue', reasoning: 1024, ctx: 65536 },
{ id: 'deep', title: 'Deep', desc: 'Сложные задачи · глубокий анализ', icon: 'cpu', color: 'violet', reasoning: 4096, ctx: 131072 },
];
const setMode = async (m) => {
setSetting(m);
setSaving(true);
try {
await API.setSettingsMode(m);
} catch (e) {
setSetting(null);
alert('Ошибка: ' + e.message);
} finally {
setSaving(false);
}
};
const activeMode = modes.find(m => m.id === currentMode) || modes[1];
const ctxK = (activeMode.ctx / 1024).toFixed(activeMode.ctx >= 102400 ? 0 : 0);
const projectCmds = [
['/newproject {name}', 'Создать новый проект (setup mode)', 'plus', 'blue'],
['/expand {project}', 'Перевести setup → expansion', 'spark', 'violet'],
['/pause {project}', 'Поставить на паузу', 'pause', 'orange'],
['/resume {project}', 'Возобновить выполнение', 'play', 'green'],
['/redefine_scope {project} "{desc}"', 'Consilium для пересмотра scope', 'cpu', 'violet'],
];
const endpoints = [
['POST', '/settings/mode', 'смена режима инференса', 'post'],
['GET', '/api/v1', 'базовый REST API', 'get'],
['POST', '/auth · JWT 24h', 'выдача access-токена', 'post'],
];
return (
{/* — Mode picker — */}
режим работы
{modeQ.loading
?
: modes.map(m => (
!saving && setMode(m.id)}
>
))}
Применяется ко всем новым AI-задачам{saving ? ' · сохранение…' : '.'}
{/* — Inference params (live values for active mode) — */}
параметры инференса
reasoning
{activeMode.reasoning}tok
Активные значения для режима {currentMode}.
{/* — Bot commands cheatsheet — */}
команды бота
{projectCmds.map(([cmd, desc, ico, color]) => (
))}
Команды доступны в Telegram-боте.
{/* — API endpoints (developer reference) — */}
api endpoints
{endpoints.map(([method, path, desc, cls]) => (
))}
{/* — About — */}
Kinley Agent · v1.0.0
);
}
Object.assign(window, { InjectMode, StatusMode, SettingsMode });