lightclawbot
Advanced tools
| import * as fs from 'node:fs'; | ||
| import * as path from 'node:path'; | ||
| export const LIGHTSOUL_CONFIG_PATH = path.join(process.env.HOME || '', '.config/lightsoul/config.json'); | ||
| export function parseSoulIdMap(raw) { | ||
| const map = {}; | ||
| let parsed; | ||
| try { | ||
| parsed = JSON.parse(raw); | ||
| } | ||
| catch (error) { | ||
| console.error('Failed to parse lightsoul config:', error); | ||
| return map; | ||
| } | ||
| if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { | ||
| return map; | ||
| } | ||
| for (const [agentId, value] of Object.entries(parsed)) { | ||
| if (typeof value === 'string' && value.trim()) { | ||
| map[agentId] = value.trim(); | ||
| continue; | ||
| } | ||
| if (value && typeof value === 'object' && !Array.isArray(value)) { | ||
| const soulId = value.soulId; | ||
| if (typeof soulId === 'string' && soulId.trim()) { | ||
| map[agentId] = soulId.trim(); | ||
| } | ||
| } | ||
| } | ||
| return map; | ||
| } | ||
| export function readSoulIdMap(configPath = LIGHTSOUL_CONFIG_PATH) { | ||
| return parseSoulIdMap(fs.readFileSync(configPath, 'utf8')); | ||
| } | ||
| export function attachSoulIdsToAgents(agents, soulMap) { | ||
| return agents.map((agent) => { | ||
| const id = typeof agent.id === 'string' ? agent.id.trim() : ''; | ||
| const name = typeof agent.name === 'string' ? agent.name.trim() : ''; | ||
| const soulId = (id && soulMap[id]) || (name && soulMap[name]) || undefined; | ||
| return soulId ? { ...agent, soulId } : agent; | ||
| }); | ||
| } |
@@ -23,5 +23,49 @@ /** | ||
| import { ensureSessionInHistory, readChatsFile, resolveChatsFilePath } from '../utils/common.js'; | ||
| import { attachSoulIdsToAgents, readSoulIdMap, LIGHTSOUL_CONFIG_PATH } from './agent-soul.js'; | ||
| import * as fs from 'node:fs'; | ||
| import * as path from 'node:path'; | ||
| const getNonEmptyString = (value) => { | ||
| if (typeof value !== 'string') | ||
| return undefined; | ||
| const trimmed = value.trim(); | ||
| return trimmed || undefined; | ||
| }; | ||
| const isMainAgent = (agent) => { | ||
| const id = getNonEmptyString(agent.id); | ||
| const name = getNonEmptyString(agent.name); | ||
| return id === DEFAULT_AGENT_ID || name === DEFAULT_AGENT_ID; | ||
| }; | ||
| const withDefaultWorkspace = (agent, defaultWorkspace) => { | ||
| if (!defaultWorkspace || getNonEmptyString(agent.workspace)) | ||
| return agent; | ||
| return { ...agent, workspace: defaultWorkspace }; | ||
| }; | ||
| /** | ||
| * 组装 agents:request 响应列表,确保单 agent 场景也返回默认 main agent。 | ||
| */ | ||
| export const normalizeAgentsWithMainAgent = (currentCfg) => { | ||
| const agentsConfig = currentCfg.agents; | ||
| const agentsList = Array.isArray(agentsConfig?.list) ? agentsConfig.list : []; | ||
| const defaultWorkspace = getNonEmptyString(agentsConfig?.defaults?.workspace); | ||
| let hasMainAgent = false; | ||
| const normalizedAgents = agentsList.map((agent) => { | ||
| if (!isMainAgent(agent)) | ||
| return agent; | ||
| hasMainAgent = true; | ||
| return withDefaultWorkspace(agent, defaultWorkspace); | ||
| }); | ||
| if (hasMainAgent) | ||
| return normalizedAgents; | ||
| const mainAgent = { | ||
| id: DEFAULT_AGENT_ID, | ||
| name: DEFAULT_AGENT_ID, | ||
| displayName: DEFAULT_AGENT_ID, | ||
| isDefault: true, | ||
| }; | ||
| if (defaultWorkspace) { | ||
| mainAgent.workspace = defaultWorkspace; | ||
| } | ||
| return [mainAgent, ...normalizedAgents]; | ||
| }; | ||
| /** | ||
| * 绑定所有 Socket.IO 事件监听器到指定 socket 实例。 | ||
@@ -271,3 +315,3 @@ * | ||
| // 事件:Agents 列表请求(agents:request) | ||
| // 职责:读取 openclaw.json 中 agents.list 并返回给客户端(支持热更新,不缓存) | ||
| // 职责:读取 openclaw.json 中 agents.list,并在单 agent 场景补齐默认 main(支持热更新,不缓存) | ||
| // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
@@ -285,4 +329,11 @@ socket.on(EVENT_AGENTS_REQUEST, (data, ack) => { | ||
| const currentCfg = pluginRuntime.config.loadConfig(); | ||
| const agentsList = currentCfg.agents?.list ?? []; | ||
| log?.info(`[${CHANNEL_KEY}] Agents request: count=${agentsList.length}`); | ||
| const agentsList = normalizeAgentsWithMainAgent(currentCfg); | ||
| let agentsWithSoulIds = agentsList; | ||
| try { | ||
| agentsWithSoulIds = attachSoulIdsToAgents(agentsList, readSoulIdMap()); | ||
| } | ||
| catch (soulErr) { | ||
| log?.warn(`[${CHANNEL_KEY}] Failed to read soul config ${LIGHTSOUL_CONFIG_PATH}: ${soulErr instanceof Error ? soulErr.message : String(soulErr)}`); | ||
| } | ||
| log?.info(`[${CHANNEL_KEY}] Agents request: count=${agentsWithSoulIds.length}`); | ||
| const agentsMsgId = generateMsgId(); | ||
@@ -293,3 +344,3 @@ reliableEmitter.emitWithAck(EVENT_AGENTS_RESPONSE, { | ||
| from: botClientId, | ||
| agents: agentsList, | ||
| agents: agentsWithSoulIds, | ||
| timestamp: Date.now(), | ||
@@ -296,0 +347,0 @@ }, agentsMsgId); |
+1
-1
| { | ||
| "name": "lightclawbot", | ||
| "version": "1.2.6-beta.0", | ||
| "version": "1.2.6", | ||
| "description": "LightClawBot channel plugin with message support, cron jobs, and proactive messaging", | ||
@@ -5,0 +5,0 @@ "type": "module", |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
467391
0.76%67
1.52%10918
0.85%1
-50%7
16.67%