feat: add user-level WeChat channel accounts

This commit is contained in:
2026-09-14 00:26:29 +08:00
parent 46e7f6f1cd
commit b7d8b1298f
15 changed files with 575 additions and 63 deletions

View File

@@ -253,16 +253,18 @@ export class CloudAgentsModule {
await this.journal.remove(binding.accountKey, id, () => this.requireCurrent(binding));
return { discarded: true };
}
if (pending.operation === 'wechatBindVerification') {
if (pending.operation === 'wechatBindVerification' || pending.operation === 'channelAccountWechatBindVerification') {
// The verification code is intentionally never persisted. First recover the
// durable provider receipt: the code may already have been consumed even if
// the response was lost. Only an absent receipt requires new input.
const pendingInput = record(pending.input);
const pendingSlug = typeof pendingInput.slug === 'string' ? pendingInput.slug : undefined;
const operationId = typeof pendingInput.operation_id === 'string' ? pendingInput.operation_id : undefined;
if (pendingSlug && operationId) {
if (operationId && (pending.operation === 'channelAccountWechatBindVerification' || pendingSlug)) {
try {
const receipt = await this.execute({ operation: 'channelOperation', input: { slug: pendingSlug, operation_id: operationId } });
const receipt = await this.execute(pending.operation === 'channelAccountWechatBindVerification'
? { operation: 'channelAccountOperation', input: { operation_id: operationId } }
: { operation: 'channelOperation', input: { slug: pendingSlug, operation_id: operationId } });
const status = typeof record(receipt).status === 'string' ? String(record(receipt).status).toLowerCase() : '';
const terminal = ['completed', 'complete', 'succeeded', 'success', 'failed', 'rejected', 'cancelled', 'canceled', 'expired', 'error'].includes(status);
if (terminal) await this.journal.remove(binding.accountKey, id, () => this.requireCurrent(binding));
@@ -332,12 +334,14 @@ export class CloudAgentsModule {
catch { throw new CloudAgentsError(422, 'invalid_input'); }
const invoke = async () => plan.project(await this.request(plan.path, plan.method, plan.body, plan.cloud === 'ws'));
const recoverable = ['submit', 'preview', 'resume', 'publish', 'createApplication', 'createKey', 'createKnowledge', 'processKnowledge', 'createSchedule', 'runSchedule', 'importKnowledgeAttachment', 'createChild',
'createChannelAccount', 'routeChannelAccount', 'enableChannelAccount', 'pauseChannelAccount', 'disconnectChannelAccount',
'channelAccountWechatBindStart', 'channelAccountWechatBindVerification', 'channelAccountWechatUnbind',
'createChannelBinding', 'switchChannelBindingAgent', 'enableChannelBinding', 'pauseChannelBinding', 'disconnectChannelBinding',
'channelPolicy', 'createChannelPairing', 'wechatBindStart', 'wechatBindVerification', 'wechatUnbind', 'revokeChannelCaller',
'channelConversationControl', 'retryChannelDeliveryPart'];
let result: unknown;
if (recoverable.includes(plan.operation)) {
const journalInput = plan.operation === 'wechatBindVerification'
const journalInput = plan.operation === 'wechatBindVerification' || plan.operation === 'channelAccountWechatBindVerification'
? Object.fromEntries(Object.entries(plan.input).filter(([key]) => key !== 'verify_code'))
: plan.input;
result = await this.withRecovery(plan.operation, journalInput, invoke);