☰
CC Workflow Studio Slack 集成:Extension Host 与 Webview 消息契约全解析
2026/10/13 3:11:51 网站建设 项目流程
  • AI 应用
  • Agent 工作流
  • 开发工具
  • MCP 服务

【免费下载链接】cc-wf-studio

CC Workflow Studio

项目地址:https://gitcode.com/gh_mirrors/cc/cc-wf-studio
点击查看免费下载

本篇技术指南围绕 CC Workflow Studio(VS Code 扩展)中 Slack 集成工作流共享功能的Extension Host 与 Webview 之间的消息传递 API 契约(定义于 extension-host-api-contracts.md)展开,详细讲解 Webview 如何通过 6 个 Command 消息驱动扩展宿主完成 Slack 连接、频道拉取、工作流共享/导入/搜索,以及扩展宿主如何通过 9 个 Event 消息回传结果。读完本文,你将掌握这套双向消息协议的完整字段语义、错误码约定、典型交互时序,并能对照仓库中的 TypeScript 类型定义与命令处理器源码,自行实现或调试基于postMessage的 VS Code 扩展与 Webview 通信层。

背景:这套契约解决什么问题

CC Workflow Studio 允许开发者在 VS Code 画布上以 JSON 形式编排工作流(节点、连线、参数),而 Slack 集成功能(对应功能规格 spec.md)把"创建"延伸到"协作":把工作流直接共享到团队 Slack 频道,以富消息卡片呈现;团队成员点击卡片即可一键导入自己的 VS Code;还支持在 VS Code 内检索历史共享的工作流并复用。

由于 Webview(前端 React 画布)与 Extension Host(Node 侧扩展宿主)运行在不同的 JavaScript 上下文,二者无法共享内存或直接调用函数,只能通过vscode.postMessage()传递可序列化的 JSON 消息。本契约文档正是这一通信层的"协议定稿":它规定了每一条消息的类型名(type)、负载结构(payload)、成功/失败事件以及多步交互的时序。仓库中对应的 TypeScript 实现位于 slack-messages.ts,文件头注释明确标注其依据即本契约文档,二者一一对应,可直接对照阅读。

通用消息格式

所有 Webview 与 Extension Host 之间的消息都遵循统一的信封结构:

export interface WebviewMessage<T = any> { type: string; payload: T; }
  • type:消息类型字符串,是双方路由的唯一依据;
  • payload:携带业务数据的负载对象,随消息类型不同而结构各异。

实际运行时(见 messages.ts 的Message接口),消息还会携带可选的requestId字段用于请求-响应关联:Webview 端每次调用都会生成形如req-${Date.now()}-${Math.random()}的唯一 ID(参见 slack-integration-service.ts),Extension Host 回传事件时原样附带该requestId,Webview 据此把异步事件匹配回对应的 Promise。这套模式贯穿全部 Slack 命令。

Webview → Extension Host:6 个 Command 消息

契约文档定义了 6 条命令,全部由 Webview 发起(例如用户点击共享按钮、选择频道后触发)。

1.SLACK_CONNECT:启动连接

发起 Slack 工作区连接(OAuth 认证流程),payload 为空对象。

interface SlackConnectCommand { type: 'SLACK_CONNECT'; payload: Record<string, never>; // 空对象 }
vscode.postMessage({ type: 'SLACK_CONNECT', payload: {} });

响应事件:成功SLACK_CONNECT_SUCCESS,失败SLACK_CONNECT_FAILED。

从源码看,Webview 侧的connectToSlack()与reconnectToSlack()(强制重连场景会附加forceReconnect: true)都会发送此命令,并设置 5 分钟超时以容忍完整的 OAuth 浏览器流程。OAuth 的具体实现另有扩展(SLACK_CONNECT_OAUTH/SLACK_OAUTH_*系列事件,见 slack-integration-service.ts),而本契约中的SLACK_CONNECT主要面向手动令牌接入场景——handleConnectSlackManual()(见 slack-connect-manual.ts)会弹出输入框要求用户粘贴以xoxp-开头的 User OAuth Token,随后调用 Slackauth.test接口自动解析出工作区 ID 与名称,最后经 slack-token-manager.ts 的storeManualConnection()存入 VSCode Secret Storage。

2.SLACK_DISCONNECT:断开连接

断开与 Slack 工作区的连接(删除已保存的令牌),payload 为空对象。

interface SlackDisconnectCommand { type: 'SLACK_DISCONNECT'; payload: Record<string, never>; }

响应事件:成功SLACK_DISCONNECT_SUCCESS,失败SLACK_DISCONNECT_FAILED。令牌管理器中clearConnection()/clearConnectionByWorkspaceId()会逐工作区删除 Secret Storage 中的令牌与元数据,并同步维护工作区列表。

3.GET_SLACK_CHANNELS:获取频道列表

拉取 Slack 频道列表,供共享目标选择。两个可选过滤参数均有默认值:

interface GetSlackChannelsCommand { type: 'GET_SLACK_CHANNELS'; payload: { includePrivate?: boolean; // 是否包含私密频道 (default: true) onlyMember?: boolean; // 仅返回自己已加入的频道 (default: true) }; }
vscode.postMessage({ type: 'GET_SLACK_CHANNELS', payload: { includePrivate: true, onlyMember: true } });

响应事件:成功GET_SLACK_CHANNELS_SUCCESS,失败GET_SLACK_CHANNELS_FAILED。

底层实现见 slack-api-service.ts 的getChannels():它通过conversations.listAPI 拉取,includePrivate决定types是否拼接private_channel,onlyMember通过is_member字段在客户端过滤,并以limit: 100+cursor游标循环分页直至取完。注意,实际请求使用的是当前登录用户的 User Token(xoxp-)而非 Bot Token,确保用户只能看到自己有权访问的频道。

4.SHARE_WORKFLOW_TO_SLACK:共享工作流

将工作流共享到指定 Slack 频道,是整套功能的核心命令:

interface ShareWorkflowToSlackCommand { type: 'SHARE_WORKFLOW_TO_SLACK'; payload: { workflowId: string; // 待共享的工作流 ID workflowName: string; // 工作流名称 channelId: string; // 目标频道 ID description?: string; // 工作流描述 (optional) overrideSensitiveWarning?: boolean; // 是否忽略机敏信息警告 (default: false) }; }
vscode.postMessage({ type: 'SHARE_WORKFLOW_TO_SLACK', payload: { workflowId: 'uuid-1234-5678', workflowName: 'My Data Processing Workflow', channelId: 'C01234ABCD', description: 'Workflow for daily data processing tasks', overrideSensitiveWarning: false } });

响应事件:

  • 成功:SHARE_WORKFLOW_SUCCESS
  • 检测到机敏信息(警告):SENSITIVE_DATA_WARNING
  • 失败:SHARE_WORKFLOW_FAILED

该命令在扩展宿主的完整处理链路见 slack-share-workflow.ts 的handleShareWorkflowToSlack(),实际执行分为 7 步:先做机敏信息检测(除非overrideSensitiveWarning: true);再提取节点数等元数据;随后调用postWorkflowMessage()用 Block Kit 发布富卡片主消息;接着以线程回复形式用files.uploadV2上传工作流 JSON 文件(文件名会先将非字母数字字符替换为_);再通过chat.update更新消息补全文件 ID 与深链;最后向 Webview 回发SHARE_WORKFLOW_SUCCESS并弹出"View in Slack"原生通知。机敏信息警告采用"警告但不强制阻断"策略(FR-006),用户选择继续后 Webview 会以overrideSensitiveWarning: true重新提交。

5.IMPORT_WORKFLOW_FROM_SLACK:从 Slack 导入工作流

从 Slack 消息导入工作流到本地:

interface ImportWorkflowFromSlackCommand { type: 'IMPORT_WORKFLOW_FROM_SLACK'; payload: { workflowId: string; // 待导入工作流 ID fileId: string; // Slack 文件 ID messageTs: string; // Slack 消息时间戳 channelId: string; // 频道 ID overwriteExisting?: boolean; // 是否覆盖已存在文件 (default: false) }; }
vscode.postMessage({ type: 'IMPORT_WORKFLOW_FROM_SLACK', payload: { workflowId: 'uuid-1234-5678', fileId: 'F01234ABCD', messageTs: '1234567890.123456', channelId: 'C01234ABCD', overwriteExisting: false } });

响应事件:

  • 成功:IMPORT_WORKFLOW_SUCCESS
  • 需要确认(文件已存在):IMPORT_WORKFLOW_CONFIRM_OVERWRITE
  • 失败:IMPORT_WORKFLOW_FAILED

扩展宿主侧实现见 slack-import-workflow.ts:先通过files.info+url_private下载文件内容(携带Authorization: Bearer头);再用核心包的validateWorkflowFile()校验 JSON 合法性、migrateWorkflow()做向后兼容迁移;随后选择工作区目录,将文件写入.vscode/workflows/{name}.json(目录不存在时自动创建);若文件已存在且未声明覆盖,则弹出模态确认框,用户拒绝时回发IMPORT_WORKFLOW_CANCELLED中止流程。成功事件 payload 中会附带完整的workflow对象,供 Webview 直接载入画布。

6.SEARCH_SLACK_WORKFLOWS:检索历史共享工作流

在 Slack 中检索过去共享过的工作流,全部过滤条件均为可选:

interface SearchSlackWorkflowsCommand { type: 'SEARCH_SLACK_WORKFLOWS'; payload: { query?: string; // 搜索关键词 (optional) channelId?: string; // 限定某频道内搜索 (optional) authorName?: string; // 按作者名过滤 (optional) fromDate?: string; // 起始日期 (ISO 8601) (optional) toDate?: string; // 结束日期 (ISO 8601) (optional) limit?: number; // 结果数量 (default: 20, max: 100) }; }
vscode.postMessage({ type: 'SEARCH_SLACK_WORKFLOWS', payload: { query: 'data processing', channelId: 'C01234ABCD', fromDate: '2025-11-01T00:00:00Z', limit: 20 } });

响应事件:成功SEARCH_SLACK_WORKFLOWS_SUCCESS,失败SEARCH_SLACK_WORKFLOWS_FAILED。

底层实现(slack-api-service.ts 的searchWorkflows())调用search.messagesAPI,查询串固定以workflow filename:*.json为基底,追加用户关键词并以in:<#频道ID>限定频道;count通过Math.min(count || 20, 100)钳制在 100 以内,默认按timestamp排序。文档同时提到authorName、fromDate、toDate等过滤维度,它们在 UI 层可组合为更精细的筛选条件。

Extension Host → Webview:9 个 Event 事件

以下事件均由 Extension Host 主动推送给 Webview,用于回报命令执行结果或请求用户确认。

2.1SLACK_CONNECT_SUCCESS

Slack 连接成功时发送,携带工作区与用户信息:

{ "type": "SLACK_CONNECT_SUCCESS", "payload": { "workspaceId": "T01234IJKL", "workspaceName": "My Team Workspace", "userId": "U01234ABCD", "authorizedAt": "2025-11-22T10:00:00Z" } }

2.2SLACK_CONNECT_FAILED

连接失败时发送。错误码约定:

错误码含义
USER_CANCELLED用户取消了认证
OAUTH_FAILEDOAuth 认证失败
NETWORK_ERROR网络错误
UNKNOWN_ERROR其他错误
{ "type": "SLACK_CONNECT_FAILED", "payload": { "errorCode": "USER_CANCELLED", "errorMessage": "用户取消了认证。" } }

实现说明:仓库实际落地时(slack-messages.ts)将errorMessage升级为 i18n 体系——用messageKey+ 可选的suggestedActionKey替代硬编码文案,由 Webview 端按语言渲染,这也与扩展的多语言(en/ja/ko/zh-CN/zh-TW)架构一致。

2.3GET_SLACK_CHANNELS_SUCCESS

频道列表获取成功,channels为SlackChannel[]:

{ "type": "GET_SLACK_CHANNELS_SUCCESS", "payload": { "channels": [ { "id": "C01234ABCD", "name": "general", "isPrivate": false, "isMember": true, "memberCount": 25, "purpose": "General discussions", "topic": "Team announcements" }, { "id": "C56789EFGH", "name": "project-alpha", "isPrivate": true, "isMember": true, "memberCount": 5 } ] } }

SlackChannel结构定义(字段含义与数据模型文件 slack-integration-types.ts 一致):

interface SlackChannel { id: string; name: string; isPrivate: boolean; isMember: boolean; memberCount?: number; purpose?: string; topic?: string; }

2.4SENSITIVE_DATA_WARNING

检测到机敏信息时发送,请求用户确认,是共享流程中最重要的安全关卡:

interface SensitiveDataWarningEvent { type: 'SENSITIVE_DATA_WARNING'; payload: { workflowId: string; findings: SensitiveDataFinding[]; }; } interface SensitiveDataFinding { type: string; // 例如: 'AWS_ACCESS_KEY', 'API_KEY' maskedValue: string; // 例如: 'AKIA...X7Z9' position: number; context?: string; severity: 'low' | 'medium' | 'high'; }
{ "type": "SENSITIVE_DATA_WARNING", "payload": { "workflowId": "uuid-1234-5678", "findings": [ { "type": "AWS_ACCESS_KEY", "maskedValue": "AKIA...X7Z9", "position": 1234, "context": "...\"aws_access_key\": \"AKIA1234...\"...", "severity": "high" }, { "type": "API_KEY", "maskedValue": "sk-p...def9", "position": 5678, "severity": "medium" } ] } }

用户必须在"继续"与"取消"之间做出选择。检测引擎实现在 sensitive-data-detector.ts:内置 8 类正则模式,包括 AWS Access Key(AKIA+ 16 位)、AWS Secret Key、Slack Token(xox[bpoa]-前缀)、GitHub Token(ghp_前缀)、通用 API Key、通用 Token、私钥(-----BEGIN ... PRIVATE KEY-----)与密码;掩码策略为"仅保留前后各 4 个字符"(maskValue),上下文截取以[REDACTED]标记,且原始值永不落盘或回传。

2.5SHARE_WORKFLOW_SUCCESS

共享成功,返回消息定位信息:

{ "type": "SHARE_WORKFLOW_SUCCESS", "payload": { "workflowId": "uuid-1234-5678", "channelId": "C01234ABCD", "channelName": "general", "messageTs": "1234567890.123456", "fileId": "F01234ABCD", "permalink": "https://myteam.slack.com/archives/C01234ABCD/p1234567890123456" } }

permalink为 Slack 消息直链,Webview 收到后可引导用户跳转查看。

2.6SHARE_WORKFLOW_FAILED

共享失败。错误码是排查问题的关键索引:

错误码含义
NOT_AUTHENTICATED未连接 Slack
CHANNEL_NOT_FOUND频道不存在
NOT_IN_CHANNELBot 不是频道成员
FILE_TOO_LARGE文件超过大小限制(1MB)
RATE_LIMITED超过 Slack API Rate Limit
NETWORK_ERROR网络错误
UNKNOWN_ERROR其他错误
{ "type": "SHARE_WORKFLOW_FAILED", "payload": { "workflowId": "uuid-1234-5678", "errorCode": "NOT_IN_CHANNEL", "errorMessage": "Bot 未加入频道。请将 Bot 邀请进频道。" } }

实现中这些错误码由 slack-error-handler.ts 的handleSlackError()统一归一化:它把 Slack Web API 返回的invalid_auth、missing_scope、rate_limited、channel_not_found、not_in_channel等原生错误映射为带messageKey/suggestedActionKey/retryAfter的结构化信息,并额外识别自定义的WORKSPACE_NOT_CONNECTED、USER_TOKEN_REQUIRED错误;getRetryDelay()提供带抖动的指数退避算法用于应对限流。

2.7IMPORT_WORKFLOW_CONFIRM_OVERWRITE

目标文件已存在时请求确认覆盖:

{ "type": "IMPORT_WORKFLOW_CONFIRM_OVERWRITE", "payload": { "workflowId": "uuid-1234-5678", "existingFilePath": "/Users/.../workflows/my-workflow.json" } }

用户在"覆盖"与"取消"间选择。实际实现(slack-import-workflow.ts)在当前版本使用 VS Code 原生模态警告框完成确认,取消时回发IMPORT_WORKFLOW_CANCELLED让 Webview 关闭加载遮罩。

2.8IMPORT_WORKFLOW_SUCCESS

导入成功:

{ "type": "IMPORT_WORKFLOW_SUCCESS", "payload": { "workflowId": "uuid-1234-5678", "filePath": "/Users/.../workflows/my-workflow.json", "workflowName": "My Data Processing Workflow" } }

2.9SEARCH_SLACK_WORKFLOWS_SUCCESS

搜索成功,返回结果元数据列表与总数:

interface SearchSlackWorkflowsSuccessEvent { type: 'SEARCH_SLACK_WORKFLOWS_SUCCESS'; payload: { workflows: SharedWorkflowMetadata[]; total: number; }; } interface SharedWorkflowMetadata { id: string; name: string; description?: string; version: string; authorName: string; sharedAt: string; // ISO 8601 channelId: string; channelName: string; messageTs: string; fileId: string; fileUrl: string; nodeCount: number; tags?: string[]; permalink: string; }
{ "type": "SEARCH_SLACK_WORKFLOWS_SUCCESS", "payload": { "total": 5, "workflows": [ { "id": "uuid-1234-5678", "name": "Data Processing Workflow", "description": "Daily data processing tasks", "version": "1.0.0", "authorName": "John Doe", "sharedAt": "2025-11-22T10:00:00Z", "channelId": "C01234ABCD", "channelName": "general", "messageTs": "1234567890.123456", "fileId": "F01234ABCD", "fileUrl": "https://files.slack.com/files-pri/...", "nodeCount": 5, "tags": ["data", "processing"], "permalink": "https://myteam.slack.com/archives/C01234ABCD/p1234567890123456" } ] } }

数据模型文件 slack-integration-types.ts 还补充了authorEmail、hasSensitiveData、sensitiveDataOverride等字段,并给出了各字段的约束(如 name 1-100 字符、description 最长 500 字符、tags 最多 10 个且每个不超过 30 字符、version 使用语义化版本号),这些元数据被嵌入 Slack 消息的 Block Kit 中,构成搜索与一键导入的数据基础。

典型消息流程:三个时序示例

契约文档给出了三条最关键的多步交互时序,可视为状态机的权威描述。

3.1 Slack 连接流程

[Webview] SLACK_CONNECT ↓ [Extension Host] OAuth 认证开始 ↓ [Browser] 用户认证 ↓ [Extension Host] 令牌获取并保存 ↓ [Webview] SLACK_CONNECT_SUCCESS

3.2 工作流共享流程(含机敏信息)

[Webview] SHARE_WORKFLOW_TO_SLACK ↓ [Extension Host] 机敏信息检测 ↓ [Webview] SENSITIVE_DATA_WARNING ↓ [User] 选择"继续" ↓ [Webview] SHARE_WORKFLOW_TO_SLACK (overrideSensitiveWarning: true) ↓ [Extension Host] Slack API 调用 ↓ [Webview] SHARE_WORKFLOW_SUCCESS

这条流程在 UI 上的落地可参考 SlackShareDialog.tsx:首次shareWorkflowToSlack()固定传overrideSensitiveWarning: false,若返回sensitiveDataWarning则切换为警告视图逐条展示type (severity)与maskedValue;用户点"继续"(handleShareOverride)才以overrideSensitiveWarning: true重新提交。成功后会保存"上次共享频道"(SET_LAST_SHARED_CHANNEL),下次打开对话框时自动预选。

3.3 工作流导入流程(覆盖确认)

[Webview] IMPORT_WORKFLOW_FROM_SLACK ↓ [Extension Host] 文件存在性检查 ↓ [Webview] IMPORT_WORKFLOW_CONFIRM_OVERWRITE ↓ [User] 选择"覆盖" ↓ [Webview] IMPORT_WORKFLOW_FROM_SLACK (overwriteExisting: true) ↓ [Extension Host] 文件下载并保存 ↓ [Webview] IMPORT_WORKFLOW_SUCCESS

完整 TypeScript 类型定义

契约文档以联合类型收束全部命令与事件,便于在 Webview 端做穷尽式路由(switch/if 判断):

// Commands (Webview → Extension Host) export type WebviewToExtensionCommand = | SlackConnectCommand | SlackDisconnectCommand | GetSlackChannelsCommand | ShareWorkflowToSlackCommand | ImportWorkflowFromSlackCommand | SearchSlackWorkflowsCommand; // Events (Extension Host → Webview) export type ExtensionToWebviewEvent = | SlackConnectSuccessEvent | SlackConnectFailedEvent | GetSlackChannelsSuccessEvent | GetSlackChannelsFailedEvent | SensitiveDataWarningEvent | ShareWorkflowSuccessEvent | ShareWorkflowFailedEvent | ImportWorkflowConfirmOverwriteEvent | ImportWorkflowSuccessEvent | ImportWorkflowFailedEvent | SearchSlackWorkflowsSuccessEvent | SearchSlackWorkflowsFailedEvent; // Message format export interface WebviewMessage<T = any> { type: string; payload: T; }

仓库实现(slack-messages.ts 与 slack-messages.ts)在契约基础上还补充了SLACK_DISCONNECT_SUCCESS、SLACK_DISCONNECT_FAILED、IMPORT_WORKFLOW_FAILED事件以及失败事件的 i18n 字段(messageKey、suggestedActionKey、messageParams、workspaceId、workspaceName等),Webview 侧的统一 Promise 封装见 slack-integration-service.ts(含SlackError自定义错误类与 30 秒默认超时)。

消息契约背后的工程要点

  • 请求-响应关联:所有命令都通过requestId与对应事件配对,Webview 用一次性事件监听器实现 Promise 化,规避了多请求并发时的串扰。
  • 安全红线:令牌一律存入 VSCode Secret Storage(明文保存被明确禁止,对应数据模型中的SlackWorkspaceConnection与 slack-token-manager.ts);机敏信息检测结果只回传掩码值与脱敏上下文;Slack 富卡片内嵌的"Import to VS Code"按钮基于自定义 URI scheme(vscode://breaking-brake.cc-wf-studio/import?...,工作区名以 Base64 编码传递),构建逻辑见 slack-message-builder.ts。
  • 可扩展性:ExtensionToWebviewEvent联合类型让 Webview 可以静态穷举所有事件分支;而底层 Slack 调用统一收敛在SlackApiService一个类中,与SlackTokenManager、handleSlackError形成"令牌管理 → 客户端初始化 → API 调用 → 错误归一化"的清晰分层,为后续新增命令(如批量共享、跨频道转发)预留了扩展位。

以上契约与实现共同支撑了功能规格中共享(FR-001~FR-007)、导入(FR-008~FR-014)、搜索(FR-015~FR-018)与令牌管理(FR-019~FR-023)等全部功能需求。若要深入验证某条消息的具体行为,可直接对照 extension-host-api-contracts.md、slack-messages.ts 与对应命令处理器三份材料逐行比对。

  • AI 应用
  • Agent 工作流
  • 开发工具
  • MCP 服务

【免费下载链接】cc-wf-studio

CC Workflow Studio

项目地址:https://gitcode.com/gh_mirrors/cc/cc-wf-studio
点击查看免费下载

相关推荐

上一篇:weathr动画架构拆解:14个动画系统如何在3层渲染管线上协作
下一篇:24G显存如何本地跑通12B开源大模型?Mobius大模型完全入门指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询