CC Workflow Studio Schema-Driven 属性面板迁移实战:Phase 1 中 Prompt / Branch Session / Codex / Group 节点面板的重构剖析

原创2026-10-12 15:05:171,711 阅读
文章标签:人工智能AI AgentAgent 工作流AI 技能开发工具低代码

CC Workflow Studio Schema-Driven 属性面板迁移实战:Phase 1 中 Prompt / Branch Session / Codex / Group 节点面板的重构剖析

CC Workflow Studio 正在通过多阶段的 changeset 迭代,将各节点类型的手写属性面板逐步迁移到一套以核心 zod schema(PropertySchema)为单一事实来源(single source of truth)的 schema-driven 渲染模型上。本文以 .changeset/schema-driven-property-panel-phase1.md 记录的 Phase 1 为核心,深入讲解 Prompt、Branch Session、Codex、Group 四类节点的属性面板如何迁移到 core 包内的 zod schema 与注册表条目,并剖析渲染器新增的 i18n-convention 选项标签与分节默认展开能力,最后展示 PromptNodeData / BranchSessionNodeData 数据接口在 core 中的去重合并。读完本文,你将掌握该 schema 模型的声明语法、渲染器的工作机制,以及为任意新节点类型接入这套模型所需遵循的完整操作步骤。

一、Phase 1 变更速览:从 changeset 到源码

Phase 1 的变更记录(.changeset/schema-driven-property-panel-phase1.md)是标准的 changesets 格式,头部声明了两个包的版本影响:

---
'@cc-wf-studio/core': minor
'cc-wf-studio': patch
---

正文摘要将其拆解为三块核心工作:

  1. 将 Prompt、Branch Session、Codex、Group 四类节点的属性面板迁移到 core 的 zod schema(对应 prompt / branch-session / codex / group 四个 schema 文件及其注册表条目);
  2. 为渲染器新增 i18n-convention 选项标签(option label 通过 <labelKey>.option.<value> 约定解析)与 分节默认展开(sectionDefaultOpen)能力;
  3. 将 PromptNodeData / BranchSessionNodeData 从 webview 侧去重合并到 core 类型定义中。

三者分别对应仓库中的三类文件:core 的 schema 定义、webview 的通用渲染器与面板配置、以及 core 的 workflow 类型。下面逐一展开。

二、Schema 模型地基:FieldMeta、PropertySchema 与注册表

要理解 Phase 1,必须先掌握 Phase 0 打下的基础(见 .changeset/schema-driven-property-panel-phase0.md):core 包中定义了一套 zod 属性字段原语,位于 packages/core/src/schema/field.ts。

每个字段由 field() 辅助函数声明,把 zod 运行时校验与领域元数据(FieldMeta)合并在一个 PropertyField 对象中:

export interface PropertyField<T extends z.ZodTypeAny = z.ZodTypeAny> {
  zod: T;
  meta: FieldMeta;
}

export function field<T extends z.ZodTypeAny>(zod: T, meta: FieldMeta): PropertyField<T> {
  return { zod, meta };
}

FieldMeta 是渲染与导出共享的元数据载体,Phase 1 用到的关键成员包括:

元数据成员 类型 作用
targets FieldTargets 该字段适用的导出目标('all' 或显式目标列表),决定 UI 显示范围与导出忽略告警
labelKey string 字段标签的 i18n key,由 webview 解析
control FieldControl UI 控件类型:text / textarea / select / checkbox / radio / color / tools / objectArray;undefined 表示仅数据字段(校验与目标作用域生效,但不由通用面板渲染)
options readonly string[] select/radio 的可选值,单一来源供 UI 使用
helpKey / placeholderKey string 控件下方的帮助行与占位文案 key
sectionKey string 将字段归入某个 CollapsibleSection 分组
visibleWhen 谓词函数 当前节点数据不满足条件时隐藏字段
readonlyWhen 谓词函数 满足条件时字段只读(如内置子代理预设控制 model/tools)
editInEditor boolean textarea 控件配套"在外部编辑器中编辑"按钮
allowCustom boolean select 控件追加"自定义"选项并切换为自由文本输入
itemFields / itemReadonlyWhen objectArray 专用 数组项行内的列定义与只读判定

FieldMeta 的范围护栏(scope guardrails)非常明确:字段间的副作用(如 outputPorts 同步)交给节点 schema 旁导出的纯函数 derive*Update;布局与样式提示靠声明顺序;对话框与导航交给 webview 面板配置中的 Header/Footer 插槽;跨字段校验用 zod 的 refine。

节点的完整属性 schema 是一个按字段名键控的 PropertySchema(Record<string, PropertyField>),全部节点 schema 汇总到注册表 packages/core/src/schema/node-schema-registry.ts 的 NODE_PROPERTY_SCHEMAS 中。Phase 1 落地后,该注册表已包含全部 12 类可编辑节点:

export const NODE_PROPERTY_SCHEMAS: Partial<Record<NodeType, PropertySchema>> = {
  [NodeType.SubAgent]: subAgentPropertySchema,
  [NodeType.Prompt]: promptPropertySchema,          // ← Phase 1
  [NodeType.BranchSession]: branchSessionPropertySchema, // ← Phase 1
  [NodeType.Codex]: codexPropertySchema,            // ← Phase 1
  [NodeType.Group]: groupPropertySchema,            // ← Phase 1
  [NodeType.AskUserQuestion]: askUserQuestionPropertySchema,
  [NodeType.Branch]: branchPropertySchema,
  [NodeType.IfElse]: ifElsePropertySchema,
  [NodeType.Switch]: switchPropertySchema,
  [NodeType.Skill]: skillPropertySchema,
  [NodeType.Mcp]: mcpPropertySchema,
  [NodeType.SubAgentFlow]: subAgentFlowPropertySchema,
};

未进入注册表的 Start/End 节点(无可编辑字段)继续保留非 schema 渲染。注册表注释明确了它的三位消费者:webview 属性面板(渲染)、导出告警推导(warnings.ts)、工作流校验器(validate-workflow.ts),三者读取的是同一份定义,从机制上杜绝了 UI 与校验/告警之间的漂移。

三、Phase 1 的四个新节点 schema 逐一拆解

3.1 Prompt 节点:promptPropertySchema

源码位于 packages/core/src/schema/nodes/prompt-schema.ts,镜像 PromptNodeData(见 packages/core/src/types/workflow-definition.ts):

export const promptPropertySchema = {
  label: field(z.string().optional(), {
    targets: 'all',
    labelKey: 'prompt.field.label',
    control: 'text',
    placeholderKey: 'prompt.field.label.placeholder',
  }),
  prompt: field(z.string(), {
    targets: 'all',
    labelKey: 'prompt.field.prompt',
    control: 'textarea',
    editInEditor: true,
    placeholderKey: 'prompt.field.prompt.placeholder',
    helpKey: 'prompt.field.prompt.help',
  }),
  variables: field(z.record(z.string(), z.string()).optional(), {
    targets: 'all',
    labelKey: 'prompt.field.variables',
  }),
} satisfies Record<string, PropertyField>;

值得注意的设计点是 variables:它是运行时填充的映射,刻意不声明 control,因此属于"仅数据字段"——通用面板不渲染它,webview 的 Prompt 面板改为在 Footer 插槽中根据 prompt 文本实时提取 {{variableName}} 并展示"检测到 N 个变量"(见 prompt-panel.tsx 的 DetectedVariables 组件)。这样把"数据校验/目标作用域"(schema 负责)与"展示方式"(webview 插槽负责)彻底解耦。

3.2 Branch Session 节点:branchSessionPropertySchema

源码位于 packages/core/src/schema/nodes/branch-session-schema.ts,镜像 BranchSessionNodeData:

export const branchSessionPropertySchema = {
  label: field(z.string(), {
    targets: 'all',
    labelKey: 'branchSession.field.label',
    control: 'text',
    placeholderKey: 'branchSession.field.label.placeholder',
  }),
  workDescription: field(z.string().optional(), {
    targets: 'all',
    labelKey: 'branchSession.field.workDescription',
    control: 'textarea',
    editInEditor: true,
    placeholderKey: 'branchSession.field.workDescription.placeholder',
    helpKey: 'branchSession.field.workDescription.help',
  }),
} satisfies Record<string, PropertyField>;

Branch Session 是 Claude Code 独占的"人机协作检查点"节点类型(见 packages/core/src/schema/claude-code-only.ts 的 CLAUDE_CODE_ONLY_NODE_TYPES),节点级目标是 claudeCode,因此字段级 targets 一律为 'all'(在该节点类型内部不再细分)。其面板配置(branch-session-panel.tsx)在 Footer 插槽中静态展示 "Claude Code only" 提示条,提醒用户该节点无法导出到其他运行时。

3.3 Codex 节点:codexPropertySchema

源码位于 packages/core/src/schema/nodes/codex-schema.ts,是 Phase 1 中字段最多、也最能体现"schema 表达边界"的 schema。它镜像 CodexNodeData(workflow-definition.ts),并导出两组预定义常量供 UI 与校验共用:

export const CODEX_PREDEFINED_MODELS = [
  'gpt-5.2-codex',
  'gpt-5.2',
  'gpt-5.1-codex-max',
  'gpt-5.1-codex-mini',
] as const;

export const CODEX_SANDBOX_MODES = ['read-only', 'workspace-write', 'danger-full-access'] as const;

export const codexPropertySchema = {
  label: field(z.string().optional(), {
    targets: 'all',
    labelKey: 'codex.field.label',
    control: 'text',
    helpKey: 'codex.field.label.help',
  }),
  promptMode: field(z.enum(['fixed', 'ai-generated']), {
    targets: 'all',
    labelKey: 'codex.field.promptMode',
    control: 'radio',
    options: ['fixed', 'ai-generated'],
  }),
  prompt: field(z.string(), {
    targets: 'all',
    labelKey: 'codex.field.prompt',
    control: 'textarea',
    editInEditor: true,
    placeholderKey: 'codex.field.prompt.placeholder',
  }),
  model: field(z.string(), {
    targets: 'all',
    labelKey: 'codex.field.model',
    control: 'select',
    options: CODEX_PREDEFINED_MODELS,
    allowCustom: true,
  }),
  reasoningEffort: field(z.enum(['low', 'medium', 'high']), {
    targets: 'all',
    labelKey: 'codex.field.reasoningEffort',
    control: 'select',
    options: ['low', 'medium', 'high'],
  }),
  skipGitRepoCheck: field(z.boolean().optional(), {
    targets: 'all',
    labelKey: 'codex.field.skipGitRepoCheck',
    control: 'checkbox',
  }),
  sandbox: field(z.enum(CODEX_SANDBOX_MODES).optional(), {
    targets: 'all',
    labelKey: 'codex.field.sandbox',
    sectionKey: 'advanced',
  }),
} satisfies Record<string, PropertyField>;

这里有几个典型细节:

  • label 在 schema 中可选、接口中必填:面板在标签被清空时会置为 undefined,画布则回退显示节点 id(保留的旧行为)。schema 的可选性可以比接口更宽松,这正是 drift guard 注释所允许的;
  • sandbox 是数据驱动 UI 的范例:schema 中仅声明为数据字段(无 control),其"启用复选框 + 模式下拉"的组合控件由 webview 的 customControls 注入(见下文第五节),并按 sectionKey: 'advanced' 归入"高级选项"分组;
  • model 的 allowCustom: true:webview 的 SelectControl 会追加一个"自定义"选项,选中后切换为自由文本输入框,支持 gpt-6.0-codex 这类自定义模型名。

3.4 Group 节点:groupPropertySchema

源码位于 packages/core/src/schema/nodes/group-schema.ts。Group 是纯布局容器,label 是唯一可编辑属性;子节点列表/导航保留在 webview 的 Footer 插槽中:

export const groupPropertySchema = {
  label: field(z.string(), {
    targets: 'all',
    labelKey: 'group.field.label',
    control: 'text',
    placeholderKey: 'group.field.label.placeholder',
  }),
} satisfies Record<string, PropertyField>;

3.5 每个 schema 的编译期 drift guard

四个新 schema 文件末尾都导出了两个类型级守卫,这是 Phase 1 引入的防漂移机制(AssertAssignable 定义见 packages/core/src/schema/field.ts):

// 字段名守卫:schema 推断出的字段名必须是节点数据接口字段名的子集
export type PromptSchemaFieldNamesGuard = AssertAssignable<
  keyof z.infer<typeof promptZodObject>,
  keyof PromptNodeData
>;
// 值类型守卫:schema 推断出的对象形状必须可赋值给 Partial<PromptNodeData>
export type PromptSchemaValueTypesGuard = AssertAssignable<
  z.infer<typeof promptZodObject>,
  Partial<PromptNodeData>
>;

一旦 schema 的字段名或值类型与 PromptNodeData 等公共接口发生分歧(例如拼错了一个枚举值),tsc 会在编译期直接报错,而不是等到运行时才暴露——这是"声明一次、处处复用"模型的正确性基石。

四、渲染器能力升级:i18n-convention 选项标签与分节默认展开

Phase 1 changeset 明确提及"add i18n-convention-based option labels and per-section default-open to the renderer",这两项能力都落在 webview 的通用渲染器 SchemaPropertyPanel.tsx 及配套组件中。

4.1 i18n-convention 选项标签

SelectControl(SelectControl.tsx)按约定解析选项文案:若存在 <labelKey>.option.<value> 这条翻译,则使用它;否则原样显示产品术语(如模型名):

const optionLabel = (opt: string) => stOptional(`${meta.labelKey}.option.${opt}`) ?? opt;

这在简体中文翻译文件中(zh-CN.ts)有完整对应,例如:

'codex.field.reasoningEffort.option.low': '低',
'codex.field.reasoningEffort.option.medium': '中',
'codex.field.reasoningEffort.option.high': '高',

这意味着 schema 只需声明 options: <a href="https://link.gitcode.com/i/b850bcbe9f47c62dbc4397dd22aa122e" target="_blank">'low', 'medium', 'high'],五种语言的 webview 翻译文件各维护一条 option 文案即可,无需在 schema 中硬编码任何显示字符串。i18n key 的解析通过 [schema-i18n.ts 的 useSchemaTranslation 包装实现:必填 key 缺失时回退到原始 key(便于开发期暴露),可选 key 缺失时返回 undefined 以跳过渲染。

4.2 分节默认展开(sectionDefaultOpen)

sandbox 字段归入 advanced 分组后,面板需要一个"智能默认":没有设置 sandbox 时收起高级选项,设置了才展开。NodePanelConfig(types.ts)新增 sectionDefaultOpen,Codex 面板配置(codex-panel.tsx)实现为:

export const codexPanelConfig: NodePanelConfig = {
  schema: NODE_PROPERTY_SCHEMAS[NodeType.Codex]!,
  mode: 'edit',
  i18nNamespace: 'codex',
  customControls: { label: ..., promptMode: ..., skipGitRepoCheck: ..., sandbox: ... },
  sectionDefaultOpen: {
    advanced: (data) => !!data.sandbox,
  },
};

SchemaPropertyPanel 渲染分组时按 sectionDefaultOpen?.[sectionKey]?.(data) ?? true 计算初始展开状态(默认展开),并以 node.id + sectionKey 作为 CollapsibleSection 的 key,保证切换节点时组件重挂载、重新计算默认展开态:

<CollapsibleSection
  key={`${node.id}-${item.sectionKey}`}
  title={st(`${i18nNamespace}.section.${item.sectionKey}`)}
  hint={stOptional(`${i18nNamespace}.section.${item.sectionKey}.hint`)}
  defaultOpen={sectionDefaultOpen?.[item.sectionKey]?.(data) ?? true}
>

分组逻辑(buildRenderItems)按 schema 声明顺序遍历字段:共享同一 sectionKey 的字段在 key 首次出现的位置聚合成一个折叠区,未分组字段保持顶层平铺。分节标题解析为 <i18nNamespace>.section.<sectionKey>(可选 .hint 提示),例如 'codex.section.advanced': '高级选项'。

五、webview 侧接线:面板配置注册表与插槽机制

Phase 1 的四个面板配置统一注册在 node-panels.ts 的 NODE_PANELS 映射中(该文件就是"节点类型 → schema 面板配置"的 webview 侧注册表;尚未迁移的节点保留旧 PropertyOverlay)。迁移完成的节点由 SchemaPropertyPanel 统一渲染,其工作流程如下(SchemaPropertyPanel.tsx):

  1. 从配置取 schema,按声明顺序构建渲染项(顶层字段 + 分组);
  2. 每个字段先过滤数据字段(无 control 且无自定义控件则跳过)与 visibleWhen 不满足的字段;
  3. 渲染 Header 插槽 → 字段区 → Footer 插槽;
  4. 每次字段变更构造 patch,先经节点可选的 derive 纯函数归一化(如 outputPorts 同步),再调用 updateNodeData 持久化。
const handleChange = (fieldName: string, value: unknown) => {
  const patch: Record<string, unknown> = { [fieldName]: value };
  updateNodeData(node.id, derive ? derive(data, patch) : patch);
};

NodePanelConfig(types.ts)的完整契约如下:

配置项 说明
schema core 注册表中的该节点属性 schema
derive 可选纯归一化函数,每个字段 patch 持久化前执行
mode 'edit'(渲染交互控件)或 'readonly'(全部只读展示,如 Skill/MCP/SubAgent 摘要面板)
i18nNamespace 分节标题前缀 <ns>.section.<sectionKey>(+ .hint)
Header / Footer 字段区上方/下方的插槽组件(徽标、编辑对话框、导航等)
customControls 按字段名键控的组件覆写:无 control 的字段整体替换渲染;有 control 的字段仅替换输入控件
sectionDefaultOpen 每个 sectionKey 的初始展开谓词,默认展开

customControls 与 schema 的组合是这套模型的"逃生舱":schema 表达不了的行为(复合控件、条件帮助文案、失焦清空等)用普通 React 注入,而字段是否可见仍由 visibleWhen 决定——自定义控件只改变渲染方式,不改变可见性。

5.1 Codex 面板的四个自定义控件

codex-panel.tsx 注入的四个控件正是 Phase 1 的"边界案例"教科书:

  • CodexLabelControl:输入框失焦且内容为空时 onChange(undefined),让画布回退到节点 id;
  • CodexPromptModeControl:RadioControl 外包一层,当值为 ai-generated 时追加条件帮助文案;
  • CodexSkipGitControl:等宽字体的 --skip-git-repo-check 标记 + 红色警告文案;
  • CodexSandboxControl:启用复选框 + CODEX_SANDBOX_MODES 模式下拉的复合控件(对应 advanced 分组,分组默认收起、设置 sandbox 后才展开)。

5.2 Prompt 面板的变量检测 Footer

prompt-panel.tsx 在 Footer 插槽中渲染 DetectedVariables:用 extractVariables 从 prompt 文本中解析 {{variableName}},以等宽字体列表展示并提示"这些变量将在执行时被替换"。这一设计呼应了 schema 中 variables 作为数据字段(无 control)的定位——检测逻辑与 schema 解耦,可以独立演进。

六、数据接口去重:PromptNodeData 与 BranchSessionNodeData 移入 core

changeset 的最后一项工作是"de-duplicate PromptNodeData/BranchSessionNodeData into core"。在 packages/core/src/types/workflow-definition.ts 中,两个接口目前定义如下:

export interface PromptNodeData {
  label?: string;
  prompt: string;
  variables?: Record<string, string>;
}

export interface BranchSessionNodeData {
  /** Display label for the branch-session checkpoint */
  label: string;
  /** Optional scope of the joint human+AI work, injected into generated instructions */
  workDescription?: string;
  /** Number of output ports (fixed at 1) */
  outputPorts: 1;
}

去重的意义在于:core 包统一导出这些类型(见 packages/core/src/index.ts 的 export * from './types/workflow-definition.js'),webview 与 CLI 都从同一处导入,配合第三节的编译期 drift guard,schema、类型接口、渲染面板三者的字段集合被约束为始终一致。此后任何"新增一个 Prompt 节点字段"的改动都变成标准流程:改 schema → 改接口 → 补五个 webview 翻译文件的 label/placeholder/help → (如需要 AI 生成)更新 resources/workflow-schema.json。

七、从 Phase 1 看全貌:四个阶段的完整迁移轨迹

Phase 1 并非孤立变更,它是 .changeset 目录下 schema-driven-property-panel 系列四阶段之一:

阶段 changeset 文件 主要内容
Phase 0 schema-driven-property-panel-phase0.md 扩展 FieldMeta(sections、visibleWhen/readonlyWhen、help/placeholder、edit-in-editor、custom select),建立节点属性 schema 注册表,用通用 SchemaPropertyPanel 渲染 SubAgent 面板
Phase 1(本文) schema-driven-property-panel-phase1.md 迁移 Prompt / Branch Session / Codex / Group 四类面板;新增 i18n-convention 选项标签与分节默认展开;PromptNodeData / BranchSessionNodeData 去重入 core
Phase 2 schema-driven-property-panel-phase2.md 迁移 AskUserQuestion / Branch / IfElse / Switch;引入纯 derive*Update 函数做 outputPorts 同步;新增 objectArray 控件(边界从 zod 读取);数组项 ID 回填改为加载时归一化(ensureNodeDataItemIds)
Phase 3 schema-driven-property-panel-phase3.md 迁移 Skill / MCP / Sub-Agent Flow(只读摘要保留编辑对话框为插槽);导出忽略字段告警推广为注册表驱动的 collectIgnoredFieldWarnings
Phase 4 schema-driven-property-panel-phase4.md 将注册表驱动的 zod 校验接入 validateAIGeneratedWorkflow(仅校验已存在的字段,visibleWhen 未激活字段跳过);移除被取代的 7 个手写枚举检查;为全部节点 schema 增加编译期 drift guard

八、注册表驱动的三驾马车:渲染、告警与校验

值得强调的是,schema 注册表不仅是 Phase 1 面板的渲染依据,它同时驱动着另外两条链路,这正是"单一事实来源"的完整含义:

8.1 导出忽略字段告警

packages/core/src/schema/warnings.ts 的 collectIgnoredFieldWarnings 遍历工作流每个节点,用 queries.ts 的 getIgnoredFields 找出"已设置但不适用于当前导出目标"的字段并生成可读告警:

Node "code-reviewer" (codex): field "model" (=gpt-5.2-codex) is ignored when exporting to ADK.

告警消息完全由 schema 的 targets 推导,因此 UI 隐藏的字段与导出器丢弃的字段永远一致(旧 API collectIgnoredSubAgentWarnings 已标记 @deprecated,保留仅为兼容)。queries.ts 还提供 getFieldsForTarget、clearFieldsNotAppliedToTarget(切换导出目标时清空不再适用的 Claude Code 专属字段)等查询辅助。

8.2 AI 生成工作流的运行时校验

Phase 4 把同一注册表接入了 packages/core/src/utils/validate-workflow.ts 的 validateNodeSchemaFields:对每个已注册 schema 的节点,逐个校验存在值的字段(value === undefined 跳过),visibleWhen 谓词不成立的字段视为未激活直接跳过,然后用字段 zod safeParse 校验。这取代了原先 7 个手写枚举检查,且天然规避了 AskUserQuestion 的 options 冲突:AI 建议模式下空数组合法,手动模式下才受 .min(2).max(4) 约束。

8.3 数组边界复用

packages/core/src/schema/field.ts 的 getArrayBounds 直接从 zod 类型的 checks 中读取 min_length / max_length / length_equals,让 UI 的 add/remove 边界与校验器声明的边界永不重复维护;length(n) 会得到 min === max === n,此时 UI 完全隐藏增删按钮。

九、实操指南:为节点类型接入 schema 面板的完整步骤

综合 Phase 1 及后续阶段的实践,为任意新节点类型接入这套模型的标准流程如下:

  1. 在 core 声明 schema:在 packages/core/src/schema/nodes/ 新建 <type>-schema.ts,用 field() 声明每个属性(zod 类型 + FieldMeta),导出 propertySchema 与 toZodObject 派生的 zodObject;
  2. 添加编译期 drift guard:导出 SchemaFieldNamesGuard / SchemaValueTypesGuard 两个 AssertAssignable 类型,确保 schema 与节点数据接口同步演进;
  3. 注册:在 node-schema-registry.ts 的 NODE_PROPERTY_SCHEMAS 中加入映射;如需 outputPorts 同步等副作用,同时注册纯函数到 NODE_DERIVE_FNS;
  4. 配置面板:在 packages/vscode/src/webview/src/components/property/panels/ 新建 <type>-panel.tsx 导出 NodePanelConfig(引用 core 的 schema,按需配置 Header/Footer/customControls/sectionDefaultOpen),并登记到 node-panels.ts;
  5. 补 i18n 文案:在 webview 五个语言文件(en.ts、ja.ts、ko.ts、zh-CN.ts、zh-TW.ts)中补充 <ns>.field.<name> 标签与可选的 placeholder/help,以及 <labelKey>.option.<value> 选项文案、<ns>.section.<sectionKey> 分节标题;extension 侧的 i18n 文件位于 packages/vscode/src/extension/i18n/translations/;
  6. (可选)支持 AI 编写:如需 AI agent 也能生成该字段,同时在 resources/workflow-schema.json 中补充描述(按设计该 AI 编写指南是手工维护的,不从 zod schema 自动生成);
  7. 更新 changeset:按变更包类型(@cc-wf-studio/core 通常为 minor)记录变更说明。

Phase 1 本身通过 @cc-wf-studio/core: minor(新增了四个 schema 的公共 API)与 cc-wf-studio: patch(webview 渲染器增强与类型去重,向后兼容)的版本声明,向我们展示了 changesets 在 monorepo 中对跨包重构的典型记录方式——这也是阅读 .changeset 目录时理解各阶段变更边界的正确姿势。

十、结语:为什么 schema 驱动是属性面板的终局形态

回顾 Phase 1 的全部落地代码,可以总结这套模型的三层价值:

  1. 单一事实来源:zod 校验、UI 渲染、导出告警、AI 工作流校验四者共享同一份 PropertySchema,字段级目标作用域(targets)让"UI 隐藏"与"导出丢弃"在机制上不可能脱节;
  2. 声明式且可扩展:schema 表达不了的复合交互(Codex sandbox 组合控件、Prompt 变量检测、Skill/MCP 摘要对话框)通过 Header/Footer 插槽与 customControls 逃生舱保留全部 React 灵活性;
  3. 编译期与运行时双重保障:AssertAssignable drift guard 在 tsc 阶段拦截 schema/接口分歧,注册表驱动的 safeParse 校验在运行时兜底,配合 ensureNodeDataItemIds 的加载时归一化,共同保证了存量工作流文件与 AI 生成工作流的兼容性。

对想要贡献 CC Workflow Studio 的开发者来说,Phase 1 之后的新节点面板开发已经从"手写一整块 React 表单"简化为"声明一份 zod schema + 配置面板插槽",而 packages/core/src/schema/ 与 packages/vscode/src/webview/src/components/property/ 两个目录就是这套模型最完整的参考实现。

登录后查看全文
cc-wf-studio