文档/插件 index.js 上下文 API

插件 index.js 上下文 API

通用开发

插件 index.js 上下文 API

概述

插件 lifecycle handler 会收到 typed IPluginContext。新插件应把 context.utils 作为 canonical capability surface;宿主会按 verified calling plugin 构造该 facade,并在受保护操作执行前完成 SDK version 与 permission policy 检查。

旧 CommonJS 插件仍可使用 globalThis utilities,但它只是同一组 host-owned capability 的兼容投影。不要导入 CoreApp 内部模块,也不要创建 raw transport channel 绕过 context facade。

Canonical lifecycle context

EXAMPLE.TYPESCRIPT
import type { IPluginLifecycle } from '@talex-touch/utils/plugin/sdk'

const lifecycle: IPluginLifecycle = {
async onInit(context) {
const {
logger,
http,
storage,
secret,
clipboard,
channel,
dialog,
box,
feature,
quickActions,
quickOps,
intelligence,
screenshot,
system,
i18n,
lexicon,
power,
recommend,
divisionBox,
openUrl
} = context.utils

    logger.info(`Loaded ${context.pluginName}`)
    void [
      http,
      storage,
      secret,
      clipboard,
      channel,
      dialog,
      box,
      feature,
      quickActions,
      quickOps,
      intelligence,
      screenshot,
      system,
      i18n,
      lexicon,
      power,
      recommend,
      divisionBox,
      openUrl
    ]
  },

    onFeatureTriggered(featureId, query) {
      // query 可以是 string,也可以是带 text/image/files/html inputs 的 TuffQuery。
      console.log(featureId, query)
    }

}

## export default lifecycle

context 还包含 pluginPath 与插件 config。secret 必须写入 context.utils.secret,不能进入普通 storage 或日志。

Capability 分组

Context field用途主要文档
box, featureCoreBox 窗口与 result item lifecycleBox、Feature
clipboard, storage, secret剪贴板、插件数据与受保护凭据Clipboard、Storage
intelligenceAI capability discovery、invoke 与 streamIntelligence
screenshot权限门禁下的显示器、指针屏幕与区域截图Screenshot
system前台应用与 permission-gated 选中文本Clipboard
i18n, lexicon主机语言与插件隔离 Domain LexiconLocalization
quickActions, quickOps全局动作与 bounded built-in toolsQuick Actions、QuickOps
divisionBox, channel独立窗口与插件 transportDivisionBox、Channel
power, recommend低电量适配与 recommendation providerPower、Recommend

受保护 capability 仍要求 manifest.json 声明对应 permission,且当前 grant 有效。字段存在于 context.utils 不代表每个操作都已获授权。

Legacy global 兼容

现有 index.js 插件仍可从 globalThis 读取 logger、clipboard、storage、feature、box、openUrl 等 utilities。新 lifecycle 代码应在 onInit 中获取 context.utils,因为它才包含完整 typed SDK surface,包括 secret、intelligence、screenshot、system、i18n 与 lexicon。


logger

插件日志记录器,日志会保存到插件的日志目录。

EXAMPLE.JAVASCRIPT
logger.info('信息日志', { extra: 'data' })
logger.warn('警告日志')
logger.error('错误日志', error)
logger.debug('调试日志')

http

HTTP 请求库 (基于 axios):

EXAMPLE.JAVASCRIPT
// GET 请求
const response = await http.get('https://api.example.com/data', {
headers: { 'Authorization': 'Bearer token' },
signal // AbortSignal 用于取消请求
})

// POST 请求
const result = await http.post('https://api.example.com/submit', {
data: 'payload'
}, { signal })

clipboard

剪贴板操作:

EXAMPLE.JAVASCRIPT
// 写入文本
clipboard.writeText('复制的内容')

// 读取文本
const text = clipboard.readText()

// 读取图片
const image = clipboard.readImage()

// 写入图片
clipboard.writeImage(nativeImage)

storage

插件专属存储(每个插件 10MB 限制):

EXAMPLE.JAVASCRIPT
// 读取配置文件
const config = storage.getFile('providers_config')

// 保存配置文件
storage.setFile('providers_config', { key: 'value' })

// 删除配置文件
storage.deleteFile('old_config')

// 列出所有文件
const files = storage.listFiles() // ['file1', 'file2']

// 监听配置变化
const unsubscribe = storage.onDidChange('providers_config', (newConfig) => {
console.log('配置已更新:', newConfig)
})

// 取消监听
unsubscribe()

power

PowerSDK:用于在低电量场景自动做能力降级。

EXAMPLE.JAVASCRIPT
// 读取当前低电量状态
const status = await power.getLowPowerStatus({ threshold: 25 })

if (status.lowPower) {
logger.info('低电量模式,跳过重负载后台任务')
}

// 监听状态变化
const disposePower = power.onLowPowerChanged((nextStatus) => {
logger.info('低电量状态变化', nextStatus)
})

// 可选:取消监听
disposePower()

说明:在 index.js 上下文里,power.onLowPowerChanged 当前是轮询(约 60 秒),严格实时推送还在待定。


recommend

RecommendSDK:向 CoreBox 推荐引擎注册自定义推荐提供者。registerProvider 与 unregisterProvider 都返回 Promise,必须 await;provider 必须实现 onExecute,否则注册时抛错。

EXAMPLE.JAVASCRIPT
// 注册推荐提供者
const dispose = await recommend.registerProvider({
id: 'my-recommendation',
name: '我的推荐',
canProvide(context) {
return context.time.timeSlot === 'morning'
},
getCandidates(context) {
return [{
id: 'morning-tip',
title: '早间提醒',
subtitle: '开始新的一天',
icon: { type: 'emoji', value: '☀️' },
priority: 75,
action: 'show-morning-tip'
}]
},
// 返回 true 或 undefined 表示主操作已被接受,宿主记一次使用;
// 返回 false 或抛错表示失败,不计数。
async onExecute(candidate) {
if (candidate.action !== 'show-morning-tip') return false
return await showMorningTip()
}
})

// 注销提供者
await dispose()
// 或
await recommend.unregisterProvider('my-recommendation')

详见 RecommendSDK API


feature

Feature SDK,用于管理搜索结果:

EXAMPLE.JAVASCRIPT
// 推送搜索结果
feature.pushItems([
new TuffItemBuilder('item-1')
.setTitle('搜索结果标题')
.setSubtitle('副标题')
.setIcon({ type: 'file', value: 'assets/icon.svg' })
.build()
])

// 清空当前插件的搜索结果
feature.clearItems()

// 获取当前插件的搜索结果
const items = feature.getItems()

box

CoreBox 控制 SDK:

EXAMPLE.JAVASCRIPT
// 隐藏 CoreBox
box.hide()

// 显示 CoreBox
box.show()

// 设置输入框内容
box.setInput('新的输入内容')

// 获取输入框内容
const input = box.getInput()

boxItems

BoxItem 管理 SDK(新版 API):

EXAMPLE.JAVASCRIPT
// 推送单个 item
boxItems.push(item)

// 批量推送 items
boxItems.pushItems([item1, item2])

// 更新指定 item
boxItems.update('item-id', { title: '新标题' })

// 删除指定 item
boxItems.remove('item-id')

// 清空该插件的所有 items
boxItems.clear()

// 获取该插件的所有 items
const items = boxItems.getItems()

quickActions / meta

QuickActions SDK 用于注册 MetaK / Quick Actions 全局动作,也可以从动作中调用原生分享。meta 是历史兼容别名,和 quickActions 指向同一个 SDK 实例;新插件请优先使用 quickActions。

EXAMPLE.JAVASCRIPT
quickActions.registerAction({
id: 'share-current-item',
render: {
basic: {
title: '分享当前项目',
subtitle: '使用当前平台可用的原生分享目标',
icon: { type: 'class', value: 'i-ri-share-line' }
},
group: '分享'
}
})

quickActions.onActionExecute(async ({ actionId, item }) => {
if (actionId !== 'share-current-item') return

    const result = await quickActions.shareItem(item, {
      preferredTargets: ['airdrop', 'system-share', 'mail']
    })

    if (!result.success) {
      logger.warn('原生分享失败', result.error)
    }

## })

常用方法:

方法说明
registerAction(action)注册一个 MetaK / Quick Actions 全局动作
onActionExecute(handler)监听当前插件注册动作的执行事件
getNativeShareTargets(payloadType?)查询当前平台真实可用的原生分享目标
resolveNativeShareTarget(options?)按 payload 类型和偏好顺序解析分享目标
nativeShare(payload, options?)复用 Flow Transfer 执行原生分享
createSharePayloadFromItem(item, options?)将 CoreBox item 转成 Flow payload
shareItem(item, options?)一步完成 item payload 构造、目标解析和分享

详见 QuickActions SDK


plugin

当前插件信息 API:

EXAMPLE.JAVASCRIPT
// 获取插件完整信息
const info = plugin.getInfo()
// { name, version, desc, readme, dev, status, features, issues, ... }

// 获取插件路径
const path = plugin.getPath()

// 获取数据目录
const dataPath = plugin.getDataPath()

// 获取配置目录
const configPath = plugin.getConfigPath()

// 获取日志目录
const logsPath = plugin.getLogsPath()

// 获取临时目录
const tempPath = plugin.getTempPath()

// 获取当前状态
const status = plugin.getStatus()

// 获取开发配置
const devInfo = plugin.getDevInfo()

// 获取平台支持信息
const platforms = plugin.getPlatforms()

plugins

其他插件 API(只读访问):

EXAMPLE.JAVASCRIPT
// 获取所有插件列表
const allPlugins = await plugins.list()

// 获取指定插件信息
const otherPlugin = await plugins.get('other-plugin-name')

// 获取插件状态
const status = await plugins.getStatus('other-plugin-name')

features

动态 Feature 管理:

EXAMPLE.JAVASCRIPT
// 动态添加 Feature
features.addFeature({
id: 'dynamic-feature',
name: '动态功能',
desc: '运行时添加的功能',
icon: { type: 'file', value: 'assets/icon.svg' },
push: true,
commands: [{ type: 'over', value: ['动态'] }],
priority: 5
})

// 移除 Feature
features.removeFeature('dynamic-feature')

// 获取所有 Features
const allFeatures = features.getFeatures()

// 获取指定 Feature
const feature = features.getFeature('feature-id')

// 设置优先级
features.setPriority('feature-id', 10)

// 获取优先级
const priority = features.getPriority('feature-id')

// 按优先级排序获取
const sorted = features.getFeaturesByPriority()

运行时新增 Feature 的 icon.type: 'file' 由宿主初始化。相对路径会在所属插件根目录内解析;目录穿越或目标缺失会 fail closed,不会把未解析的相对路径留给 CoreBox。


channel

IPC 通道桥接:

EXAMPLE.JAVASCRIPT
// 发送消息到主进程
const result = await channel.sendToMain('event-name', { data: 'payload' })

// 发送消息到渲染进程
await channel.sendToRenderer('event-name', { data: 'payload' })

// 监听主进程消息
const dispose = channel.onMain('event-name', (data) => {
console.log('收到主进程消息:', data)
})

// 监听渲染进程消息
const dispose = channel.onRenderer('event-name', (data) => {
console.log('收到渲染进程消息:', data)
})

// 访问原始 channel 对象
channel.raw

$event

Feature 事件监听:

EXAMPLE.JAVASCRIPT
// 监听 Feature 生命周期
$event.onFeatureLifeCycle('feature-id', {
onLaunch: (feature) => { console.log('启动', feature) },
onFeatureTriggered: (data, feature) => { console.log('触发', data) },
onInputChanged: (input) => { console.log('输入变化', input) },
onClose: (feature) => { console.log('关闭', feature) }
})

// 取消监听
$event.offFeatureLifeCycle('feature-id', callback)

dialog

系统对话框:

EXAMPLE.JAVASCRIPT
// 消息对话框
await dialog.showMessageBox({
type: 'info',
title: '标题',
message: '消息内容',
buttons: ['确定', '取消']
})

// 打开文件对话框
const result = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections'],
filters: [{ name: 'Images', extensions: ['jpg', 'png'] }]
})

// 保存文件对话框
const result = await dialog.showSaveDialog({
defaultPath: 'file.txt'
})

divisionBox

DivisionBox SDK,用于创建独立窗口:

EXAMPLE.JAVASCRIPT
// 打开 DivisionBox
const session = await divisionBox.open({
url: 'plugin://my-plugin/index.html',
title: '独立窗口',
size: 'medium', // 'compact' | 'medium' | 'expanded'
keepAlive: true
})

// 关闭 DivisionBox
await divisionBox.close(session.sessionId)

// 监听状态变化
divisionBox.onStateChange(session.sessionId, (state) => {
console.log('状态变化:', state)
})

TuffItemBuilder

搜索结果构建器:

EXAMPLE.JAVASCRIPT
const item = new TuffItemBuilder('unique-id')
.setSource('plugin', 'plugin-features')
.setTitle('标题')
.setSubtitle('副标题')
.setIcon({ type: 'file', value: 'assets/icon.svg' })
.createAndAddAction('action-id', 'copy', '复制', '复制的内容')
.addTag('标签', 'blue')
.setMeta({
pluginName: 'my-plugin',
featureId: 'my-feature',
customData: 'any value'
})
.build()

openUrl

打开外部链接:

EXAMPLE.JAVASCRIPT
openUrl('https://example.com')

生命周期钩子

插件 index.js 需要导出生命周期钩子对象:

EXAMPLE.JAVASCRIPT
const pluginLifecycle = {
/\*\*
_ Feature 被触发时调用
_ @param {string} featureId - Feature ID
_ @param {string|TuffQuery} query - 查询内容
_ @param {IPluginFeature} feature - Feature 定义
_ @param {AbortSignal} signal - 用于取消操作
_/
async onFeatureTriggered(featureId, query, feature, signal) {
// 兼容新版本:query 可能是字符串或 TuffQuery 对象
const queryText = typeof query === 'string' ? query : query?.text

      // 处理 Feature 逻辑...
    },

    /**
     * 搜索结果项被点击时调用
     * @param {TuffItem} item - 被点击的项
     */
    async onItemAction(item) {
      if (item.meta?.defaultAction === 'copy') {
        const copyAction = item.actions.find(a => a.type === 'copy')
        if (copyAction?.payload) {
          clipboard.writeText(copyAction.payload)
          box.hide()
        }
      }
    }

}

## module.exports = pluginLifecycle

技术原理

  • 上下文对象由主进程注入到插件沙箱,统一提供系统能力入口。
  • 权限校验与能力限制在主进程完成,避免插件绕过限制。

最佳实践

  1. 使用 AbortSignal:在异步操作中传递 signal 参数,支持用户取消
  2. 错误处理:使用 try-catch 包装所有异步操作
  3. 日志记录:使用 logger 而不是 console,便于调试和收集
  4. 存储限制:注意 10MB 存储限制,大文件使用 tempPath
  5. 兼容 TuffQuery:处理 query 时兼容字符串和对象两种格式

相关文档