news 2026/9/27 12:28:30

Operit Kotlin 与 TypeScript 桥接接口对齐:NativeInterface 清理、Tools.Net 浏览器截图与 Compose DSL 节点声明实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Operit Kotlin 与 TypeScript 桥接接口对齐:NativeInterface 清理、Tools.Net 浏览器截图与 Compose DSL 节点声明实战
  • AI Agent
  • 人工智能
  • 大模型
  • AI 应用
  • 工具调用
  • 本地部署
  • MCP Clients
  • Agent 记忆

【免费下载链接】Operit

The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent

项目地址:https://gitcode.com/gh_mirrors/op/Operit
点击查看免费下载

本文围绕 Operit 在 2026-08-09 完成的「Kotlin 与 TypeScript 桥接接口对齐」任务展开,说明该 Android AI Agent 如何保证脚本运行时(QuickJS/ToolPkg)看到的 TypeScript 声明与 Kotlin 侧@JavascriptInterface真实注入能力严格一致:包括移除没有 Kotlin 实现的死声明、补齐Tools.Net.browserTakeScreenshot类型,以及为 Compose DSL 增加AiChat与AdaptiveSidePanel两个渲染节点。读者读完可以掌握 Operit 脚本桥接的类型维护方法论、参数归一化约定与"无编译静态双向核对"的验证技巧,并能在自己的 ToolPkg 脚本中正确使用这些公开接口。

一、任务背景:声明与运行时脱节带来的三个问题

Operit 的脚本体系由多层组成:Android 侧 Kotlin 代码通过@JavascriptInterface向 QuickJS 脚本运行时暴露桥接方法,脚本开发者则通过examples/types/下的.d.ts声明文件获得类型提示。这套体系中,Kotlin 注入实现与 TypeScript 声明必须一一对应,否则会出现"类型提示缺失"或"提示了根本不存在的方法"两类问题。

本次对齐任务(任务索引)清点出三个具体问题:

  1. examples/types/network.d.ts缺少 Kotlin 已注入的方法:JsTools已向Tools.Net注入了browserTakeScreenshot,但类型声明中没有对应成员,脚本作者无法获得提示,也无法获得参数校验的约束信息;
  2. examples/types/core.d.ts保留了两个没有 Kotlin 实现的死声明:NativeInterface命名空间中的setResult与setError在 Kotlin 侧并无对应@JavascriptInterface实现,属于历史遗留,脚本开发者若调用将直接失败;
  3. Compose DSL 声明缺少已确认需要公开的节点:ToolPkg 脚本可以声明式渲染 Compose UI,但AiChat(嵌入宿主 AI 聊天界面)与AdaptiveSidePanel(自适应侧栏)两个节点在 TypeScript 声明和 Kotlin 渲染器两侧都没有对齐。

任务的目标很明确:让运行时与类型声明在"公开接口"这个边界上完全一致,并同步更新面向脚本开发者的文档。

二、公开接口清点:NativeInterface的边界在哪里

桥接接口清点(1_NativeBridgeInventory.md)首先回答了"哪些方法应该出现在公共类型里"这个边界问题。

JsEngine通过@JavascriptInterface暴露的桥接方法并不全是公开脚本接口——其中一部分由 ToolPkg 运行时与运行时内部包装使用,属于内部桥接,不应进入公共NativeInterface类型。判断标准是:公开NativeInterface类型只描述脚本开发者可以直接使用的桥接方法,因此setResult、setError这种没有 Kotlin 实现的旧成员必须移除,而内部桥接方法也绝不能因为"存在注入"就被复制进core.d.ts。

对齐后,core.d.ts 中NativeInterface命名空间的公共成员为:

成员说明
callTool(toolType, toolName, paramsJson)同步调用工具(legacy 方式),返回 ToolResult 的 JSON 字符串
callToolAsync(callbackId, toolType, toolName, paramsJson)异步调用工具,回调携带 ToolResult
callToolAsyncStreaming(callbackId, intermediateCallbackId, toolType, toolName, paramsJson)流式异步调用
logInfo(message)/logError(message)/logDebug(message, data)三类日志桥接
registerToolPkgToolboxUiModule(specJson)注册 ToolPkg 工具箱 UI 模块
registerToolPkgAppLifecycleHook(specJson)注册应用生命周期钩子
registerToolPkgMessageProcessingPlugin(specJson)注册消息处理插件
registerToolPkgXmlRenderPlugin(specJson)注册 XML 渲染插件
getPluginConfigDir(pluginId)解析插件持久化配置目录(/sdcard/Download/Operit/plugins/<id>下的绝对路径)
registerInputMenuTogglePlugin(specJson)注册输入菜单开关插件

移除setResult与setError后,其余公共声明均能映射到 Kotlin 的@JavascriptInterface实现,ToolPkg 与运行时内部桥接也未混入公共声明。

三、Tools.Net.browserTakeScreenshot:从类型声明到参数归一化

3.1 TypeScript 声明

对齐后,network.d.ts 中browserTakeScreenshot的声明为:

function browserTakeScreenshot(options: { type?: string; // 图片格式,省略时默认 "png" element?: string; // 快照元素描述 ref?: string; // 快照元素引用,需与 element 成对提供 fullPage?: boolean; // 是否截取完整页面 }): Promise<string>;

返回值是Promise<string>——即截图保存后的文件路径字符串。声明注释同时强调:提供ref时必须附带与之匹配的快照element描述。

3.2 Kotlin 注入侧的参数归一化

类型声明只是契约的一半,另一半在 Kotlin 注入实现中。JsTools.kt(app/src/main/java/com/ai/assistance/operit/core/tools/javascript/JsTools.kt)向Tools.Net注入该方法的实现,其参数归一化逻辑与声明严格对应:

  • 入参校验:options必须是一个普通对象,否则抛出browserTakeScreenshot only accepts one options object;
  • type:转字符串并trim(),空值兜底为"png";
  • element/ref:分别转字符串(ref额外trim()),并且二者必须成对出现——ref存在而element缺失时报element is required when ref is provided,反之报ref is required when element is provided;
  • fullPage:归一化为布尔值(!!params.fullPage);
  • 最终调用toolCall("browser_take_screenshot", params)进入标准浏览器会话工具链。

3.3 Kotlin 工具实现侧的约束

browser_take_screenshot工具在浏览器会话工具中落地(StandardBrowserSessionTools.kt),Kotlin 侧还补充了声明层看不到的运行约束:

  • type只接受png/jpeg/jpg,否则报type must be png or jpeg;
  • element与ref成对校验(element != null && ref == null时报错);
  • fullPage与ref互斥:fullPage为真且提供ref时报fullPage cannot be used with element screenshots——即"截取完整页面"与"截取指定元素"不可同时使用;
  • 截图通过takeScreenshot落盘,返回的ToolResult中result字段携带Saved screenshot to <路径>,同时附上当前打开的标签页列表与页面状态,供 Agent 继续决策。

面向脚本开发者的文档 docs/doc-src/package-dev/network.md 已同步更新,其中明确说明"ref与element必须成对提供,fullPage控制是否截取完整页面"。一个典型用法:

const path = await Tools.Net.browserTakeScreenshot({ type: 'png', fullPage: true }); console.log(path); // 截图保存路径

四、Compose DSL 新节点:AiChat与AdaptiveSidePanel

脚本开发者除了调用工具,还可以通过 Compose DSL 以声明方式渲染 UI。本次对齐在 TypeScript 声明与 Kotlin 渲染器两侧同时补齐了两个节点(2_TypeDeclarationsAndDocs.md)。

4.1 TypeScript 声明

compose-dsl.d.ts 中新增:

/** Embeds the host AI chat surface without its workspace panel. */ export interface AiChatProps extends ComposeCommonProps {} /** Controls a responsive trailing panel around the supplied screen content. */ export interface AdaptiveSidePanelProps extends ComposeCommonProps { open: boolean; side: ComposeChildren; onOpenChanged: (open: boolean) => void; defaultWidth?: number; minWidth?: number; minContentWidth?: number; // ... }

并在节点工厂中注册(compose-dsl.d.ts):

AiChat: ComposeNodeFactory<AiChatProps>; AdaptiveSidePanel: ComposeNodeFactory<AdaptiveSidePanelProps>;
  • AiChat语义为"嵌入宿主 AI 聊天界面(不含工作区面板)",用于在 ToolPkg 自定义界面中直接嵌入聊天能力;
  • AdaptiveSidePanel语义为"围绕给定屏幕内容控制响应式尾部面板":open控制开关、side为侧栏内容、onOpenChanged为状态变更回调,defaultWidth/minWidth/minContentWidth控制面板宽度行为。

4.2 Kotlin 渲染器实现

Kotlin 侧渲染分发位于 ToolPkgComposeDslScreen.kt:节点类型归一化后,aichat分发到renderAiChatNode,adaptivesidepanel分发到renderAdaptiveSidePanelNode。

  • renderAiChatNode(L2477-L2484)在应用通用修饰符后直接嵌入AIChatScreen(embedded = true)——即"嵌入模式"的聊天界面;
  • renderAdaptiveSidePanelNode(L2487-L2619)实现了双布局自适应:
    • 宽屏布局:内容区与侧栏区并排(Row+weight(1f)),侧栏展开时提供 3dp 宽、56dp 高的拖拽手柄,通过detectDragGestures实时调整面板宽度,并在minWidth/maxWidth范围内coerceIn收敛;
    • 窄屏布局:内容区全屏铺底,侧栏从右侧滑出,背景叠加Color.Black.copy(alpha = 0.18f)半透明遮罩,点击遮罩触发onOpenChanged(false)关闭——这正是任务文档记录的"宽屏拖拽调宽、窄屏遮罩关闭"能力来源。

onOpenChanged为必填动作属性,渲染器通过ToolPkgComposeDslParser.extractActionId提取动作 ID,缺失或空白都会直接抛错。

五、静态双向核对:不跑编译的验证方法论

本任务遵循执行约束——不运行编译、构建或测试命令(index.md),验证完全采用静态核对,这也是它最有方法论价值的地方。核对范围(3_StaticVerification.md)包括四件事:

  1. 检查core.d.ts的NativeInterface不包含没有 Kotlin 实现的旧成员;
  2. 比较JsTools.kt注入的浏览器截图方法与network.d.ts的Net成员;
  3. 比较compose-dsl.d.ts的新增节点与 Kotlin Compose DSL 节点分发;
  4. 审查文档和类型文件的差异。

完成标准与核对结果:

  • core.d.ts中每个NativeInterface成员均能在 Kotlin@JavascriptInterface中找到实现;
  • setResult与setError未出现在任何公开类型声明或开发者文档中;
  • browserTakeScreenshot的四个参数(type/element/ref/fullPage)与 Kotlin 归一化逻辑一致;
  • Compose DSL 类型工厂与 Kotlin 渲染节点各 88 个,双向集合无差异——以集合比较的方式确认"声明了的一定能渲染,能渲染的一定已声明";
  • git diff --check未发现空白错误。

这套方法可以直接迁移到其他"声明文件 ↔ 运行时实现"双端同步的维护场景:先清点注入面,再定义公开边界,然后做双向集合比较,最后用 diff 审查收尾。

六、对齐后的收益:脚本开发者的实际体验

所有公开类型都通过 examples/types/index.d.ts 统一再导出(export * from './core'、export * from './network'、export * from './compose-dsl'等),脚本作者只需引用这一个入口即可获得完整类型提示:

  • 写Tools.Net.browserTakeScreenshot时能获得参数结构、默认值与返回值提示,不再"盲写";
  • 写 Compose DSL 时可以使用AiChat嵌入聊天、用AdaptiveSidePanel做响应式侧栏,且有完整的 props 类型约束;
  • 不会再看到NativeInterface.setResult/setError这类调用即失败的幽灵方法。

从源码结构看,这正是 Operit "Kotlin 运行时注入 → TypeScript 声明 → 开发者文档"三层一致性的一个典型维护闭环:任何新增的@JavascriptInterface注入或 Compose DSL 节点,都应同步完成类型声明、文档更新与双向静态核对,才能保证脚本生态的类型可靠与可检索性。

相关文件索引

  • 任务索引与执行约束:docs/TODO/kt_ts_bridge_alignment_20260809/index.md
  • 公开接口清点:1_NativeBridgeInventory.md
  • 类型声明与文档更新:2_TypeDeclarationsAndDocs.md
  • 静态双向核对:3_StaticVerification.md
  • 桥接类型声明:examples/types/core.d.ts、examples/types/network.d.ts、examples/types/compose-dsl.d.ts、统一入口 examples/types/index.d.ts
  • Kotlin 注入实现:JsTools.kt、浏览器会话工具 StandardBrowserSessionTools.kt、Compose DSL 渲染器 ToolPkgComposeDslScreen.kt
  • 开发者文档:docs/doc-src/package-dev/network.md
  • AI Agent
  • 人工智能
  • 大模型
  • AI 应用
  • 工具调用
  • 本地部署
  • MCP Clients
  • Agent 记忆

【免费下载链接】Operit

The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent

项目地址:https://gitcode.com/gh_mirrors/op/Operit
点击查看免费下载

相关推荐

上一篇:NocoBase 数据源管理之 IField 接口详解:字段抽象、FieldOptions 与类型注册机制
下一篇:Telegraf HashiCorp Vault Secret Store 插件:从配置到源码的完整实战指南

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

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

上位机与Web后台怎么选?工业设备软件选型逻辑与融合架构解析

上周三晚上&#xff0c;一个做非标自动化设备的朋友跟我打电话&#xff0c;问了一个我一年至少要听三遍的问题&#xff1a;设备交付出去之后&#xff0c;甲方要求做个上位机&#xff0c;但内部又有同事建议做成Web后台&#xff0c;这俩到底选哪个&#xff1f;电话那头背景音是车…

作者头像 李华
网站建设 2026/9/27 12:25:57

Cortex E2E 测试框架实战指南:从依赖安装到全量端到端测试运行

后端云原生模型推理服务MLOps人工智能 【免费下载链接】cortex Production infrastructure for machine learning at scale 项目地址&#xff1a; https://gitcode.com/gh_mirrors/co/cortex 点击查看 免费下载 导读 本文基于 Cortex 仓库&#xff08;Production infrastruct…

作者头像 李华
网站建设 2026/9/27 12:25:22

智能感知技术入门:从传感器到模式识别的完整实践指南

1. 智能感知到底在解决什么问题1.1 从一个生活场景说起你家里有没有那种走廊灯&#xff1f;晚上走过去&#xff0c;灯自己亮了&#xff0c;过一会儿又自己灭了。你可能会说&#xff0c;这不就是声控灯嘛&#xff0c;拍个手就亮。但如果你仔细想想&#xff0c;声控灯其实挺笨的—…

作者头像 李华
网站建设 2026/9/27 12:25:11

Windows上打arm64 deb:三个认知坑与Docker/QEMU完整方案

在交付一个纯 Linux 生态的安装包这件事上&#xff0c;我一开始还真没把它当回事。项目的最终产物是一个跑在 arm64 网关上的代理服务&#xff0c;客户要求必须提供.deb安装包&#xff0c;而团队手里的办公机几乎全是 Windows。接到任务的第一反应是&#xff1a;deb 不就是个压…

作者头像 李华