feat: prepare Zhinian desktop client for pilot release
This commit is contained in:
@@ -12,6 +12,25 @@ const MAIN_AGENT_ID = 'main';
|
||||
const MAIN_AGENT_NAME = 'Main Agent';
|
||||
const DEFAULT_ACCOUNT_ID = 'default';
|
||||
const DEFAULT_WORKSPACE_PATH = '~/.openclaw/workspace';
|
||||
const CHANNEL_AGENT_LABELS: Record<string, string> = {
|
||||
wechat: '微信',
|
||||
wecom: '企业微信',
|
||||
dingtalk: '钉钉',
|
||||
feishu: '飞书',
|
||||
lark: '飞书',
|
||||
telegram: 'Telegram',
|
||||
whatsapp: 'WhatsApp',
|
||||
discord: 'Discord',
|
||||
signal: 'Signal',
|
||||
imessage: 'iMessage',
|
||||
matrix: 'Matrix',
|
||||
line: 'LINE',
|
||||
msteams: 'Teams',
|
||||
googlechat: 'Google Chat',
|
||||
mattermost: 'Mattermost',
|
||||
qqbot: 'QQ',
|
||||
agentbus: 'AgentBus',
|
||||
};
|
||||
const AGENT_BOOTSTRAP_FILES = [
|
||||
'AGENTS.md',
|
||||
'SOUL.md',
|
||||
@@ -144,6 +163,30 @@ function slugifyAgentId(name: string): string {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function normalizeChannelAgentIdSegment(value: string): string {
|
||||
const normalized = value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9_-]+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/^-|-$/g, '')
|
||||
.slice(0, 48);
|
||||
return normalized || 'default';
|
||||
}
|
||||
|
||||
function buildChannelAgentIdentity(channelType: string, accountId: string): { id: string; name: string } {
|
||||
const uiChannelType = toUiChannelType(channelType);
|
||||
const channelSegment = normalizeChannelAgentIdSegment(uiChannelType);
|
||||
const accountSegment = normalizeChannelAgentIdSegment(accountId || DEFAULT_ACCOUNT_ID);
|
||||
const label = CHANNEL_AGENT_LABELS[uiChannelType] ?? uiChannelType;
|
||||
const isDefaultAccount = accountSegment === DEFAULT_ACCOUNT_ID;
|
||||
|
||||
return {
|
||||
id: isDefaultAccount ? `channel-${channelSegment}` : `channel-${channelSegment}-${accountSegment}`,
|
||||
name: isDefaultAccount ? `${label}助手` : `${label}助手 ${accountId}`,
|
||||
};
|
||||
}
|
||||
|
||||
async function fileExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await access(path, constants.F_OK);
|
||||
@@ -775,6 +818,54 @@ export async function assignChannelAccountToAgent(
|
||||
});
|
||||
}
|
||||
|
||||
export async function ensureChannelAgentForAccount(
|
||||
channelType: string,
|
||||
accountId = DEFAULT_ACCOUNT_ID,
|
||||
): Promise<AgentsSnapshot> {
|
||||
return withConfigLock(async () => {
|
||||
const normalizedAccountId = accountId.trim() || DEFAULT_ACCOUNT_ID;
|
||||
const config = await readOpenClawConfig() as AgentConfigDocument;
|
||||
const { agentsConfig, entries, syntheticMain } = normalizeAgentsConfig(config);
|
||||
const identity = buildChannelAgentIdentity(channelType, normalizedAccountId);
|
||||
const nextEntries = syntheticMain
|
||||
? [createImplicitMainEntry(config), ...entries.filter((_, index) => index > 0)]
|
||||
: [...entries];
|
||||
|
||||
let agentEntry = nextEntries.find((entry) => entry.id === identity.id);
|
||||
if (!agentEntry) {
|
||||
agentEntry = {
|
||||
id: identity.id,
|
||||
name: identity.name,
|
||||
workspace: `~/.openclaw/workspace-${identity.id}`,
|
||||
agentDir: getDefaultAgentDirPath(identity.id),
|
||||
};
|
||||
nextEntries.push(agentEntry);
|
||||
await provisionAgentFilesystem(config, agentEntry, { inheritWorkspace: true });
|
||||
logger.info('Created channel agent config entry', {
|
||||
agentId: identity.id,
|
||||
channelType,
|
||||
accountId: normalizedAccountId,
|
||||
});
|
||||
} else if (!agentEntry.name) {
|
||||
agentEntry.name = identity.name;
|
||||
}
|
||||
|
||||
config.agents = {
|
||||
...agentsConfig,
|
||||
list: nextEntries,
|
||||
};
|
||||
config.bindings = upsertBindingsForChannel(config.bindings, channelType, identity.id, normalizedAccountId);
|
||||
|
||||
await writeOpenClawConfig(config);
|
||||
logger.info('Ensured channel agent binding', {
|
||||
agentId: identity.id,
|
||||
channelType,
|
||||
accountId: normalizedAccountId,
|
||||
});
|
||||
return buildSnapshotFromConfig(config);
|
||||
});
|
||||
}
|
||||
|
||||
export async function clearChannelBinding(channelType: string, accountId?: string): Promise<AgentsSnapshot> {
|
||||
return withConfigLock(async () => {
|
||||
const config = await readOpenClawConfig() as AgentConfigDocument;
|
||||
|
||||
Reference in New Issue
Block a user