脚本 API
本页按使用场景整理脚本 API。需要在编辑器中查看实时自动补全和完整签名时,请在脚本首行加入:
/// <reference path="./.cursorcrane/cursorcrane.d.ts" />每个脚本都需要 meta 和 run(ctx):
/** @type {CursorCrane.Meta} */const meta = { type: "action", name: "Example", permissions: [],};
/** @param {CursorCrane.Context} ctx */async function run(ctx) { ctx.toast("Done");}meta 字段 | 说明 |
|---|---|
type | "action" 执行一次动作;"form" 打开表单。 |
presentation | form 可选。"panel" 适合持续使用;"transient" 失焦后关闭。 |
name | 脚本在 Cursor Crane 中显示的名称。 |
permissions | 脚本需要的权限。 |
可用权限是 "accessibilityAPI"、"clipboard"、"keyboard"、"mouse" 与 "shell"。没有声明某项权限时,对应 API 不可使用。
找到应用和窗口
Section titled “找到应用和窗口”ctx.applicationManager 用于查看运行中的应用:
| 成员 | 用途 |
|---|---|
applications | 所有可用应用。 |
getApplicationByBundleIdentifier(id) | 按 bundle identifier 找应用。 |
getApplicationByPid(pid) | 按进程 ID 找应用。 |
每个 Application 都有 name、bundleIdentifier 和 pid。
ctx.windowManager 用于寻找和切换窗口:
| 方法 | 用途 |
|---|---|
getWindowsByPid(pid) | 取得某个应用的窗口。 |
getWindowsByBundleIdentifier(id) | 按应用查窗口。 |
getWindowById(windowId) | 按窗口 ID 查找。 |
getActiveWindow() | 当前正在使用的窗口。 |
getPreviousWindow() | 上一个使用过的窗口。 |
activateWindow(windowId) | 切换到指定窗口。 |
Window 包含 title、windowId 和所属的 application。
找到和操作界面元素
Section titled “找到和操作界面元素”ctx.elementInspector.getElementByPid(pid) 取得一个应用的界面元素入口,需要 accessibilityAPI 权限。之后可以从应用的 getWindows() 找到窗口,再继续查找按钮、文本框或列表项。
const application = ctx.elementInspector.getElementByPid(window.application.pid);const root = application?.getWindows().find( item => item.windowId === window.windowId);const saveButton = root?.queryElements('Button[title="Save"]')[0];Element 信息
Section titled “Element 信息”| 属性 | 用途 |
|---|---|
title | 元素提供的标题。 |
displayTitle | 更适合展示给用户看的标题。 |
label、description | 元素的补充说明。 |
kind | 元素类型,例如 Button 或 TextField。 |
windowId | 元素所在窗口的 ID。 |
rect / frame | 元素在屏幕上的位置与尺寸。 |
Element 遍历与查询
Section titled “Element 遍历与查询”| 方法 | 用途 |
|---|---|
getChildren() | 取得直接子元素。 |
getVisibleChildren() | 取得当前可见的子元素。 |
getParent() | 取得父元素。 |
getFocusedWindow() | 取得应用当前聚焦的窗口。 |
getWindows() | 取得应用的窗口。 |
getWindow() | 取得元素所在窗口。 |
queryElements(selector, maxCount?) | 搜索窗口中的所有元素。会遍历 children;元素层级很大时,可能造成明显阻塞。 |
queryVisibleElement(selector, maxCount?) | 只搜索当前可见的内容。 |
matchesSelector(selector) | 验证这个已知元素是否符合完整选择器,包括父级与祖先条件。 |
选择器写法见元素选择器。
读取和设置值
Section titled “读取和设置值”getValue() 与 setValue(value) 用于读取和设置元素的 AXValue 属性。要访问其他辅助功能属性,请使用 getAttributeValue(name) 与 setAttributeValue(name, value)。值可以是文字、整数或小数。
const value = await element.getValue();await element.setValue("Updated title");
const title = await element.getAttributeValue("AXTitle");await element.setAttributeValue("AXValue", 50);这四个方法都需要 accessibilityAPI 权限。属性不可用、值类型不匹配,或 macOS 拒绝操作时都会抛出错误;需要向用户说明失败原因时,请用 try / catch 处理。
Element 操作
Section titled “Element 操作”以下方法需要 accessibilityAPI 权限,并且可以 await:
| 方法 | 用途 |
|---|---|
performPress() | 触发元素的主要操作。 |
performClick() | 移动到元素并点击。 |
performMoveCursorTo() | 移动到元素。 |
performShowMenu() | 打开元素的菜单。 |
getActions() | 查看元素支持的操作。 |
performAction(identifier) | 触发指定操作。 |
ctx.elementInspector.triggerMenuItem(menuPath) 会触发当前应用中的菜单项。传入包含完整菜单层级标题的数组:
await ctx.elementInspector.triggerMenuItem([ "Navigate", "Move Focus to Next Editor Pane",]);它需要 accessibilityAPI 权限。Cursor Crane 会按菜单层级寻找目标,并且只会对最终菜单项运行 AXPress。
键盘、鼠标和提示
Section titled “键盘、鼠标和提示”ctx.keyboard
Section titled “ctx.keyboard”需要 keyboard 权限。三个方法都返回 Promise:
await ctx.keyboard.pressKey("a", ["command"]);await ctx.keyboard.releaseKey("a", ["command"]);await ctx.keyboard.pressAndReleaseKey("return", []);修饰键可使用 "shift"、"control"、"option" 和 "command"。
ctx.mouse
Section titled “ctx.mouse”需要 mouse 权限:
| 方法 | 用途 |
|---|---|
moveTo(x, y) | 平滑移动指针。 |
warpTo(x, y) | 立即移动指针。 |
click(button, modifiers?) | 点击;0 为左键、1 为右键、2 为中键。 |
doubleClick(button, modifiers?) | 双击。 |
rightClick(modifiers?) | 右键点击。 |
ctx.shell
Section titled “ctx.shell”需要 shell 权限。execute(command) 会运行一条非交互式 zsh 命令,并返回其输出:
const result = await ctx.shell.execute("git status --short");if (result.exitCode === 0) { ctx.toast(result.stdout || "Working tree is clean");}返回值包含 stdout、stderr 和 exitCode。命令与脚本共用执行超时,每一路输出最多保留 1 MiB。
ctx.clipboard
Section titled “ctx.clipboard”需要 clipboard 权限。可以读取或替换系统剪贴板中的纯文本:
const text = ctx.clipboard.readText();if (text !== null) { ctx.clipboard.writeText(text.trim());}剪贴板中没有纯文本时,readText() 返回 null。writeText(value) 会用指定文字替换剪贴板当前的内容。
ctx.toast 与 console
Section titled “ctx.toast 与 console”ctx.toast(message) 用于简短反馈。ctx.toast.highlight(rect, text?, timeout?) 会高亮屏幕上的一个范围,并在下方显示可选提示文字。
ctx.toast("Finished");ctx.toast.highlight(element.rect, "Save button", 3);console.log()、error()、warn()、info() 和 debug() 会写入脚本日志,适合在开发脚本时查看过程与错误。Console 方法会保持原有行为,同时显示在 Cursor Crane 的脚本日志中。
setTimeout 与 clearTimeout
Section titled “setTimeout 与 clearTimeout”使用 setTimeout(callback, delay?, ...args) 可以在至少等待指定毫秒数后执行一次回调。它会返回一个标识符;回调执行前,可以将该标识符传给 clearTimeout(identifier) 以取消定时器。
const timeout = setTimeout((message) => { ctx.toast(message);}, 500, "Still working");
// 不再需要延迟操作时取消它。clearTimeout(timeout);定时器只会在脚本仍处于执行状态时运行。表单关闭前会维持脚本执行,因此定时器可以更新或隐藏已打开的表单;关闭表单会取消尚未执行的定时器。
form 脚本从 run(ctx) 返回一个数组。每项都需要唯一的 id:
function run(ctx) { return [ { id: "selector", kind: "textInput", title: "Selector", value: "Button", }, { id: "run", kind: "button", title: "Query", submit: true, onClick: formCtx => { formCtx.toast(`Searching for ${formCtx.formData.selector}`); }, }, ];}Cursor Crane 会在 run(ctx) 开始前创建并显示 form。对于 async form,在 run(ctx) 完成前会显示加载指示器。form 初始的 run(ctx) 没有超时限制,因此可以等待较慢的初始化工作再返回表单项;run(ctx) 也可以立即更新、隐藏或关闭已显示的 form。
| kind | 常用字段 | 适合什么 |
|---|---|---|
label | text、value? | 展示说明或结果。 |
numberInput | title、value?、min?、max?、step? | 输入数字。 |
textInput | title、value?、placeholder? | 输入单行文字。 |
select | title、value?、options | 从列表中选择。 |
checkbox | title?、value? | 勾选一个选项。 |
toggle | title?、value? | 开关一个选项。 |
button | title、submit?、onClick? | 执行操作。 |
可编辑项可以提供 onChange(newValue, ctx);按钮使用 onClick(ctx)。两者都可以是 async 函数。
form 的 ctx 还提供:
| 成员 | 用途 |
|---|---|
formData | 读取当前表单的值。 |
updateForm(items) | 用新的表单内容替换当前表单。未提供 value 的同名项目会保留用户当前输入。 |
hideForm() | 隐藏 panel,但不结束其会话。再次触发同一个脚本时会显示原来的表单并保留状态;用于 transient 时则会关闭表单。 |
closeForm() | 关闭当前表单。 |
onFormActivate(callback) | 注册一个在已隐藏的 panel 再次显示时触发的回调。回调会收到当前 form context,也可以是 async。 |
当用户回到已隐藏的 panel 时,可以用 onFormActivate 刷新临时 state。它不会在首次显示时触发;transient 会直接关闭而非复用,因此不适用。请在 run(ctx) 中注册:
function run(ctx) { ctx.onFormActivate(async formCtx => { await refreshResults(); formCtx.updateForm(renderForm(formCtx)); });
return renderForm(ctx);}用户可以使用 Tab、Shift + Tab 和方向键在表单中移动焦点。聚焦按钮后按 Enter 或 Space 会触发它;标记为 submit: true 的按钮可用 Command + Enter 从任意位置触发。
对于 panel,Esc 和标题栏的黄色按钮会隐藏表单;红色按钮与 Command + W 会关闭表单并结束会话。再次触发一个已隐藏的 panel 脚本时,会重新显示同一个表单,而不是创建新窗口。transient 表单则会在失去焦点时关闭。
在开发脚本时,可以用 try / catch 显示清楚的提示:
try { await ctx.mouse.moveTo(100, 100);} catch (error) { ctx.toast(String(error));}如果调用了没有声明权限的 API、选择器写错,或目标暂时不可用,Cursor Crane 会让这次调用失败。给常见失败情况加上 toast 或表单中的说明,脚本会更容易使用。