The main agent session uses key 'agent:main:main' which doesn't contain a channel ID. The session monitor now falls back to reading deliveryContext/lastTo from sessions.json and resolves 'user:XXXX' format via the Mattermost direct channel API. Fixes: status watcher not tracking the main agent's active transcript
123 lines
3.5 KiB
JavaScript
123 lines
3.5 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* config.js — Centralized env-var config with validation.
|
|
* All config is read from environment variables.
|
|
* Throws on missing required variables at startup.
|
|
*/
|
|
|
|
function getEnv(name, defaultValue, required = false) {
|
|
const val = process.env[name];
|
|
if (val === undefined || val === '') {
|
|
if (required) {
|
|
throw new Error(`Required environment variable ${name} is not set`);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
function getEnvInt(name, defaultValue, required = false) {
|
|
const val = getEnv(name, undefined, required);
|
|
if (val === undefined) return defaultValue;
|
|
const n = parseInt(val, 10);
|
|
if (isNaN(n)) throw new Error(`Environment variable ${name} must be an integer, got: ${val}`);
|
|
return n;
|
|
}
|
|
|
|
function getEnvBool(name, defaultValue) {
|
|
const val = process.env[name];
|
|
if (val === undefined || val === '') return defaultValue;
|
|
return val === '1' || val.toLowerCase() === 'true' || val.toLowerCase() === 'yes';
|
|
}
|
|
|
|
/**
|
|
* Build and validate the config object.
|
|
* Called once at startup; throws on invalid config.
|
|
*/
|
|
function buildConfig() {
|
|
const config = {
|
|
// Mattermost API
|
|
mm: {
|
|
token: getEnv('MM_BOT_TOKEN', null, true),
|
|
baseUrl: getEnv('MM_BASE_URL', 'https://slack.solio.tech'),
|
|
maxSockets: getEnvInt('MM_MAX_SOCKETS', 4),
|
|
botUserId: getEnv('MM_BOT_USER_ID', null),
|
|
},
|
|
|
|
// Transcript directory (OpenClaw agents)
|
|
transcriptDir: getEnv('TRANSCRIPT_DIR', '/home/node/.openclaw/agents'),
|
|
|
|
// Timing
|
|
throttleMs: getEnvInt('THROTTLE_MS', 500),
|
|
idleTimeoutS: getEnvInt('IDLE_TIMEOUT_S', 60),
|
|
sessionPollMs: getEnvInt('SESSION_POLL_MS', 2000),
|
|
|
|
// Limits
|
|
maxActiveSessions: getEnvInt('MAX_ACTIVE_SESSIONS', 20),
|
|
maxMessageChars: getEnvInt('MAX_MESSAGE_CHARS', 15000),
|
|
maxStatusLines: getEnvInt('MAX_STATUS_LINES', 20),
|
|
maxRetries: getEnvInt('MAX_RETRIES', 3),
|
|
|
|
// Circuit breaker
|
|
circuitBreakerThreshold: getEnvInt('CIRCUIT_BREAKER_THRESHOLD', 5),
|
|
circuitBreakerCooldownS: getEnvInt('CIRCUIT_BREAKER_COOLDOWN_S', 30),
|
|
|
|
// Health check
|
|
healthPort: getEnvInt('HEALTH_PORT', 9090),
|
|
|
|
// Logging
|
|
logLevel: getEnv('LOG_LEVEL', 'info'),
|
|
|
|
// PID file
|
|
pidFile: getEnv('PID_FILE', '/tmp/status-watcher.pid'),
|
|
|
|
// Offset persistence
|
|
offsetFile: getEnv('OFFSET_FILE', '/tmp/status-watcher-offsets.json'),
|
|
|
|
// Optional external tool labels override
|
|
toolLabelsFile: getEnv('TOOL_LABELS_FILE', null),
|
|
|
|
// Fallback channel for non-MM sessions (null = skip)
|
|
defaultChannel: getEnv('DEFAULT_CHANNEL', null),
|
|
|
|
// Feature flags
|
|
enableFsWatch: getEnvBool('ENABLE_FS_WATCH', true),
|
|
|
|
// Mattermost plugin integration (optional)
|
|
// When configured, updates are sent to the plugin instead of using PUT on posts
|
|
plugin: {
|
|
url: getEnv('PLUGIN_URL', null), // e.g. https://slack.solio.tech/plugins/com.openclaw.livestatus
|
|
secret: getEnv('PLUGIN_SECRET', null),
|
|
enabled: getEnvBool('PLUGIN_ENABLED', true),
|
|
detectIntervalMs: getEnvInt('PLUGIN_DETECT_INTERVAL_MS', 60000),
|
|
},
|
|
};
|
|
|
|
// Validate MM base URL
|
|
try {
|
|
new URL(config.mm.baseUrl);
|
|
} catch (_e) {
|
|
throw new Error(`MM_BASE_URL is not a valid URL: ${config.mm.baseUrl}`);
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
// Singleton — built once, exported
|
|
let _config = null;
|
|
|
|
function getConfig() {
|
|
if (!_config) {
|
|
_config = buildConfig();
|
|
}
|
|
return _config;
|
|
}
|
|
|
|
// Allow resetting config in tests
|
|
function resetConfig() {
|
|
_config = null;
|
|
}
|
|
|
|
module.exports = { getConfig, resetConfig, buildConfig };
|