From 5ec6f113cd70696e230517375d4893aa1acabbc2 Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 8 Mar 2026 05:12:13 -0700 Subject: [PATCH] feat(agent): DingTalk channel binding support in instance controller + system prompt - agent-instance.controller.ts: accept dingTalkClientId/dingTalkClientSecret in POST /instances body, forward to deploy service - system-prompt-builder.ts: add DingTalk 5-step binding guide for iAgent so the AI can walk users through connecting their DingTalk account Co-Authored-By: Claude Sonnet 4.6 --- .../engines/claude-code-cli/system-prompt-builder.ts | 11 ++++++++++- .../rest/controllers/agent-instance.controller.ts | 10 ++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/services/agent-service/src/infrastructure/engines/claude-code-cli/system-prompt-builder.ts b/packages/services/agent-service/src/infrastructure/engines/claude-code-cli/system-prompt-builder.ts index 8d8f624..669b254 100644 --- a/packages/services/agent-service/src/infrastructure/engines/claude-code-cli/system-prompt-builder.ts +++ b/packages/services/agent-service/src/infrastructure/engines/claude-code-cli/system-prompt-builder.ts @@ -44,7 +44,16 @@ export class SystemPromptBuilder { 'When a user asks to create a OpenClaw / 小龙虾 / personal AI agent / autonomous agent:\n' + '1. Ask for a name if not given\n' + '2. Use the Current User ID from this prompt as userId\n' + - '3. Call the create API with Bash and report the result (id, status, containerName)', + '3. Call the create API with Bash and report the result (id, status, containerName)\n\n' + + '## DingTalk Channel Binding (钉钉接入)\n' + + 'To bind a DingTalk bot to an OpenClaw instance, the user needs a DingTalk app with Stream mode enabled:\n' + + ' Step 1: Go to https://open.dingtalk.com/developer → "创建应用" → Enterprise Internal App\n' + + ' Step 2: Enable "机器人" → set message receive mode to "Stream模式"\n' + + ' Step 3: Copy the AppKey (ClientId) and AppSecret (ClientSecret)\n' + + ' Step 4: Create/re-deploy the OpenClaw instance with DingTalk creds:\n' + + ' wget -q -O- --post-data=\'{"name":"","userId":"","usePool":true,"dingTalkClientId":"","dingTalkClientSecret":""}\' \\\n' + + ' --header="Content-Type: application/json" http://localhost:3002/api/v1/agent/instances\n' + + ' Step 5: Tell the user to add the bot to a DingTalk group or send it a direct message — it will respond via OpenClaw AI.', ); // Tenant + user context diff --git a/packages/services/agent-service/src/interfaces/rest/controllers/agent-instance.controller.ts b/packages/services/agent-service/src/interfaces/rest/controllers/agent-instance.controller.ts index 6d19d77..7ca6a94 100644 --- a/packages/services/agent-service/src/interfaces/rest/controllers/agent-instance.controller.ts +++ b/packages/services/agent-service/src/interfaces/rest/controllers/agent-instance.controller.ts @@ -51,6 +51,9 @@ export class AgentInstanceController { sshPort?: number; sshUser?: string; sshKey?: string; // plaintext private key, only used if usePool=false + // Optional: DingTalk channel binding + dingTalkClientId?: string; + dingTalkClientSecret?: string; }) { if (!body.name || !body.userId) { throw new BadRequestException('name and userId are required'); @@ -66,9 +69,12 @@ export class AgentInstanceController { instance.config = {}; instance.hostPort = 0; // Will be set by deploy service + const dtClientId = body.dingTalkClientId; + const dtClientSecret = body.dingTalkClientSecret; + if (body.usePool !== false) { await this.instanceRepo.save(instance); - this.deployService.deployFromPool(instance, this.claudeApiKey) + this.deployService.deployFromPool(instance, this.claudeApiKey, dtClientId, dtClientSecret) .then(() => this.instanceRepo.save(instance)) .catch(async (err) => { instance.status = 'error'; @@ -83,7 +89,7 @@ export class AgentInstanceController { instance.sshPort = body.sshPort ?? 22; instance.sshUser = body.sshUser; await this.instanceRepo.save(instance); - this.deployService.deployToUserServer(instance, body.sshKey, this.claudeApiKey) + this.deployService.deployToUserServer(instance, body.sshKey, this.claudeApiKey, dtClientId, dtClientSecret) .then(() => this.instanceRepo.save(instance)) .catch(async (err) => { instance.status = 'error';