高级导出 API 完整参考
本参考按 package.json exports 覆盖全部公开导入路径和命名导出,列出所有导出类不以下划线开头的公开原型方法。下划线方法及未通过包导出的 src/device_* 实现模块属于内部接口。应用从 应用 API 完整参考 开始,不应在同一连接并行创建多个所有者/解析器。以下签名使用 JavaScript 参数/默认值语法,异步方法返回 Promise。
@esp-vision/device-core
根入口仅导出 createDevice(options),全部参数与返回设备对象的方法见 应用 API 完整参考。
@esp-vision/device-core/evmux
EvMuxParser
new EvMuxParser(handlers = {})
增量字节解析器,处理不完整帧边界、CRC、过滤和 channel 分发。handlers 包括 text、rawText、log、command、other、preview(base64, metadata, bytes)、debugImage(base64, metadata)、rpc(metadata, payload)、filterFrame(metadata, payload)、frameRejected(metadata)。filterFrame 返回 false 时阻止该帧交付。
EvMuxParser.setLimits({ metadataMax, payloadMax, frameProgressTimeoutMs, frameTotalTimeoutMs } = {})更新正值 metadata/payload 大小和不完整帧时间限制。
EvMuxParser.resetReplTextDecoders()重置 stdout/stderr 的 UTF-8 解码状态。
EvMuxParser.reset()清除输入缓冲、不完整帧定时器与统计。
EvMuxParser.getDiagnostics()返回 active/rxBytes/rxChunks/bufferedBytes/frames/droppedFrames/lastChannel。
EvMuxParser.hasPartialFrame()判断缓冲中是否存在可能的不完整帧。
EvMuxParser.feedText(text)将兼容文本编码后输入解析器,优先使用字节入口。
EvMuxParser.feedBytes(bytes)输入任意 Uint8Array 块,允许拆帧、粘帧。
EvMuxParser.dispatch(metadata, payload)将已解析 metadata/payload 分发至 channel handler,此入口绕过线上帧校验。
makeEvMuxFrame
makeEvMuxFrame({ channel, type = 'data', method = null, payload = '', seq = 0, sid = 'web-ide', sessionId = undefined, vmEpoch = undefined, contentType = undefined, width = undefined, height = undefined, route = undefined, encoding = undefined })
返回含 metadata、文本/二进制 payload 和 CRC 的线上 Uint8Array。seq 用于关联,sessionId/vmEpoch 标识所有权代次;编码不发送数据,也不获取会话。
@esp-vision/device-core/evrepl
EV_REPL_FILE_CHUNK_BYTES
兼容 REPL 文件写入器的默认块大小,与 script.write RPC 分块不同。
224
EV_REPL_INPUT_MAX_BYTES
完整 REPL 输入命令的默认 UTF-8 预算,包括 exec 转义开销。
511
EvRepl
new EvRepl(io)
framed MicroPython REPL 协调器。io.writeEvmux(channel, type, method, payload) 必须返回 Promise,并持续输入 stdout。下列公开方法也包含解释器记账辅助操作,应用通常使用 device.python/files。
EvRepl.execInputBytes(source)计算转义后的 REPL exec 命令 UTF-8 字节数,包含命令包装开销。
EvRepl.createScriptEndSentinel()生成并返回唯一脚本完成标记打印命令。
EvRepl.createScriptRunCommand(path)生成含内联完成标记的 execfile 运行命令。
EvRepl.cancelScriptEndSentinel()清除待匹配的完成标记。
EvRepl.drainScriptEnd()消费缓冲的脚本完成标记,通知 onScriptEnd。
EvRepl.partialMarkerTail()查找末尾不完整完成标记以保留缓冲。
EvRepl.feedStdout(text)输入文字,推进提示符/输出等待者。
EvRepl.drain()处理已缓冲的提示符与完成数据。
EvRepl.failAll(error)以错误拒绝排队/当前等待者。
EvRepl.resetEpoch(error)重置解释器 epoch 状态,使旧等待者失效。
EvRepl.enqueue(operation, { signal = null } = {})串行执行 operation,返回其结果 Promise,支持取消。
EvRepl.writeStdin(data)向 repl.stdin 写字符串/Uint8Array。
EvRepl.execLine(line, { timeout = 8000, signal = null } = {})执行准备好的行,等待提示符/输出。
EvRepl.execPy(source, { timeout = 8000, signal = null } = {})将源码转义为 exec 命令,返回收集的输出。
EvRepl.getDeviceInfo({ signal = null } = {})查询平台、实现、板卡及解释器信息。
EvRepl.getFsStats(path = '/', { signal = null } = {})查询路径所在文件系统容量/空闲信息。
EvRepl.listFs(path, { recursive = false, signal = null } = {})返回平铺 {name,path,type,size} 条目,可递归。
EvRepl.walkFs({ signal = null } = {})返回兼容接口使用的嵌套文件树。
EvRepl.readFile(fn, { timeout = 30000, signal = null, maxBytes = null } = {})返回完整文件 Uint8Array;文件变化、超限或不完整时拒绝。
EvRepl.touchFile(fn)创建/截断一个文件,Promise<void>。
EvRepl.removeFile(path, { signal = null } = {})删除一个文件,Promise<void>。
EvRepl.removeDir(path, { signal = null } = {})删除空目录,Promise<void>。
EvRepl.makePath(path)通过兼容 REPL 辅助方法创建路径。
EvRepl.writeFile(fn, data, chunk_size = EV_REPL_FILE_CHUNK_BYTES, direct = false, { signal = null } = {})兼容 REPL 字节/分块写入器;应用 files.write 使用会话 RPC 写入器。
EvRepl.readSysInfoMD()返回 Markdown 格式的系统信息。
evReplExecInputBytes
evReplExecInputBytes(source)
计算转义后的 REPL exec 命令 UTF-8 字节数,包含命令包装开销。
@esp-vision/device-core/session
DeviceSession
new DeviceSession({ transport, EvMuxParser, makeEvMuxFrame, EvRepl, handlers = {}, sid = 'ide-client', sleep = defaultSleep, })
底层 v4 解释器,注入 transport、EvMuxParser、EvRepl、makeEvMuxFrame;handlers 接收 text/rawText/log/preview/rpc/diagnostic 及会话生命周期回调。调用方须订阅传输输入并喂入字节,构造类本身不打开连接;应用入口 createDevice 已封装这些生命周期操作。
DeviceSession.feedBytes(bytes)向会话解析器输入接收字节。
DeviceSession.feedText(text)输入兼容文本。
DeviceSession.hasFeature(feature)判断协商能力是否声明指定 feature。
DeviceSession.writeFrame(channel, type, method, payload, { authorize = null, vmRequest = null, seq: requestedSeq = null, signal = null, writeTimeout = null, } = {})按当前会话/epoch 编码发送帧,返回传输 Promise。
DeviceSession.rpc(method, payload = {}, { timeout = 4000, channel = null, signal = null, binaryResponse = false } = {})发送关联 RPC,等待 JSON 或 binaryResponse;超时、取消、固件错误会拒绝。
DeviceSession.captureImage(method, payload = {}, options = {})调用抓图方法,返回 JPEG {data,mimeType,metadata}。
DeviceSession.negotiate(options = {})探测 v4、校验身份/限制、获取所有权,可验证 REPL。
DeviceSession.writeFile(path, content, { encoding = 'utf-8', mode = 'overwrite', runAfterWrite = false, signal = null, onProgress = null, } = {})通过统一的 file.write 二进制引擎写入 UTF-8/字节,完成 SHA-256 提交后返回;要求 file.write.binary,不回退旧上传路径。
DeviceSession.writeScript(path, content, { runAfterWrite = false, signal = null } = {})writeFile 的 UTF-8 兼容封装。
DeviceSession.createOperationId()生成新的网络修改 operation ID。
DeviceSession.networkStatus(options = {})返回 network.status 状态/结果。
DeviceSession.configureNetwork({ ssid, password, operationId = undefined }, options = {})发送凭据和 operationId,返回 accepted ACK,不代表最终联网成功。
DeviceSession.setNetworkEnabled(enabled, options = {})发送持久化网络开关修改。
DeviceSession.forgetNetwork(options = {})请求清除持久化凭据。
DeviceSession.release({ timeout = 1200 } = {})释放 EV-MUX 会话租约,Promise 返回 RPC 结果。
DeviceSession.executePython(source, { timeout = 8000, signal = null } = {})通过 framed REPL 执行源码并返回输出。
DeviceSession.interrupt()通过 framed signal 通道发送 Ctrl-C。
DeviceSession.control(operation, { timeout = 15000, signal = null } = {})执行 stop/soft-reset/hard-reset/bootloader 控制。
DeviceSession.stopForRepl({ timeout = 5000, signal = null } = {})中断并等待可执行 REPL,返回就绪布尔值。
DeviceSession.runScript(path, content, { timeout = 10000, signal = null, onStarted = null, maxOutputChars = Number.POSITIVE_INFINITY, trial = null, inlineCompletion = false, } = {})执行设备脚本,可先上传 content,等待有界完成。
DeviceSession.startScript(path, content, { onStarted = null, ...options } = {})启动脚本,返回含 done Promise 的句柄。
DeviceSession.scriptRunSnapshot()返回当前运行快照或 null。
DeviceSession.snapshot()返回会话状态、role、能力、parser/transport 统计和待处理 RPC 数量。
DeviceSession.close(error = new Error('connection closed'))拒绝待处理工作,重置会话状态/定时器;物理连接由宿主关闭。
可读取的协商状态包括 state、role、identity、capabilities、sessionId、vmEpoch、vmReady、limits、parser、repl;应通过会话方法更新,不直接赋值。negotiate 选项为 role=”unknown”、drainMs=300、acquire=true、probeRepl=true、expectedDevice=null、signal=null,TCP 跳过启动 drain。control 接受 stop/soft-reset/hard-reset/bootloader 字符串,不是应用对象的请求结构。底层网络修改返回 accepted ACK,应用 configure 才会等到对应事务完成。使用 typed run trial 时要求 operation_id 为 UUIDv4、source_path 不超过 512 个 UTF-8 字节、source_hash 为 64 位小写 SHA-256,并要求固件声明 launcher trial/typed-runner 能力;trial 不能与 inlineCompletion 组合。
EV_MUX_DEFAULT_LIMITS
协议回退限制,实际操作使用协商的固件 limits;txLargePayloadMax=0 是初始回退值,不表示所有设备只能发送零字节图像。
{
"metadataMax": 384,
"rxPayloadMax": 2048,
"replInputMax": 511,
"vmPendingMax": 1,
"txLargePayloadMax": 0,
"txFrameTimeoutMs": 1500,
"txQueueTimeoutMs": 2000
}
EV_MUX_PATH_MAX_BYTES
设备路径 UTF-8 字节限制,已考虑临时文件名余量。
123
EV_MUX_PAYLOAD_LIMIT
兼容 payload 预算常量,不替代协商的 RX 限制。
1800
EV_MUX_REQUIRED_CHANNELS
v4 能力校验要求的 channel 集合。
[
"user.rpc",
"debug.rpc",
"repl.stdin",
"repl.stdout",
"repl.stderr",
"repl.signal",
"log.idf",
"preview.frame"
]
EV_MUX_REQUIRED_FEATURES
v4 能力校验要求的 feature 集合。
[
"capabilities",
"route.changed",
"framed.repl",
"session.firstCome",
"session.keepalive"
]
EV_MUX_SCRIPT_CHUNK_BYTES
旧 script.write 的参考常量;SDK 0.3.0 上传不再使用,二进制块长取自 fileWriteChunkMax。
900
EV_MUX_VERSION
支持的会话协议版本。
4
evmuxRpcChannelForMethod
evmuxRpcChannelForMethod(method)
debug.* 方法返回 debug.rpc,其他返回 user.rpc。
requiresEvmuxFirmwareUpgrade
requiresEvmuxFirmwareUpgrade(err)
判断是否为需要升级固件的错误,返回布尔值,包括版本不匹配或固件升级标记。
validateEvmuxCapabilities
validateEvmuxCapabilities(payload)
校验 v4 版本、必需 features/channels,并校验会话租约,返回 void;不匹配或缺失能力时抛错。
@esp-vision/device-core/actions
DEVICE_ACTION_DESCRIPTORS
下列 17 个描述定义宿主动作契约,不代表 Core 已实现平台端口枚举、连接 UI 或 workspace 部署;这些 handler 须显式绑定。输入以各描述的 inputSchema 为准,outputSchema 允许 handler 自定义附加字段,risk 含 readOnly/destructive/openWorld 标志。
device.listPorts
List platform-native serial candidates for the user to select; this action never chooses a device.
{
"inputSchema": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": true,
"destructive": false,
"openWorld": false
},
"requiresReadyDevice": false,
"concurrency": "shared-read"
}
device.connect
Connect to an ESP Vision device using standard elicitation for a serial port or an explicit device TCP address, then acquire an EV-MUX v4 session.
{
"inputSchema": {
"type": "object",
"properties": {
"transport": {
"enum": [
"serial",
"tcp"
],
"default": "serial",
"description": "Use serial for a platform port or tcp for an IP address on the device network."
},
"port": {
"type": "string",
"description": "Platform-native serial identifier, such as /dev/ttyACM0 or /dev/cu.usbmodem*. Omit it only when the MCP host advertises elicitation or ESP_VISION_PORT is configured. Non-interactive callers must pass it explicitly. A direct network address may also be supplied as tcp://IP:PORT or IP:PORT; alternatively set transport to tcp and provide host and tcpPort."
},
"host": {
"type": "string",
"description": "IPv4 or IPv6 address of the device when transport is tcp."
},
"tcpPort": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 3232,
"description": "Device EV-MUX TCP port."
},
"baudRate": {
"type": "integer",
"minimum": 1200,
"maximum": 4000000,
"default": 115200
},
"drainMs": {
"type": "integer",
"minimum": 0,
"maximum": 3000,
"default": 300
}
},
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": false,
"openWorld": true
},
"requiresReadyDevice": false,
"concurrency": "exclusive"
}
device.disconnect
Disconnect the active device session and release its lease.
{
"inputSchema": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": false
},
"requiresReadyDevice": false,
"concurrency": "exclusive"
}
device.negotiate
Discover EV-MUX v4, explicitly acquire a session, and prove the framed REPL is live.
{
"inputSchema": {
"type": "object",
"properties": {
"role": {
"type": "string",
"default": "unknown"
},
"drainMs": {
"type": "integer",
"minimum": 0,
"maximum": 3000,
"default": 300
}
},
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": false,
"openWorld": false
},
"requiresReadyDevice": false,
"concurrency": "exclusive"
}
device.inspect
Inspect protocol state, device identity, filesystem, and recent logs.
{
"inputSchema": {
"type": "object",
"properties": {
"includeFiles": {
"type": "boolean",
"default": true
},
"logLimit": {
"type": "integer",
"minimum": 0,
"maximum": 500,
"default": 100
}
},
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": true,
"destructive": false,
"openWorld": false
},
"requiresReadyDevice": true,
"concurrency": "shared-read"
}
python.execute
Execute MicroPython through the shared interpreter using bounded REPL or binary file staging.
{
"inputSchema": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"timeoutMs": {
"type": "integer",
"minimum": 100,
"maximum": 120000,
"default": 8000
}
},
"required": [
"code"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": true
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
python.runEphemeral
Run one Python draft from the reserved temporary device path without modifying a user-owned file.
{
"inputSchema": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"maxDurationMs": {
"type": "integer",
"minimum": 1000,
"maximum": 180000,
"default": 180000
}
},
"required": [
"code"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": true
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
python.observeEphemeral
Observe the bounded output and status of the current temporary Python run.
{
"inputSchema": {
"type": "object",
"properties": {
"runId": {
"type": "string"
},
"waitMs": {
"type": "integer",
"minimum": 0,
"maximum": 10000,
"default": 1000
},
"outputOffset": {
"type": "integer",
"minimum": 0,
"maximum": 1048576,
"default": 0
}
},
"required": [
"runId"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": true,
"destructive": false,
"openWorld": false
},
"requiresReadyDevice": true,
"concurrency": "shared-read"
}
python.stopEphemeral
Stop the current temporary Python run and remove its reserved device file.
{
"inputSchema": {
"type": "object",
"properties": {
"runId": {
"type": "string"
}
},
"required": [
"runId"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": false
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
deviceFile.read
Read a file from the device filesystem.
{
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"encoding": {
"enum": [
"utf-8",
"base64"
],
"default": "utf-8"
}
},
"required": [
"path"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": true,
"destructive": false,
"openWorld": false
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
deviceFile.write
Write a file to the device using the negotiated transfer path.
{
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"content": {
"type": "string"
},
"encoding": {
"enum": [
"utf-8",
"base64"
],
"default": "utf-8"
},
"runAfterWrite": {
"type": "boolean",
"default": false
}
},
"required": [
"path",
"content"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": true
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
workspaceFile.deploy
Stream one workspace-relative file to the device without placing binary content in the model context.
{
"inputSchema": {
"type": "object",
"properties": {
"sourcePath": {
"type": "string",
"description": "Workspace-relative source file path."
},
"devicePath": {
"type": "string",
"description": "Absolute destination path on the device."
},
"expectedBytes": {
"type": "integer",
"minimum": 0,
"maximum": 16777216
},
"expectedSha256": {
"type": "string",
"description": "Optional lowercase SHA-256 expected before transfer."
},
"overwrite": {
"type": "boolean",
"default": false
}
},
"required": [
"sourcePath",
"devicePath"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": true
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
deviceFile.remove
Remove one file or one empty directory from the device filesystem.
{
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"kind": {
"enum": [
"file",
"directory"
],
"default": "file"
}
},
"required": [
"path"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": true
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
deviceTest.run
Write, run, and capture a target-side MicroPython test.
{
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"default": "/.agent_test.py"
},
"source": {
"type": "string"
},
"timeoutMs": {
"type": "integer",
"minimum": 500,
"maximum": 120000,
"default": 10000
}
},
"required": [
"source"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": true
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
device.control
Interrupt, reset, or enter the bootloader.
{
"inputSchema": {
"type": "object",
"properties": {
"operation": {
"enum": [
"stop",
"soft-reset",
"hard-reset",
"bootloader"
]
},
"confirmBootloader": {
"type": "boolean",
"default": false
}
},
"required": [
"operation"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": false
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
device.rpc
Call a negotiated user or debug RPC method. For network.configure, network.enable, and network.forget, include a 32-character lowercase hexadecimal operationId and reuse the exact same ID and payload when retrying after a timeout.
{
"inputSchema": {
"type": "object",
"properties": {
"method": {
"type": "string"
},
"payload": {
"type": "object",
"default": {}
},
"channel": {
"enum": [
"user.rpc",
"debug.rpc"
]
},
"timeoutMs": {
"type": "integer",
"minimum": 100,
"maximum": 120000,
"default": 4000
}
},
"required": [
"method"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": false,
"destructive": true,
"openWorld": true
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
frame.capture
Capture one JPEG frame for an explicitly requested observation.
{
"inputSchema": {
"type": "object",
"properties": {
"timeoutMs": {
"type": "integer",
"minimum": 500,
"maximum": 30000,
"default": 8000
}
},
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"additionalProperties": true
},
"risk": {
"readOnly": true,
"destructive": false,
"openWorld": false
},
"requiresReadyDevice": true,
"concurrency": "exclusive"
}
DEVICE_ACTION_SCHEMA_VERSION
宿主动作描述的 schema 版本。
1
DEVICE_ACTORS
宿主动作层允许的 actor 名称,普通设备对象不要求 actor。
[
"agent"
]
DeviceActionError
new DeviceActionError(code, message, { recovery = null, details = null, cause = null } = {})
结构化宿主动作错误,含 code/message 和可选 recovery/details/cause;toJSON 不序列化 cause。
DeviceActionError.toJSON()返回 {code,message,recovery?,details?}。
DeviceActionGateway
new DeviceActionGateway({ registry = new DeviceActionRegistry(), coordinator = new OperationCoordinator(), lease = new DeviceLease(), getSession = () => null, authorize = async () => true, } = {})
校验动作输入/context,检查宿主租约与 ready 会话,调用 authorize(descriptor, input, context),再协调已绑定 handler。context.actor 必须受支持,context.requestId 必填,context.signal 支持取消。宿主须提供实际授权策略,默认 authorize 返回 true。
DeviceActionGateway.invoke(actionName, input = {}, context = {})Promise:返回校验/授权后 handler 的结果;错误附动作上下文。
DeviceActionRegistry
new DeviceActionRegistry(descriptors = DEVICE_ACTION_DESCRIPTORS, { schemaVersion = DEVICE_ACTION_SCHEMA_VERSION } = {})
动作描述与显式绑定 handler 的注册表。描述要求 name、inputSchema、outputSchema、risk。未绑定描述仅是元数据,Gateway.invoke 会以 ACTION_UNAVAILABLE 拒绝调用;register/bind 返回 this,可链式调用。
DeviceActionRegistry.register(descriptor, handler = null)注册描述与可选 handler。
DeviceActionRegistry.bind(name, handler)为已有动作绑定 handler。
DeviceActionRegistry.descriptor(name)返回描述或 null。
DeviceActionRegistry.handler(name)返回 handler 或 null。
DeviceActionRegistry.list()返回描述数组。
DeviceActionRegistry.validate(name, input)校验输入、应用默认值,返回校验后的副本。
DeviceLease
new DeviceLease()
宿主侧 actor 租约,与固件 EV-MUX 会话租约不同。actor 必须属于 DEVICE_ACTORS,同 actor 的多个句柄按数量维护;它不获取或强制抢占设备会话。
DeviceLease.acquire(actor)返回 {id,actor,release},不同 actor 冲突时抛错。
DeviceLease.release(id)释放一个宿主租约 ID。
DeviceLease.assertAccess(actor)actor 未持有宿主租约时抛错。
DeviceLease.snapshot()返回 {state,primary,actors,holders}。
DeviceLeaseConflictError
new DeviceLeaseConflictError(owner, requester)
宿主 actor 与当前租约持有者冲突时的 DeviceActionError。
OperationCoordinator
new OperationCoordinator()
支持取消与关闭处理的宿主串行队列。此适配层会排队,设备对象自身则拒绝冲突;operation 接收对应 AbortSignal。
OperationCoordinator.run({ actor, requestId, actionName, signal }, operation)将 operation 排队,返回结果 Promise。
OperationCoordinator.close(reason = actionAbortError('Device session closed'))拒绝排队工作并取消当前工作,停止接受新操作。
OperationCoordinator.snapshot()返回当前及排队的 actor/request/action 身份。
@esp-vision/device-core/bindings
DeviceEvidenceBuffer
new DeviceEvidenceBuffer(limit = MAX_EVIDENCE_ENTRIES)
有界宿主证据历史,构造默认上限 500,snapshot 默认 100;值会限长,应用仍须处理敏感信息。
DeviceEvidenceBuffer.append(kind, value, details = null)追加带时间戳、限长的值及可选 details。
DeviceEvidenceBuffer.snapshot(limit = 100)返回最近若干条记录。
DeviceEvidenceBuffer.clear()清空历史。
EPHEMERAL_PYTHON_PATH
高级临时运行控制器的保留路径;应用 source/start 使用自己的唯一临时路径。
"/.espvision_agent_run.py"
EphemeralPythonController
new EphemeralPythonController(session, { evidence = null, generation = 'session' } = {})
每个控制器管理一个有时限的临时 Python 运行,使用 EPHEMERAL_PYTHON_PATH;session 为 DeviceSession。快照含 runId/path/status/output/nextOffset/finished/cleaned,可附 error/outputTruncated/cleanupError/residualPath;启动成功不代表清理完成。
EphemeralPythonController.start(code, { maxDurationMs = 180000, signal = null } = {})启动有时限的临时源码运行,返回快照,完成后清理。
EphemeralPythonController.observe(runId, { waitMs = 1000, outputOffset = 0, signal = null } = {})最多等待 waitMs,从 outputOffset 返回增量输出。
EphemeralPythonController.stop(runId)停止指定运行,返回清理/状态快照。
EphemeralPythonController.close()控制器关闭时停止/清理当前运行。
bindSessionDeviceActions
bindSessionDeviceActions(registry, { evidence = new DeviceEvidenceBuffer(), lease = null, getOperations = () => null, getGeneration = () => 'session', } = {})
向注册表绑定通用会话/文件/Python/抓图 handler,返回 { evidence, controllerFor }。此函数不提供宿主专用的 listPorts/connect/disconnect/workspace 部署实现,须由宿主绑定。
@esp-vision/device-core/firmware
FirmwareFlashSession
new FirmwareFlashSession(loader, image, { reset = () => loader.hardReset(), mode = 'update' } = {})
ROM 烧写器适配,与 EV-MUX 运行态会话分开,每个实例只使用一次。state 为 idle → connecting → connected → flashing → verified → closed,失败可经 error → closed;flash 最终会断开,单凭 closed 不能判定校验成功。
FirmwareFlashSession.connect(bytes)写入前校验镜像、芯片/版本/安全模式和现有分区表哈希。
FirmwareFlashSession.flash(bytes, { onProgress = () => {}, onPhase = () => {} } = {})写应用分区,完整读回并核验 SHA-256,然后复位;最终断开。onProgress 为 0..100,onPhase 接收状态文字。
FirmwareFlashSession.disconnect()关闭 loader 一次,重复调用不重复关闭。
镜像描述包含 evMuxVersion=4、bytes、offset、partitionSize、sha256、chipFamily、chipRevision、partitionTable:{offset,bytes,sha256}。须提供可信构建元数据与精确 Uint8Array;默认模式不执行整片擦除或分区迁移。注入的 loader 提供 initialize()、chipFamily/chipRevision、getSecurityInfo()、runStub(true)、hardReset()、disconnect();stub 提供 flashData()、readFlash()、readRegister()、writeRegister()。内置 P4 正常启动锁存处理。读回校验通过后的复位失败由 onPhase 提示,不会撤销校验或自动重写。
mode: 'install' 使用完整合并镜像描述:原应用元数据放在 application 中,bytes/offset=0/sha256/flashSize/bootloader 描述完整镜像,partitionTable 保持原结构;芯片修订范围须同时覆盖 bootloader 与应用。安装跳过旧分区表匹配,要求返回的 stub 提供 flashId(),返回整数 JEDEC ID,位 16–23 为容量指数。现有应用更新适配器无需修改;内置 Node 适配器及官网 Web Serial loader 提供该方法。完整安装的数据覆盖语义见 应用 API 完整参考。
firmwareMode(mode = 'update') 校验并返回 update/install,其他值抛出 TypeError。validateFirmwareImage(image, bytes, { mode = 'update' } = {}) 在 install 模式下还会校验嵌入的 bootloader、分区表和应用范围及哈希。
firmwareSha256
firmwareSha256(bytes)
Promise<string>,返回小写 SHA-256 十六进制摘要,要求 Web Crypto。
validateFirmwareImage
validateFirmwareImage(image, bytes, { mode = 'update' } = {})
Promise<Uint8Array>,校验 v4、精确字节数、应用分区边界和 SHA-256,返回输入字节;不推断或下载可信构建清单。
@esp-vision/device-core/node
tcpTransport({ host, port })返回被动构造的 Node TCP 字节适配器。由 Core 在
device.connect({ role: 'tcp' })时打开。serialTransport({ serial, python })返回共用的 Node/Python 串口字节适配器。指定设备名称/路径及已安装 pyserial 的解释器(例如
python3);由 Core 打开和关闭,按实际接口选择usj、usb或uart角色。适配器只传字节,不维护另一套设备会话。此 Node 专用导出从 0.1.1 起可用。openNodeRom({ port, python?, signal? })返回内置 ROM 适配器,创建时不打开端口。用于
device.firmware.update({ openLoader: ({ signal }) => openNodeRom({ port, signal }), ... }),安装与应用更新共用。需要独立安装 Python esptool>=5.3,<6;Unix 默认 python3,Windows 默认 python。Core 负责初始化、校验、写入/回读、复位与释放。SDK 0.3.0 新增,仅用于 Node;区别于运行态 serialTransport,也不是 TCP OTA。
@esp-vision/device-core/discovery
ESP_VISION_DISCOVERY_SERVICE
Node provider 查询的 DNS-SD 服务名。
"_esp-vision._tcp.local"
NetworkDiscovery
new NetworkDiscovery(provider)
包装含 list({timeout, signal}) 的 provider,校验结果并按主机名排序;list 与 device.discover 使用相同限制及结果结构,构造时不扫描。
NetworkDiscovery.list({ timeout = 3000, signal } = {})在发现时间窗口内收集规范记录,支持 AbortSignal。
createBridgeDiscovery
createBridgeDiscovery(connectionUrl, { fetch: fetcher = globalThis.fetch } = {})
返回基于桥接 GET /devices?timeout=… 的 NetworkDiscovery;接收 ws/wss connectionUrl,可注入 fetch。结果中的 connectionUrl 须同源且为 /device/<32-hex> 路径。
normalizeDiscoveredDevice
normalizeDiscoveredDevice(value)
返回规范记录或抛错:12 位小写 MAC 十六进制、DNS 主机名不超过 63 字符、board 不超过 96、firmwareVersion 不超过 255、整数 evMuxVersion、端口 1..65535、至少一个合法 IPv4;地址去重,compatible 表示 v4。
@esp-vision/device-core/discovery/node
createNodeDiscovery
createNodeDiscovery({ createMdns, interfaceAddress } = {})
返回使用 _esp-vision._tcp.local 的 PTR/SRV/TXT/A 记录的 NetworkDiscovery。createMdns 可注入兼容 multicast-dns 的工厂,interfaceAddress 选择网卡 IPv4;否则动态导入可选依赖 multicast-dns。仅接受私网/链路本地 IPv4 候选,遵守 TTL,每轮扫描后关闭 socket。
使用边界
子路径导出是实际实现或宿主契约,不是所有应用请求的别名。同一物理设备保持一个会话协调入口;设备所有权、宿主 actor 租约、ROM 烧写生命周期和发现扫描含义不同。普通设备交互使用应用对象,仅在实现对应宿主层时选用子路径。
FirmwareFlashSession.flash 返回值
回读校验后,flash 返回 { verified: true, needsReset: boolean }。复位失败时 needsReset 为 true;仅凭 closed 状态不能证明校验成功。应用通常使用 device.firmware.update() 完成发布目录校验与端口交接。
@esp-vision/device-core/browser
openBrowserRom({ port, logger = {}, signal? })此 API 尚未发布,npm 0.3.0 不含此入口。为用户选择的 Web Serial
SerialPort返回被动 ROM 适配器;应在用户手势内申请端口,先释放已有运行时连接。initialize()延迟加载锁定的 tasmota 9.4.2,打开端口、识别芯片并提供chipName/chipFamily/chipRevision,不启动 stub。适配器实现initialize/getSecurityInfo/runStub/hardReset/disconnect供firmware.update使用;Core 校验镜像与 ROM 安全模式后才启动 stub,统一负责写入、完整回读和复位。logger可提供log/error/debug;signal取消初始化,不中断已开始的写入与回读。disconnect()幂等释放端口,初始化失败也清理资源;关闭后不可复用。须使用浏览器打包器,不支持直接加载上游原生 ESM。完整示例见 Device Core npm 包对接。此入口不加载 Node 模块,根与 Node 入口也不加载浏览器后端。
@esp-vision/device-core/firmware 发布读取器(0.4.0)
loadOfficialFirmware({ board?, version?, fetch = globalThis.fetch, signal? })通过 publication 引用加载官方当前或精确指定版本的快照,返回 manifest 及 publication 历史;可选板型筛选前校验整份快照。无 publication 的配置保留旧元数据兼容。
loadReleaseManifest({ url, sha256, version?, sourceCommit?, boards?, fetch = globalThis.fetch, signal? })只加载一份不可变官方 manifest,校验原始字节 SHA-256、身份、完整板集、所有固件描述及 website/model/MCP 来源边界,返回 manifest 对象,不下载固件也不打开 ROM。字段、限制与单 BIN 升级行为见 应用 API 完整参考。