aa95f2132c
Implement Phase 3 channel adapters that decouple message sources from the agent via a uniform ChannelAdapter interface and ChannelRegistry. - Add ChannelAdapter/InboundMessage/OutboundMessage types - Add ChannelRegistry for adapter lifecycle and message routing - Add TelegramAdapter (grammy bot, auth middleware, confirmations, chunking) - Add WebChatAdapter (thin shim over GatewayServer) - Refactor daemon to use ChannelRegistry with per-channel-per-user agents - Add config.get/config.patch gateway handlers (Phase 2 loose end) - Add system.restart gateway handler (Phase 2 loose end) - Add implementation plans and design docs Tests: 225 passing (33 new channel adapter + gateway handler tests)
99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import type { GatewayRequest, OutboundMessage } from '../protocol.js';
|
|
import { makeResponse, makeError, ErrorCode } from '../protocol.js';
|
|
import type { Config } from '../../config/index.js';
|
|
|
|
export interface ConfigHandlerDeps {
|
|
config: Config;
|
|
}
|
|
|
|
/**
|
|
* Redact sensitive values from config before returning.
|
|
* Replaces API keys, tokens, and passwords with "***".
|
|
*/
|
|
function redactConfig(config: Config): Record<string, unknown> {
|
|
const raw = JSON.parse(JSON.stringify(config)) as Record<string, unknown>;
|
|
|
|
// Redact telegram bot token
|
|
const telegram = raw.telegram as Record<string, unknown> | undefined;
|
|
if (telegram?.bot_token) {
|
|
telegram.bot_token = '***';
|
|
}
|
|
|
|
// Redact model keys/tokens
|
|
const models = raw.models as Record<string, unknown> | undefined;
|
|
if (models) {
|
|
for (const tier of ['default', 'fast', 'complex', 'local'] as const) {
|
|
const m = models[tier] as Record<string, unknown> | undefined;
|
|
if (m?.api_key) m.api_key = '***';
|
|
if (m?.auth_token) m.auth_token = '***';
|
|
}
|
|
const localProviders = models.local_providers as Record<string, Record<string, unknown>> | undefined;
|
|
if (localProviders) {
|
|
for (const provider of Object.values(localProviders)) {
|
|
if (provider.api_key) provider.api_key = '***';
|
|
if (provider.auth_token) provider.auth_token = '***';
|
|
}
|
|
}
|
|
}
|
|
|
|
return raw;
|
|
}
|
|
|
|
/** Keys that are safe to update at runtime via config.patch. */
|
|
const PATCHABLE_KEYS: Record<string, (config: Config, value: unknown) => boolean> = {
|
|
'hooks.confirm': (config, value) => {
|
|
if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) return false;
|
|
config.hooks.confirm = value as string[];
|
|
return true;
|
|
},
|
|
'hooks.log': (config, value) => {
|
|
if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) return false;
|
|
config.hooks.log = value as string[];
|
|
return true;
|
|
},
|
|
'hooks.silent': (config, value) => {
|
|
if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) return false;
|
|
config.hooks.silent = value as string[];
|
|
return true;
|
|
},
|
|
'server.localhost': (config, value) => {
|
|
if (typeof value !== 'boolean') return false;
|
|
config.server.localhost = value;
|
|
return true;
|
|
},
|
|
};
|
|
|
|
export function createConfigHandlers(deps: ConfigHandlerDeps) {
|
|
return {
|
|
'config.get': async (request: GatewayRequest): Promise<OutboundMessage> => {
|
|
return makeResponse(request.id, redactConfig(deps.config));
|
|
},
|
|
|
|
'config.patch': async (request: GatewayRequest): Promise<OutboundMessage> => {
|
|
const patches = request.params?.patches;
|
|
if (!patches || typeof patches !== 'object' || Array.isArray(patches)) {
|
|
return makeError(request.id, ErrorCode.InvalidRequest, 'params.patches must be an object of { key: value } pairs');
|
|
}
|
|
|
|
const applied: string[] = [];
|
|
const rejected: string[] = [];
|
|
|
|
for (const [key, value] of Object.entries(patches as Record<string, unknown>)) {
|
|
const patcher = PATCHABLE_KEYS[key];
|
|
if (!patcher) {
|
|
rejected.push(key);
|
|
continue;
|
|
}
|
|
const ok = patcher(deps.config, value);
|
|
if (ok) {
|
|
applied.push(key);
|
|
} else {
|
|
rejected.push(key);
|
|
}
|
|
}
|
|
|
|
return makeResponse(request.id, { applied, rejected });
|
|
},
|
|
};
|
|
}
|