@aixnn/aixautots
发布包已迁移到 npm 用户 aixnn 的 scope,完整包名为 @aixnn/aixautots。本仓库目录仍为 aixautots/,各子包入口保持不变,例如 @aixnn/aixautots/core。
npm install @aixnn/aixautots
发布(在 aixautots/ 目录,使用有权限的 npm 账号):
npm run typecheck
npm test
npm run build
python3 ../scripts/npm-with-access-key.py publish --access public
发布脚本读取 ~/.aixenv 的 NPM_ACCESS_KEY,通过临时 npm 配置使用令牌,结束后删除临时配置,不修改全局登录配置。令牌需拥有 @aixnn/aixautots 的发布权限;第三方项目也可使用自身 npm 凭据执行 npm publish --access public。发布后应运行 npm view @aixnn/aixautots version dist-tags --json --prefer-online 确认远端版本;若 registry 仍显示 0.0.0-stage 占位版本,正式版本尚不可安装,需检查 npm 的待审批发布状态。若令牌未允许绕过双重验证,npm 仍可能要求一次性验证码。
aixautobot 的 TypeScript 客户端 SDK。core/react 是 headless 的数据/传输/鉴权层;ui 既提供细粒度
部件,也提供开箱即用的成品页面(设备/任务/市场/脚本/编辑器/检查器)+ 一套可换肤主题,
让任意项目都能"轻松接入"。桌面端 frontend/ 是全量改用本 SDK 的参考实现。
子路径导出:
| 子包 | 职责 |
|---|---|
@aixnn/aixautots/core | 资源 REST 客户端(AixClient)+ 会话层(设备通道 / Flow / 镜像)+ 类型 + 错误 |
@aixnn/aixautots/react | 上述能力的 React hooks(AixProvider + 资源/会话 hooks) |
@aixnn/aixautots/ui | 设备向细粒度部件(MirrorView/ControlBar/ScriptEditor/DebugConsole/ScriptPanel) |
@aixnn/aixautots/ui/primitives | 基础件(Button/Input/Select/Badge/Dialog/Popover) |
@aixnn/aixautots/ui/{devices,tasks,market,scripts,editor,inspector} | 成品页面组件 |
@aixnn/aixautots/nav | 路由无关导航注入(AixNavProvider/useAixNav) |
@aixnn/aixautots/i18n | 内置 i18n(AixI18nProvider/useAixT,en/zh,无 i18next 依赖) |
@aixnn/aixautots/tailwind-preset · @aixnn/aixautots/styles.css | Tailwind 预设 + 主题 token |
npm i @aixnn/aixautots # peer: react(react 子包用);ui 子包另需 @uiw/react-codemirror
接入(三步)
用到 @aixnn/aixautots/ui 的成品页面/部件时,按以下三步接入样式与运行时上下文:
1) 样式 —— Tailwind 预设 + 主题 token + content 扫描:
// tailwind.config.js
module.exports = {
presets: [require('@aixnn/aixautots/tailwind-preset')],
content: [
'./src/**/*.{ts,tsx}',
'./node_modules/@aixnn/aixautots/dist/**/*.js', // 关键:否则 aixautots 组件的类名不会被生成
],
}
/* 入口 CSS */
@import "@aixnn/aixautots/styles.css"; /* :root 浅色 + .dark 深色 token,可覆盖换肤 */
2) Provider —— 在 AixProvider 内注入导航与语言:
import { AixProvider } from '@aixnn/aixautots/react'
import { AixNavProvider } from '@aixnn/aixautots/nav'
import { AixI18nProvider } from '@aixnn/aixautots/i18n'
import { useNavigate } from 'react-router-dom' // 任意路由库都行
function Providers({ children }) {
const navigate = useNavigate()
return (
<AixProvider config={{ baseUrl, auth }}>
<AixNavProvider nav={{ navigate }}>
<AixI18nProvider lang="zh">{children}</AixI18nProvider>
</AixProvider>
</AixNavProvider>
)
}
3) 用页面 —— 直接挂成品页面,路由参数以 props 传入:
import { DevicesPage } from '@aixnn/aixautots/ui/devices'
import { ScriptEditorPage } from '@aixnn/aixautots/ui/editor'
<DevicesPage />
<ScriptEditorPage scriptId={params.id} />
Quickstart
import { AixClient } from '@aixnn/aixautots/core'
const client = new AixClient({
baseUrl: 'https://aixautobot.com',
auth: { kind: 'user', getToken: async () => myUserJwt },
})
const devices = await client.devices.list()
const { run_id } = await client.fleet.start({ flow, device_ids: [devices[0].id] })
React:
import { AixProvider, useDevices } from '@aixnn/aixautots/react'
function App() {
return (
<AixProvider config={{ baseUrl, auth: { kind: 'user', getToken } }}>
<DeviceList />
</AixProvider>
)
}
function DeviceList() {
const { data, loading, refetch } = useDevices()
// ...
}
鉴权
AixConfig.auth 接受单个或多个凭据;SDK 按请求 scope 自动选用并注入请求头。浏览器只需 bearer 或
project key/secret——不需要、也不应携带网关 X-API-Key(后端 require_principal 已放行任一有效主体)。
type Credential =
| { kind: 'project'; key: string; secret: string } // 第三方:X-Project-Key / X-Project-Secret
| { kind: 'user'; getToken: () => string | Promise<string> } // 自家前端:user JWT
| { kind: 'developer'; getToken: () => string | Promise<string> } // 项目管理 / 平台管理
| Scope | 用到的凭据 | 覆盖的资源 |
|---|---|---|
caller | project key/secret 或 user JWT | devices / scripts / clusters / fleet / tasks / locators / nodes |
developer | developer JWT | projects / auth 管理 / webhooks / llm |
admin | developer JWT(后端校验 admin) | admin(rate/plan/billing)/ images |
public | 无 | auth.login / register / send-code / verify-code |
多凭据示例(自家前端同时持 developer + user):
auth: [
{ kind: 'user', getToken: currentUserToken }, // caller 资源用
{ kind: 'developer', getToken: () => devJwt }, // developer/admin 资源用
]
第三方前端(浏览器不持 project secret):后端铸 user JWT,前端用 createCachedUserCredential 自动缓存/刷新(按 expiresIn 或 JWT exp,并发去重):
import { createCachedUserCredential } from '@aixnn/aixautots/core'
const auth = createCachedUserCredential({
mint: () => fetch('/mint-token').then((r) => r.json()), // 你的后端 → { token, expiresIn? } 或 token 字符串
})
new AixClient({ baseUrl, auth })
兼容旧写法:
getAuthToken: () => userJwt等价于auth: { kind: 'user', getToken }。
资源总览(AixClient + hooks)
| 资源 | client.* | React hook |
|---|---|---|
| 鉴权 | auth | useAuth |
| 项目 | projects | useProjects |
| 设备 | devices | useDevices / useDeviceActions |
| 节点 | nodes | useNodes |
| 集群 | clusters | useClusters |
| 脚本 | scripts | useScripts |
| 定位器 | locators | useLocators |
| Fleet 运行 | fleet | useFleetRun |
| Webhook 任务 | tasks | useTasks / useTaskStatus |
| Webhook 配置 | webhooks | useWebhooks |
| LLM | llm | useLlm |
| 管理/计费/镜像 | admin | useAdmin / useImages |
| 巡检(截图+UI树) | (devices.op) | useInspector |
设备操作:两条路径
REST devices.op | WS DeviceChannel | |
|---|---|---|
| 形态 | 一次性、无状态 | 长连、低延迟 |
| 适用 | 截图 / UI 树 / 安装 / 电源 等 | adb / wda / frida / scriptlet 流式 + 原始隧道、重交互调试 |
| 入口 | client.devices.op(id, { op, ... }) | DeviceChannel.open(cfg, id) |
通用命令:devices.op(id, op) 是唯一入口,op 类型为开放联合——已知命令有自动补全,
任意新命令无需新 SDK 方法直接透传。screenshot()/captureUiTree()/installApkUrl()/power() 等都是它的薄包装。
await client.devices.screenshot(id) // 薄糖
await client.devices.op(id, { op: 'some_future_op', x: 1 }) // 新命令零改动
运行 flow:两条链路
Fleet(fleet / useFleetRun) | Webhook 任务(FlowRun / tasks / useTaskStatus) | |
|---|---|---|
| 端点 | POST /api/fleet/runs | POST /api/webhook/tasks |
| 触发方 | 自家 app 交互式运行 | 第三方 webhook |
| 进度 | 轮询 fleet.get(runId) | 流式 /ws/execution/:task_id(FlowRun)或轮询 tasks.get(id) / 事件 tasks.events(id) |
| 控制 | cancel / pause / resume | tasks.cancel(id)(v1:DB 侧取消未完成 target) |
AI(生成 / 精炼 / 解释)
LLM 由后端网关代理(OpenAI 兼容),developer scope。
const { flow } = await client.llm.generate({ prompt: '写一个登录流程', platform: 'android' })
const refined = await client.llm.refine({ flow, instruction: '登录后再点设置' }) // → { ok, flow? }
const { explanation } = await client.llm.explain(flow) // → 自然语言说明
React:useLlm() → { generate, refine, explain, test }(参考实现见 frontend 的 AIPanel)。
镜像
import { MirrorSession } from '@aixnn/aixautots/core'
const s = await MirrorSession.join(cfg, deviceId)
s.onStream((stream) => { videoEl.srcObject = stream })
s.sendInput({ t: 'tap', x, y }) // 归一坐标 [0,1]
React:useMirror(deviceId) → { stream, sendInput };UI:<MirrorView> + <ControlBar>。
错误
所有失败抛 AixError(含网络层失败,code: 'offline')。按 code 区分:
auth / timeout / offline / locked / protocol / unknown。
参考实现 & 规划
- 参考实现:
frontend/(Tauri 桌面控制台,全量消费本 SDK)。 - 规划与契约:
docs/superpowers/plans/2026-06-07-aixautots-full-client-sdk.md。