news 2026/9/10 15:53:25

Sim 应用操作迁移实战:在内部 API、公共 API 与 Copilot 之间共享授权与业务语义

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sim 应用操作迁移实战:在内部 API、公共 API 与 Copilot 之间共享授权与业务语义

Sim 应用操作迁移实战:在内部 API、公共 API 与 Copilot 之间共享授权与业务语义

【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim

这篇指南面向 Sim 仓库(Sim 是用于构建、部署与监控 AI Agent 与工作流的协作平台)中需要新增受保护端点、工具命令或 CRUD 方法的开发者。它系统讲解如何把"路由/工具本地的授权与业务逻辑"收敛为一次定义、处处复用的语义化 Application Operation(应用操作),让内部 API、v1/v2 公共 API、Copilot 及其他受信工具适配器共享同一份授权与业务行为,同时各自保留认证方式、输入 schema 与响应形状。读完你将掌握:应用边界不变量、语义操作注册表的定义与运行时校验、Principal统一身份模型、内部/公共/Copilot 三类适配器的正确姿势,以及一整套基线测试与验收命令。

本仓库中该主题的权威操作文档位于 .agents/skills/migrate-application-operation/SKILL.md,本文在此基础上结合源码深入展开。

一切从应用边界不变量开始

迁移的第一原则是一条必须始终成立的不变量:

每个对持久化或受保护数据的真实操作,都必须经由一个已授权的应用用例(authorized application use case)进入。

它覆盖的范围比想象中更广:写操作自不必说,内容与元数据读取、规范资源查找(canonical resource lookup),乃至"把引用解析成资源"——只要该解析对授权敏感——统统在列。

表面辅助器(Surface Helper)的边界

每个入口(HTTP 路由、工具命令)都会有一层很薄的"表面辅助器",它被允许做且只被允许做这些事:

  • 把已认证的表面上下文规范化为一个Principal
  • 把别名或线上参数翻译成应用输入;
  • 选择一个代码定义的 Operation 与应用用例;
  • 调用应用用例;
  • 把类型化结果与错误翻译成表面契约(wire contract)。

而它绝不能做:

  • 查询数据库或存储;
  • 决定 workspace 或资源授权;
  • 实现业务事务;
  • 记录语义审计或共享域通知;
  • 从未受信参数推断权威身份、workspace、audience 或 scope;
  • 用计费归属(billing attribution)冒充身份。

一个辅助器只有在下述情况才允许解析路径:真正的受保护查找确实经由一个受权的应用解析器完成。一旦辅助器开始做真实的数据工作,就应该把这项工作下沉到应用用例中。

为什么"查一次库"也算越界

辅助器"顺手查一次库"看起来无害,但它绕过了应用层统一的授权、审计与错误投影。源码里这一点被写成了强约束:应用代码必须保持"表面中立",不得 importapp/api/**next/server、内部/v1/v2 契约或 presenter,也不得 import Copilot 工具处理器——它只返回领域值,让每个表面自己投影线上的结果(见 apps/sim/lib/core/application/authorized-workspace-use-case.ts)。

动工之前:先读地基,再定迁移范围

SKILL 文档列出了一份"必读清单",这些文件定义了应用操作架构的全部共享地基,动工前必须完整阅读:

  • packages/auth/src/principal.ts—— 统一 Principal 身份模型
  • apps/sim/lib/core/application/operation.ts—— 操作定义与能力断言
  • apps/sim/lib/core/application/workspace-operation.ts—— workspace 语义操作构造器
  • apps/sim/lib/core/application/workspace-authorization.ts—— 授权漏斗
  • apps/sim/lib/core/application/authorized-workspace-use-case.ts—— 授权用例包装器
  • apps/sim/lib/api/server/routes/definition.ts—— 路由/用例操作一致性校验
  • apps/sim/lib/api/server/routes/internal-json-route.ts—— 内部 JSON 路由构造器
  • apps/sim/lib/api/server/routes/v2-json-route.ts—— v2 公共 JSON 路由构造器
  • apps/sim/lib/auth/internal-delegation.ts—— 内部执行器委托绑定
  • apps/sim/lib/copilot/application/application-adapter.ts—— Copilot 应用适配器
  • apps/sim/lib/copilot/auth/application-delegation.ts—— Copilot 委托 Principal 构造

workspace-files(文件域)是文档指定的"黄金样例切片",建议对照阅读:

  • apps/sim/lib/workspace-files/application/operations.ts
  • apps/sim/lib/workspace-files/application/authorized-workspace-file-use-case.ts
  • apps/sim/lib/workspace-files/application/rename-workspace-file.ts
  • apps/sim/lib/copilot/application/execute-file-use-case.ts
  • apps/sim/lib/copilot/auth/file-delegation.ts

读完地基后再去读目标领域的操作注册表、应用代码、仓库层、契约、适配器、别名、恢复路径(resume paths)与聚焦测试。如果共享地基缺失,立即失败,绝不要在领域内部另起炉灶重建一套。

盘点每一个入口并分类

编辑前要建立完整清单,逐项盘点:

  • 内部 HTTP 路由与契约;
  • 公共/版本化 API 路由与契约;
  • Copilot 工具、别名、恢复路径与多态分支;
  • 其他工具服务器、工作流执行器、任务或服务调用方;
  • 当前的认证、授权、workspace 断言与隐藏(concealment)行为;
  • 管理器或编排调用链;
  • 审计、通知、分析与计费副作用;
  • 错误/状态/结果行为;
  • 限流身份、灰度开关、配额与并发准入。

每项分类为migrate(迁移)、defer(推迟)或non-goal(非目标)。不要因为相邻操作共享一个模块就顺手迁移它们;除非任务明确包含 v1,否则不要改动 v1。

当各表面对安全性或兼容性行为存在分歧时,停下来报告决策,不要默默二选一。

冻结可观察行为:先写基线,再动代码

把遗留路由或工具当作"有序程序"(ordered program)对待,而不是一袋散装业务逻辑。搬代码之前,为每个范围内的入口写一份紧凑基线,并为尚未被测试钉住的行为补上特征化测试。

基线的捕获面包括:

  • 接受的输入:trim 规则、空值省略、重复查询键、别名、默认值与边界;
  • 认证与授权的顺序、最低角色、资源成员关系、隐藏行为与精确的错误/状态映射;
  • 精确的成功响应体、可选字段、状态码、重定向、cookie、header 与二进制/流行为;
  • 变更排序、事务边界、幂等性、无操作(no-op)与每个部分失败后的可观察状态;
  • 审计、通知、分析与计费的时机、语义维度与归属;
  • 浏览器或协议状态的所有权、并发隔离、过期、回调顺序与清理行为;
  • 每个新进入 HTML、JavaScript、SQL、URL、日志、供应商 payload 或其他编码上下文的取值。

文档给出了"遗留顺序 vs 应用生命周期"的对照:

legacy parse/normalize -> legacy authorization checks -> branch-specific canonical lookup -> mutation(s) -> per-step side effects -> response or redirect catch

把它包进一个包装器,即使每个单独调用都被复用,行为仍可能改变。文档特别点名了几个高频陷阱:

  • projectAuditafterSuccess只在execute返回后运行。当后续步骤抛错时,它们无法描述先前已提交的变更——要么让复合变更原子化,要么先定义显式的"部分结果/失败投影"语义;
  • 操作元数据是可执行策略:给一个仅 workspace 级的遗留读操作加上资源角色,是授权变更,不是架构清理;
  • 共享错误策略不会自动保留路由本地的隐藏行为、异常子类顺序、浏览器重定向或分支专属消息;
  • 共享契约不会自动保留手工URLSearchParams规范化或遗留响应联合类型;
  • 共享用例可以拥有领域行为,而独立的表面 presenter 仍可保持各自的线上形状;
  • 当流程的另一部分仍在浏览器全局状态(如一个 cookie)里时,逐流程的身份是不够的;
  • 把新支持的参数透传给旧渲染代码,会创造一个新的安全边界,即使渲染器本身未变。

基线无法从代码、测试或明确的产品决策中建立时,快速失败。不要因为某个行为以前是隐式的,就推断它不重要。

五层职责:保持层次分明

迁移后的代码按如下五层分工:

  1. 认证适配器:验证表面凭据或受信执行上下文,构造Principal
  2. 路由/工具适配器:选择限流策略、解析自身契约、翻译输入、调用应用用例、渲染自己的结果;
  3. 应用用例:加载规范上下文、比较断言 scope、授权语义操作、执行业务行为、投影语义审计、触发共享域效果;
  4. 管理器或仓库:使用规范标识与 scope 执行数据库和存储读写,绝不允许接收凭据或 principal
  5. Presenter:只返回表面成功体或类型化二进制描述符,绝不构造认证、限流或错误行为。

普通公共 JSON 路由的固定顺序

IP abuse limit -> authenticate -> build Principal -> operation rate limit -> parse surface contract -> application use case -> canonical load -> asserted-scope concealment -> current authorization -> manager read or mutation -> semantic audit -> shared domain effects -> surface presenter

内部路由只有在"有显式策略与理由"时才允许省略 IP 桶或操作限流。注意文档的区分:用量计费、存储配额、成本准入与并发是独立于请求级限流的。

两条铁律:应用层绝不查询 API key 或 session;绝不添加 fallback 身份或授权行为。基础设施故障应该向上传播,而不是被改写成 not-found 或 forbidden。

这一顺序在 v2 路由构造器 apps/sim/lib/api/server/routes/v2-json-route.ts 中逐行可见:admitRateLimitedV2Request先做预认证 IP 限流(V2_PREAUTH_IP_LIMIT,600 令牌、300 补发、60 秒窗口),再认证 API key,然后以v2:{operation.id}:{subjectId}为桶键做操作级限流,最后才进入parseRequest与应用用例。

语义操作:一次定义,处处复用

在目标领域的操作注册表里新增一个稳定条目,使用defineWorkspaceOperation

rename: defineWorkspaceOperation({ id: 'widgets.rename', minimumRole: 'write', workspaceApiKey: 'allow', capability: 'widgets.use', principalKinds: ['session', 'personal_api_key', 'workspace_api_key', 'delegated'], delegatedServices: ['copilot'], })

不要为内部、公共或 Copilot 各自创建同一语义操作的副本。如果两个调用方确实有实质不同的业务或事务语义,就定义独立的语义操作与用例,并说明区别。

构造器如何 fail fast

defineWorkspaceOperation(apps/sim/lib/core/application/workspace-operation.ts)在定义时就做一致性校验,而不是等请求到来:

  • principalKinds不能为空、不能有重复;
  • 是否允许workspace_api_key必须与workspaceApiKey: 'allow'/'deny'严格一致;
  • workspace API key 有写上限:只有minimumRolereadwrite才允许workspace_api_keyadmin操作直接拒绝——因此"workspace 键有写天花板,无法满足 admin 操作";
  • 是否允许delegated必须与delegatedServices非空严格一致,且不能有重复服务;
  • capability必填:要么命名控制该操作的权限组能力,要么写'none'并在其正上方注释// permission-group-exempt: <reason>

capability缺失时defineWorkspaceOperation会直接抛错。这背后有一段值得复述的历史:capability放在基类ApplicationOperation而非仅WorkspaceOperation上,正是因为曾有一次 OAuth 连接操作从一个裸对象字面量铸造出来,绕过了类型与守卫,导致"十二个配置键配了 admin 复选框却没有服务端闸门"。类型不是全部保证(apps/sim/tsconfig.json排除了测试文件),所以运行时守卫与check:permission-group-enforcement脚本双保险(见 apps/sim/lib/core/application/operation.ts)。

真实代码样例:文件域的操作注册表 apps/sim/lib/workspace-files/application/operations.ts 中,files.list/files.read_metadata等使用ALL_FILE_TOOL_PRINCIPAL_POLICY(session、personal_api_key、workspace_api_key、delegated,委托给 copilot 与 executor),而compiledCheck只允许session——principal kinds 的选择来自真实行为,而不是"用例共享所以全收"。

运行时选择只允许来自受信注册表

路由声明、工具适配器与用例必须使用同一个字面量操作对象。允许运行时选择操作的唯一场景是从受信、代码定义的注册表中选择。绝不从 HTTP body、模型参数或其他不受信输入中接受操作 ID 或权限标签。

统一选择器执行是一个操作

文档特别指明:动态选择器分发(selector dispatch)是"受信运行时选择"的有意实例。selectors.execute只定义并授权一次,语义是"配置工作流或 workspace 资源时枚举选项"。浏览器只提供一个来自穷举的浏览器安全清单中的 selector 键、scope、白名单上下文与 list/detail 请求;在规范 scope 授权后,应用用例从穷举的服务器专用注册表中选出匹配附件。

真实实现见 apps/sim/lib/selectors/application/operations.ts:selectors.executeminimumRole: 'read'workspaceApiKey: 'deny'principalKinds: ['session']capability: 'none',其permission-group-exempt注释解释了为什么——凭据访问按凭据逐一授权,参数化的allowedIntegrations键由用例通过assertSelectorIntegrationAllowed对 selector 自己的资源断言。

Provider 与内部附件是该语义操作下的受信实现适配器,不是独立的应用操作。不要为每个 selector、provider 或列端点创建操作。附件只能选择代码定义的凭据/服务绑定、目标策略、provider 原语与投影行为;不能从请求接受模块、provider、服务、操作种类、来源或权限标签。selectors.execute用例端到端拥有引用解析、凭据授权、provider 调用、清理与安全结果投影。

实现应用用例

defineAuthorizedWorkspaceUseCase直接定义,或用薄的领域绑定提供领域专属授权选项(文件域就是这么做的:defineAuthorizedWorkspaceFileUseCase只是把authorizationOptions: { delegation: workspaceFileDelegationPolicy }预填进去,见 apps/sim/lib/workspace-files/application/authorized-workspace-file-use-case.ts)。

export const renameWidget = defineAuthorizedWorkspaceUseCase({ operation: widgetOperations.rename, resolveContext: ({ input }: { input: RenameWidgetInput }) => loadCanonicalWidgetContext(input.id, input.assertedWorkspaceId), authorizationOptions: { delegation: widgetDelegationPolicy }, execute: async ({ input, context }) => renameWidgetRecord({ workspaceId: context.workspaceId, widgetId: context.resourceId, name: input.name, }), projectAudit: ({ result }) => ({ action: AuditAction.WIDGET_UPDATED, resourceType: AuditResourceType.WIDGET, resourceId: result.id, resourceName: result.name, }), afterSuccess: ({ context }) => notifyWidgetsChanged(context.workspaceId), })

文档提醒:示例中的字段名是示意,请按目标领域真实的授权选项适配,不要照抄虚构字段。

包装器必须拥有的六步生命周期

  1. 在受保护加载之前拒绝不允许的 principal kinds;
  2. 加载规范上下文,按需隐藏断言 scope 不匹配;
  3. 使用当前策略状态授权操作;
  4. 执行管理器或仓库原语;
  5. 从权威结果投影审计;
  6. 等待共享的后置成功效果。

不要在普通迁移后的用例体中手工调用共享授权、principal 审计归属或recordAuditprojectAudit只在操作有语义审计时使用;权威的 no-op 不返回审计条目。产品分析(如captureServerEvent)保持表面专属,通过适配器的成功钩子触发。

包装器的源码实现可见 apps/sim/lib/core/application/authorized-workspace-use-case.ts:authorizePhase依次执行requireAllowedWorkspacePrincipalresolveContext(规范加载)→authorizeWorkspaceOperation→ 可选的资源级authorizeResourceexecute再跑业务、投影审计(通过recordProjectedUseCaseAuditEntries,把operation.idactor写入审计 metadata)并等待afterSuccess。它还暴露了authorize(),供 v2 的headSafe: false路由在HEAD上做"只授权不执行业务"的探测——避免把HEAD变成资源存在性预言机。

复用遗留编排前先检查

如果遗留编排已经在做授权、审计、通知或分析采集,就调用更低层原语,或为已迁移调用方去掉重复职责。

授权漏斗内部:不同 Principal 走不同分支

authorizeWorkspaceOperation(apps/sim/lib/core/application/workspace-authorization.ts)按 principal kind 分流:

  • session / personal_api_key:解析当前有效 workspace 权限,permissionSatisfies(permission, minimumRole)不足则抛InsufficientWorkspacePermissionsError;无 workspace 访问则抛NoWorkspaceAccessError(v2 表面把它隐藏为 404,避免泄露资源存在性)。personal key 额外遵守 workspace 的allowPersonalApiKeys开关与权限组的personal_api_key.use能力。能力检查排在角色检查之后是有意的:先做角色检查可用隐藏的 404 挡住外部人,避免向完全无关的调用方泄露组织扣留了哪些能力;
  • workspace_api_key:以 workspace 身份授权,与创建者成员关系无关;校验workspaceId匹配、操作workspaceApiKey === 'allow'permissionSatisfies('write', minimumRole)(写天花板)。workspace 键不解析任何权限组——用键的创建者做归属会把旁观者的组强加到每个共享键调用方身上;
  • delegated:校验 audience、过期时间、workspaceId 与委托策略isWithinScope,然后对 copilot 委托主体走完整"角色+能力"检查;executor 委托只做角色检查("工作流运行携带触发者的角色,但不携带其能力",能力由块/工具/模型的assertPermissionsAllowed另行把关)。

适配内部 API

普通 JSON 内部路由使用defineInternalJsonRoute(apps/sim/lib/api/server/routes/internal-json-route.ts)。显式声明:契约、认证策略、语义操作、限流策略、错误策略、输入映射、用例,以及当线上结果不同时的 presenter。

  • session 专用路由用internalSessionAuth
  • 只有端点确实支持签名执行器委托时,才用createInternalSessionOrExecutorAuth,且此时语义操作必须允许executor服务的delegatedprincipal;
  • 绝不把无 actor 的遗留 JWT 伪装成假的 session、owner 或 user principal。

内部适配器负责认证与内部响应信封(统一为{ error, requestId? }),不实现 workspace 授权。内部专属分析通过onSuccess在应用成功后保留。路由模块保持声明式——若多个内部路由重复认证、解析、错误渲染或响应构造,就去改进共享的内部路由构造器,而不是加领域专属的路由包装器。

执行器委托的绑定逻辑在 apps/sim/lib/auth/internal-delegation.ts:bindInternalExecutorDelegation把签名执行器声明绑定到工作流的规范活动 workspace,校验 audience 非空、subject 与兼容 actor 互斥、执行/部署上下文仍活动,并把compatibilityActorlegacy_execution_user)作为兼容性策略写入delegationContext——它不改变授权与审计主体。

适配公共或版本化 API

公共/版本化路由使用对应的构造器(如defineV2JsonRoute,apps/sim/lib/api/server/routes/v2-json-route.ts),配置 API key 认证、显式语义操作与限流策略、外部错误投影、输入映射、应用用例与外部 presenter。

关键约束:

  • 认证与 HTTP 格式化可以与内部 API 不同,授权与业务行为必须相同
  • 限流以凭据或 principal 主体为准,绝不以计费 owner 为准;计费归属只用于计费、配额或遗留必填用户字段的解析;
  • 线上形状不同时保持表面契约分离;但 IDs、names、bounds、formats 等不变量应复用共享原语 schema 与领域校验器。不要因为路由分离就维护重复的内外 schema;线上形状真正一致时 import 同一 schema。绝不把一种表面响应强转为另一种
  • v1 中间件与路由保持不变,除非显式包含。

v2 构造器值得一提的细节:HEADGET的别名是合法的(methodMatchesContract允许,RFC 9110 §9.3.2),但headSafe: false的路由在HEAD上会先完成 admission、解析与授权,再返回无体响应而不执行业务阶段——而requireHeadAuthorizableUseCase会在模块加载时拒绝"声明了headSafe: false却没有authorize()"的路由,因为那样HEAD就只能凭认证回答,从而泄露资源存在性。

适配 Copilot

Copilot 是表面适配器,不是独立的应用层。如果某个 HTTP 或其他表面已经在使用应用用例,Copilot 必须调用那个确切的用例,而不是在lib/copilot下重新实现受保护业务行为。

为每个领域创建一个 Copilot 应用适配器(createCopilotApplicationAdapter,apps/sim/lib/copilot/application/application-adapter.ts),而不是在每个工具里各自构造委托 principal:

executeCopilotWidgetUseCase(context, renameWidget, input, { resourceId })

适配器必须:

  • 要求受信的服务端撰写的 Copilot 执行标记(requireTrustedCopilotExecutionContext校验copilotToolExecution === true、非空 userId/workspaceId/toolCallId);
  • 要求已认证主体、规范 workspace、工具调用或执行身份、必要的 audience 或生命周期 scope;
  • 一处构造共享的委托Principal
  • 可选地在受信解析后绑定规范资源 scope;
  • 验证用例暴露的是已注册的代码定义操作(操作对象成员身份与身份集中检查,且注册表要求操作对象Object.isFrozen、无重复 ID);
  • 直接调用应用用例。

绝不从模型提供的 workspace ID、用户 ID、操作 ID、资源 scope 或权限标签构造权威委托。模型参数只是"请求的目标",必须对照受信执行上下文与规范数据校验。

委托 principal 的构造在 apps/sim/lib/copilot/auth/application-delegation.ts:createCopilotApplicationPrincipal从受信上下文派生 delegationId(如copilot-tool:{toolCallId}),以ORCHESTRATION_TIMEOUT_MS为 TTL,把 chatId/executionId 并入resourceScope,并整体Object.freeze。文件域的实际绑定见 apps/sim/lib/copilot/application/execute-file-use-case.ts。

工具处理器可以拥有参数别名、可恢复遗留名、abort 检查、工具调用上报与工具专属展示;它们不得为受保护操作直接查询管理器、手工授权或实现受保护业务行为。Copilot 引用辅助器只能通过调用"处于目标语义操作之下"的受权应用解析器把路径翻译成资源——传代码定义的操作对象可以,传模型提供的操作字符串不行。跨生命周期边界(恢复的工具调用、执行器回调、排队/后台工作、上传控制腿与最终化、持久完成、长运行 provider 操作)必须重新认证与授权。当"解析+执行"构成单一业务操作、需要一致快照或反复成对出现时,优先用顶层应用用例(如renameWidgetByReference)。

表面适配器不得组合受保护变更:原子复合动作需要一个顶层语义域操作与用例来拥有事务与权威结果。明确 best-effort 的应用命令只有在定义了硬输入/展开上限、取消检查点、部分结果语义、审计行为与限流/配额策略时,才能协调多个操作。把组合保持为例外与显式;普通工具应使用共享执行适配器。

错误映射:把预期的类型化错误映射为安全工具结果;未知错误必须变成通用 system/retryable 消息,同时把完整 cause 保留在服务端日志。绝不把原始数据库或存储错误返回给模型。

适配其他内部或外部工具

每个工具运行时都是表面适配器:

  • 通过该运行时或领域的一个共享适配器,把已认证执行上下文规范化为已有Principal
  • 调用与 HTTP 和 Copilot 表面相同的应用用例;
  • 保留工具协议的输入、输出、重试与取消语义;
  • 保持权威 workspace 与主体 scope 由服务端撰写。

内部调用方不自动受信可以绕过授权:它必须提供显式 principal,或使用刻意设计的服务/委托 principal。若当前 principal 模型无法表达其权限,停下来有意识地扩展身份模型——不要 fallback 到 owner、uploader、creator 或任意用户 ID。

外部工具端点在其适配器处像公共 API 一样认证,绝不在应用用例内二次认证。

保留身份与归属

  • session 与 personal-key principal 通过当前人工 workspace 权限授权;
  • personal API key 还遵守 workspace 的 personal-key 策略;
  • workspace key 以 workspace 身份、按显式操作策略与写天花板授权,独立于创建者成员关系;
  • 委托 principal 会重新检查当前主体及其 workspace、audience、过期、执行与资源 scope;
  • 计费 owner用于计费或遗留必填列,绝不用于授权、限流身份、委托身份、审计 actor 或人工分析身份;
  • 语义审计中保留结构化PrincipalActor元数据。

身份模型详见 packages/auth/src/principal.ts:Principalsession | personal_api_key | workspace_api_key | delegated | system | credential_group_enrollment的联合类型;delegated再细分 copilot/realtime 主体委托与 executor 工作流执行委托。关键辅助函数:resolvePrincipalSubjectUserId(actorless 调用者是常态而非例外)、requirePrincipalSubjectUserId(只有操作语义真正依赖人工主体时才用)、resolvePrincipalAuditAttribution(workspace key 在审计表中故意actorId 为 null,actorName: 'Workspace API key',不假装计费 owner 执行了操作)、resolvePrincipalAttribution(把已授权 principal 投影到遗留 user 归属列,workspace billing owner 只填空,不改决策)。

如果遗留 user 列无法表示真实 actor,显式标注兼容性归属,绝不假装它就是操作的人工。

特殊操作:显式处理,不硬套

以下场景不要强行塞进普通 JSON 迁移:

  • 上传/多部分生命周期:绑定不可变凭据身份,对控制腿与最终化重新授权,持久完成保持幂等;
  • 大请求体:在有限缓冲之前先认证并做廉价准入;
  • 二进制/流响应:使用 binary/stream 构造器与类型化描述符;
  • 批量或递归操作:对输入与展开去重并设上限,规范加载所有资源,明确"原子 vs best-effort"行为;
  • 多态工具:只在受信目标种类解析后选择语义操作;不要把无关分支路由进一个领域注册表;
  • 多资源事务:保持规范 scope 谓词,从权威受影响行派生审计。

遇到缺失设计就停下来报告,不要削弱身份、授权、限制或错误语义。

测试完整矩阵与验收命令

为每个迁移的表面和操作允许的每种 principal kind 添加聚焦测试,文档给出的测试矩阵:

  • 应用层:允许与拒绝的角色、规范加载前的 principal-kind 拒绝、workspace 断言不匹配、委托 scope、not found、冲突、no-op、基础设施传播;
  • 操作注册表:角色/workspace-key/principal-kind/委托服务一致性,非法定义 fail fast;
  • 仓库层:规范活动查找、workspace 谓词写入、归档资源、权威受影响行、数据库错误传播;
  • 内部 API:解析前先认证、精确契约、类型化错误、仅成功后才有表面分析;
  • 公共 API:personal 与 workspace key、限流行为、隐藏、精确外部信封与限流头;
  • Copilot/工具:受信上下文、精确注册操作成员身份、伪造 scope 被拒、别名与恢复路径、权限复检、安全错误、工具结果形状不变;
  • 副作用:审计从权威结果派生、共享通知跟在审计后、拒绝与 no-op 两者都不发生;
  • 兼容性特征化:遗留规范化、精确响应/重定向/cookie 行为、隐藏、错误子类优先级、分支专属输出;
  • 失败排序:在每一个独立提交步骤后注入失败,断言持久状态与审计、分析、通知效果;
  • 并发:重叠有状态浏览器或 provider 流程,证明每个回调只消费自己的状态与返回目的地;
  • 渲染边界:对每个新连入 HTML、内联 JavaScript、URL、日志或 provider 请求的输入施加恶意值。

至少运行以下命令:

bunx vitest run <focused test files> bunx biome check <changed source and test files> bunx turbo run type-check --filter=@sim/app --filter=@sim/auth bun run check:api-validation:strict git diff --check

不要声称某项检查通过,除非它真的完整成功。

并行安全与交接

并行工作时:分配不重叠的路由模块与调用方集合(同一路由文件里的两个方法是一个所有权单元);把操作注册表、契约族、路由策略与共享表面适配器视为合并热点;共享核心地基由单一任务持有,普通领域迁移消费它而不修改它;保留无关的工作树变更,绝不 stage 提案文档、lockfile 漂移或别的 Agent 的编辑;除非被要求,不 commit、不 push、不开 PR。

交接报告应包含:

  1. 语义操作、角色、workspace-key 策略与 principal kinds;
  2. 已迁移、已推迟与非目标入口清单;
  3. 内部、公共、Copilot 与其他工具表面各自保留的行为;
  4. 每个表面的身份构造与权威 scope 来源;
  5. 变更文件与共享合并热点;
  6. 运行过的测试与检查及结果;
  7. 剩余风险或阻塞项——不变量无法实现时快速失败。

小结

把一次"加一个受保护端点"的琐碎任务做成了架构纪律:以 .agents/skills/migrate-application-operation/SKILL.md 为操作手册,以 packages/auth/src/principal.ts 的联合身份模型为轴,以 apps/sim/lib/core/application 下的 operation、workspace-operation、workspace-authorization、authorized-workspace-use-case 为执行核心,内部 API、公共 API、Copilot 与工具运行时全部收敛为"认证适配器 + 表面适配器 + 应用用例 + 管理器/仓库 + presenter"五层结构。语义操作一次定义、注册表 fail-fast 校验、运行时选择只信受信代码、表面只做翻译不做决策——这套模式保证了无论未来接入多少新表面,受保护数据都只从"已授权的应用用例"这一扇门进入。

【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 15:49:08

Zebra打印机中文打印解决方案与ZPL实现

1. 项目概述最近在做一个仓储管理系统的打印模块时&#xff0c;遇到了一个棘手的问题&#xff1a;Zebra打印机默认不支持中文打印。这让我不得不深入研究ZPL语言&#xff08;Zebra Programming Language&#xff09;的中文打印实现方案。经过两周的摸索和测试&#xff0c;终于找…

作者头像 李华
网站建设 2026/9/10 15:48:25

CVAT 实战指南:3 步完成首次部署与视频、3D 数据标注

CVAT 实战指南&#xff1a;3 步完成首次部署与视频、3D 数据标注 【免费下载链接】cvat Computer Vision Annotation Tool (CVAT) is a leading platform for building high-quality visual datasets for vision AI. It offers open-source, cloud, and enterprise products, a…

作者头像 李华
网站建设 2026/9/10 15:47:19

MySQL逻辑函数实战技巧与性能优化指南

1. MySQL逻辑函数深度解析作为一名与MySQL打了十年交道的数据库工程师&#xff0c;我处理过太多因为逻辑函数使用不当导致的性能问题和业务逻辑错误。今天我们就来彻底拆解MySQL中的逻辑函数体系&#xff0c;从基础用法到高阶技巧&#xff0c;再到那些官方文档里没写的实战经验…

作者头像 李华