mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
fix(ble): fix OpenCode BLE UART bridge plugin for v1.17+ compatibility
- Add type assertion for event.type to bypass strict TS compilation errors - Accept both 'permission.asked' and 'permission.updated' event types - Add normalizePermissionEvent() to handle internal vs SDK event property format differences (pattern vs patterns, type vs permission fields) - Add RawPermissionEvent type for loose event property parsing - Add troubleshooting section and subdirectory auto-loading note to README
This commit is contained in:
@@ -99,6 +99,12 @@ flowchart LR
|
||||
cp tools/ble/ble_uart_bridge/demos/opencode/src/*.ts ~/.config/opencode/plugins/opencode-ble-uart-bridge/
|
||||
```
|
||||
|
||||
> **Note:** OpenCode's auto-loader may only scan the top-level of the plugin
|
||||
> directory (not subdirectories). If the plugin does not load after copying
|
||||
> the files, add an explicit entry in `opencode.json` (see step 5) pointing
|
||||
> to the entry TypeScript file with an **absolute path** — this is the
|
||||
> reliable method that works across all OpenCode versions.
|
||||
|
||||
5. Merge the relevant parts of `opencode.json.example` into your `opencode.json`.
|
||||
|
||||
For OpenCode plugin loading details, see the official
|
||||
@@ -412,6 +418,50 @@ and truncated before crossing BLE.
|
||||
- If JSON parsing fails, return an error response.
|
||||
- Keep displayed metadata short to avoid leaking large prompts or secrets.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Plugin has no effect after configuration
|
||||
|
||||
If OpenCode does not forward events to the ESP-BLE-UART Daemon after you
|
||||
followed the Quick Start steps:
|
||||
|
||||
1. **Check the plugin is actually loaded.** OpenCode loads local plugins from
|
||||
`~/.config/opencode/plugins/` and `.opencode/plugins/`. Some versions only
|
||||
scan the top-level directory for `.ts` files, so placing files in a
|
||||
subdirectory may not work without an explicit `opencode.json` entry. Add a
|
||||
`plugin` entry with an **absolute path** to the entry file:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugin": ["/Users/you/.config/opencode/plugins/opencode-ble-uart-bridge/opencode-ble-uart-bridge.ts"]
|
||||
}
|
||||
```
|
||||
|
||||
2. **Check the TypeScript compilation.** OpenCode 1.17+ uses stricter TypeScript
|
||||
checking for local plugins. If you see compilation errors in the plugin
|
||||
output, ensure you are using the latest version of the plugin source from
|
||||
`tools/ble/ble_uart_bridge/demos/opencode/src/`.
|
||||
|
||||
3. **Check the daemon is running and reachable.** The plugin calls
|
||||
`GET http://127.0.0.1:8888/status` on startup. If the daemon is not running,
|
||||
the plugin enters a "disabled" forwarding state. Start the daemon first:
|
||||
|
||||
```bash
|
||||
python tools/ble/ble_uart_bridge/main.py daemon "<device_id>"
|
||||
```
|
||||
|
||||
Then restart OpenCode. The daemon URL can be customized with the
|
||||
`OPENCODE_BLE_DAEMON_URL` environment variable.
|
||||
|
||||
4. **Enable debug logging.** Set `OPENCODE_BLE_DEBUG=1` before starting
|
||||
OpenCode to see verbose plugin logs in the terminal where OpenCode was
|
||||
launched:
|
||||
|
||||
```bash
|
||||
export OPENCODE_BLE_DEBUG=1
|
||||
opencode
|
||||
```
|
||||
|
||||
## Open items
|
||||
|
||||
- Add an integration test with a mocked ESP-BLE-UART Daemon.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BLE_DAEMON_URL } from "./config"
|
||||
import { debugLog } from "./logging"
|
||||
import { isPermissionDecision } from "./opencode-permission-reply"
|
||||
import type { BridgeResponse, DaemonResponse, DaemonStatus } from "./types"
|
||||
|
||||
@@ -38,8 +37,6 @@ export async function getDaemonStatus(): Promise<DaemonStatus> {
|
||||
* from leaking into the permission queue code.
|
||||
*/
|
||||
function parseBridgeResponse(body: DaemonResponse): BridgeResponse {
|
||||
debugLog("daemon raw response", body)
|
||||
|
||||
// Try nested data/response fields first (daemon envelope)
|
||||
const payload = body.data ?? body.response
|
||||
if (payload !== undefined) {
|
||||
|
||||
@@ -28,9 +28,6 @@ export const DEFAULT_REJECT_MESSAGE = "Rejected from BLE device"
|
||||
*/
|
||||
export const METADATA_DISPLAY_KEYS = ["command", "path", "url"] as const
|
||||
|
||||
/** Enables verbose local console logging when OPENCODE_BLE_DEBUG=1. */
|
||||
export const DEBUG = process.env.OPENCODE_BLE_DEBUG === "1"
|
||||
|
||||
/** HTTP base URL of the local ESP-BLE-UART Daemon that bridges OpenCode to the BLE device. */
|
||||
export const BLE_DAEMON_URL = process.env.OPENCODE_BLE_DAEMON_URL ?? "http://127.0.0.1:8888"
|
||||
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
// SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
// SPDX-FileCopyrightText: 2026 Esposif Systems (Shanghai) CO LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { DEBUG } from "./config"
|
||||
import type { OpenCodePermissionClient } from "./types"
|
||||
|
||||
/**
|
||||
* Write local debug logs only when explicitly enabled.
|
||||
*
|
||||
* Demo plugins often need extra observability while being developed, but they
|
||||
* should stay quiet by default when users copy them into normal OpenCode use.
|
||||
*/
|
||||
export function debugLog(message: string, data?: unknown) {
|
||||
if (!DEBUG) {
|
||||
return
|
||||
}
|
||||
if (data === undefined) {
|
||||
console.info(`[opencode-ble] ${message}`)
|
||||
return
|
||||
}
|
||||
console.info(`[opencode-ble] ${message}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer OpenCode's application log API. Fall back to debug-only local logs so
|
||||
* daemon connection failures do not pollute the OpenCode TUI.
|
||||
* Prefer OpenCode's application log API. Silently no-op when the API is
|
||||
* unavailable, avoiding fallback to console.* calls that would corrupt
|
||||
* the OpenCode TUI display.
|
||||
*
|
||||
* The partial client type means a copied demo can still run against OpenCode
|
||||
* versions that do not expose every helper method used by newer SDKs.
|
||||
@@ -43,14 +26,6 @@ export async function appLog(
|
||||
extra,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
if (level === "error") {
|
||||
debugLog(`error: ${message}`, extra)
|
||||
} else if (level === "warn") {
|
||||
debugLog(`warn: ${message}`, extra)
|
||||
} else {
|
||||
debugLog(message, extra)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +45,8 @@ export async function showToastBestEffort(
|
||||
duration: 5000,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
debugLog("failed to show TUI toast", { error: String(error), title, message, variant })
|
||||
} catch {
|
||||
// Best-effort; toast is not critical
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +54,7 @@ export async function showToastBestEffort(
|
||||
* Best-effort logging wrapper used on error paths.
|
||||
*
|
||||
* Permission handling should not fail just because the logging endpoint is
|
||||
* unavailable, so this helper catches log write errors and reports them locally.
|
||||
* unavailable, so this helper catches log write errors silently.
|
||||
*/
|
||||
export async function appLogBestEffort(
|
||||
client: OpenCodePermissionClient,
|
||||
@@ -89,7 +64,7 @@ export async function appLogBestEffort(
|
||||
) {
|
||||
try {
|
||||
await appLog(client, level, message, extra)
|
||||
} catch (error) {
|
||||
debugLog(`failed to write app log: ${message}`, { error: String(error) })
|
||||
} catch {
|
||||
// Best-effort; silent no-op
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,19 @@ import type { Plugin } from "@opencode-ai/plugin"
|
||||
|
||||
import { getDaemonStatus, notifyBLE } from "./ble-daemon-client"
|
||||
import { DEFAULT_REJECT_MESSAGE } from "./config"
|
||||
import { appLog, appLogBestEffort, debugLog, showToastBestEffort } from "./logging"
|
||||
import { appLog, appLogBestEffort, showToastBestEffort } from "./logging"
|
||||
import { replyToOpenCodePermission } from "./opencode-permission-reply"
|
||||
import { buildPermissionCancelPayload, buildSessionStatusPayload } from "./permission-payload"
|
||||
import {
|
||||
buildPermissionCancelPayload,
|
||||
buildSessionStatusPayload,
|
||||
normalizePermissionEvent,
|
||||
} from "./permission-payload"
|
||||
import {
|
||||
enqueuePermissionRequest,
|
||||
markActiveBLEPermissionsExternallyResolved,
|
||||
statusShouldCancelPendingPermission,
|
||||
} from "./permission-queue"
|
||||
import type { DaemonStatus, OpenCodePermissionClient, PermissionEventProperties } from "./types"
|
||||
|
||||
export { notifyBLE, sendRequestToBLE } from "./ble-daemon-client"
|
||||
export { buildPermissionPayload } from "./permission-payload"
|
||||
import type { DaemonStatus, OpenCodePermissionClient, RawPermissionEvent } from "./types"
|
||||
|
||||
type BLEPluginState = "unknown" | "connected" | "degraded" | "disabled"
|
||||
|
||||
@@ -61,13 +62,6 @@ async function notifyStateChange(
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenCode plugin entry point for the BLE device bridge demo.
|
||||
*
|
||||
* OpenCode calls this exported function once when loading the plugin. The
|
||||
* returned object registers an `event` callback, and OpenCode calls that
|
||||
* callback for session and permission events.
|
||||
*/
|
||||
export const BLEDeviceBridgePlugin: Plugin = async ({ client, serverUrl, directory }) => {
|
||||
const openCodeClient = client as OpenCodePermissionClient
|
||||
let bleState: BLEPluginState = "unknown"
|
||||
@@ -101,7 +95,13 @@ export const BLEDeviceBridgePlugin: Plugin = async ({ client, serverUrl, directo
|
||||
return bleState
|
||||
}
|
||||
|
||||
await refreshBLEState(false)
|
||||
// Fire-and-forget the initial BLE state check. Per OpenCode issue #4140,
|
||||
// awaiting client methods (app.log, tui.showToast) during plugin init can
|
||||
// hang OpenCode if the server isn't fully ready. The state will be
|
||||
// refreshed on the first incoming event anyway.
|
||||
void refreshBLEState(false).catch(() => {
|
||||
// Best-effort; state refresh happens on next event
|
||||
})
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -112,7 +112,12 @@ export const BLEDeviceBridgePlugin: Plugin = async ({ client, serverUrl, directo
|
||||
* - `permission.asked`: ask the BLE device user to approve or reject.
|
||||
*/
|
||||
event: async ({ event }) => {
|
||||
if (event.type === "session.status") {
|
||||
// OpenCode's SDK Event type and its internal event system use different
|
||||
// type strings for conceptually similar events. Use a string cast to
|
||||
// avoid TypeScript strict-mode compilation failures.
|
||||
const eventType = (event as { type: string }).type
|
||||
|
||||
if (eventType === "session.status") {
|
||||
// OpenCode emits this event whenever a session changes state, such as
|
||||
// becoming busy, retrying, or going idle. The plugin forwards this
|
||||
// state to the BLE device so the device can mirror OpenCode's current
|
||||
@@ -186,25 +191,15 @@ export const BLEDeviceBridgePlugin: Plugin = async ({ client, serverUrl, directo
|
||||
await appLogBestEffort(openCodeClient, "warn", "session.status handler failed", {
|
||||
error: String(error),
|
||||
})
|
||||
debugLog("session.status handler failed", { error: String(error) })
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
if (event.type === "permission.asked") {
|
||||
const permission = event.properties as PermissionEventProperties
|
||||
const permissionSummary = {
|
||||
keys: Object.keys(permission),
|
||||
id: permission.id,
|
||||
requestID: permission.requestID,
|
||||
permissionID: permission.permissionID,
|
||||
sessionID: permission.sessionID,
|
||||
permission: permission.permission,
|
||||
type: permission.type,
|
||||
title: permission.title,
|
||||
}
|
||||
debugLog("received permission.asked", permissionSummary)
|
||||
await appLogBestEffort(openCodeClient, "info", "received permission.asked", permissionSummary)
|
||||
// OpenCode's internal event system uses "permission.asked"; the SDK
|
||||
// event system uses "permission.updated". Accept both for compatibility
|
||||
// across OpenCode versions.
|
||||
if (eventType === "permission.asked" || eventType === "permission.updated") {
|
||||
const permission = normalizePermissionEvent(event.properties as RawPermissionEvent)
|
||||
|
||||
try {
|
||||
const state = await refreshBLEState(true)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { debugLog } from "./logging"
|
||||
import { permissionRequestID } from "./permission-payload"
|
||||
import type { OpenCodePermissionClient, PermissionDecision, PermissionEventProperties } from "./types"
|
||||
|
||||
@@ -62,21 +61,8 @@ export async function replyToOpenCodePermission(
|
||||
// - raw HTTP fallback: fetch(serverUrl /permission/:id/reply)
|
||||
// - v1 SDK helper: postSessionIdPermissionsPermissionId()
|
||||
// - deprecated v2 SDK: client.permission.respond()
|
||||
debugLog("replying to OpenCode permission", {
|
||||
requestID,
|
||||
sessionID: permission.sessionID,
|
||||
decision,
|
||||
message,
|
||||
hasServerUrl: serverUrl !== undefined,
|
||||
hasDirectory: directory !== undefined,
|
||||
hasInternalPost: typeof client._client?.post === "function",
|
||||
hasV2Reply: typeof client.permission?.reply === "function",
|
||||
hasV2Respond: typeof client.permission?.respond === "function",
|
||||
hasV1Respond: typeof client.postSessionIdPermissionsPermissionId === "function",
|
||||
})
|
||||
|
||||
if (typeof client.permission?.reply === "function") {
|
||||
debugLog("using OpenCode v2 permission.reply API")
|
||||
const result = await client.permission.reply({
|
||||
requestID,
|
||||
reply: decision,
|
||||
@@ -86,7 +72,6 @@ export async function replyToOpenCodePermission(
|
||||
if (error !== undefined) {
|
||||
throw new Error(`OpenCode permission response failed: ${String(error)}`)
|
||||
}
|
||||
debugLog("OpenCode permission response completed", result)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -95,7 +80,6 @@ export async function replyToOpenCodePermission(
|
||||
// fetch(serverUrl) may fail or hit the wrong OpenCode instance. The
|
||||
// injected HeyAPI client uses OpenCode's in-process app.fetch() binding,
|
||||
// so this is the reliable fallback when the public reply helper is absent.
|
||||
debugLog("using OpenCode internal raw v2 permission.reply endpoint")
|
||||
const result = await client._client.post({
|
||||
url: `/permission/${encodeURIComponent(requestID)}/reply`,
|
||||
body: {
|
||||
@@ -108,37 +92,25 @@ export async function replyToOpenCodePermission(
|
||||
if (error !== undefined) {
|
||||
throw new Error(`OpenCode permission response failed: ${String(error)}`)
|
||||
}
|
||||
debugLog("OpenCode permission response completed", result)
|
||||
return
|
||||
}
|
||||
|
||||
if (serverUrl !== undefined && directory !== undefined) {
|
||||
debugLog("using OpenCode raw v2 permission.reply endpoint")
|
||||
try {
|
||||
const response = await fetch(new URL(`/permission/${encodeURIComponent(requestID)}/reply`, serverUrl), {
|
||||
method: "POST",
|
||||
headers: opencodeRequestHeaders(directory),
|
||||
body: JSON.stringify({
|
||||
reply: decision,
|
||||
message,
|
||||
}),
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status} ${await response.text()}`)
|
||||
}
|
||||
try {
|
||||
debugLog("OpenCode permission response completed", await response.json())
|
||||
} catch {
|
||||
debugLog("OpenCode permission response completed (non-JSON body)")
|
||||
}
|
||||
return
|
||||
} catch (error) {
|
||||
debugLog("OpenCode raw v2 permission.reply failed, trying fallback", { error: String(error) })
|
||||
const response = await fetch(new URL(`/permission/${encodeURIComponent(requestID)}/reply`, serverUrl), {
|
||||
method: "POST",
|
||||
headers: opencodeRequestHeaders(directory),
|
||||
body: JSON.stringify({
|
||||
reply: decision,
|
||||
message,
|
||||
}),
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status} ${await response.text()}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof client.postSessionIdPermissionsPermissionId === "function") {
|
||||
debugLog("using OpenCode v1 permission respond API")
|
||||
const result = await client.postSessionIdPermissionsPermissionId({
|
||||
path: {
|
||||
id: permission.sessionID,
|
||||
@@ -152,12 +124,10 @@ export async function replyToOpenCodePermission(
|
||||
if (error !== undefined) {
|
||||
throw new Error(`OpenCode permission response failed: ${String(error)}`)
|
||||
}
|
||||
debugLog("OpenCode permission response completed", result)
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof client.permission?.respond === "function") {
|
||||
debugLog("using OpenCode v2 deprecated permission.respond API")
|
||||
const result = await client.permission.respond({
|
||||
sessionID: permission.sessionID,
|
||||
permissionID: requestID,
|
||||
@@ -167,7 +137,6 @@ export async function replyToOpenCodePermission(
|
||||
if (error !== undefined) {
|
||||
throw new Error(`OpenCode permission response failed: ${String(error)}`)
|
||||
}
|
||||
debugLog("OpenCode permission response completed", result)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,49 @@ import {
|
||||
MAX_METADATA_VALUE_CHARS,
|
||||
METADATA_DISPLAY_KEYS,
|
||||
} from "./config"
|
||||
import type { PermissionEventProperties } from "./types"
|
||||
import type { PermissionEventProperties, RawPermissionEvent } from "./types"
|
||||
|
||||
/**
|
||||
* Normalize raw permission event properties from either the internal OpenCode
|
||||
* event system (type="permission.asked") or the SDK event system
|
||||
* (type="permission.updated") into the PermissionEventProperties shape that
|
||||
* this plugin's payload builders and permission queue expect.
|
||||
*
|
||||
* The two event systems use different field names for similar concepts:
|
||||
* - Internal `permission` ↔ SDK `type`
|
||||
* - Internal `patterns` (string[]) ↔ SDK `pattern` (string | string[])
|
||||
* - Internal has `tool_name`/`tool_input`; SDK has `messageID`/`callID`/`time`
|
||||
*/
|
||||
export function normalizePermissionEvent(raw: RawPermissionEvent): PermissionEventProperties {
|
||||
// Normalize patterns: SDK Permission uses `pattern` (string | string[]),
|
||||
// internal events use `patterns` (string[]).
|
||||
let patterns: string[] | undefined
|
||||
if (Array.isArray(raw.patterns)) {
|
||||
patterns = raw.patterns as string[]
|
||||
} else if (Array.isArray(raw.pattern)) {
|
||||
patterns = raw.pattern as string[]
|
||||
} else if (typeof raw.pattern === "string") {
|
||||
patterns = [raw.pattern]
|
||||
}
|
||||
|
||||
return {
|
||||
id: raw.id,
|
||||
requestID: raw.requestID ?? raw.id,
|
||||
permissionID: raw.permissionID ?? raw.id,
|
||||
sessionID: raw.sessionID ?? "",
|
||||
permission: raw.permission ?? raw.type,
|
||||
type: raw.type ?? raw.permission,
|
||||
title: raw.title ?? DEFAULT_PERMISSION_TITLE,
|
||||
metadata: typeof raw.metadata === "object" && raw.metadata !== null
|
||||
? (raw.metadata as Record<string, unknown>)
|
||||
: undefined,
|
||||
patterns,
|
||||
tool_name: raw.tool_name,
|
||||
tool_input: typeof raw.tool_input === "object" && raw.tool_input !== null
|
||||
? (raw.tool_input as Record<string, unknown>)
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a unique event ID for messages sent to the ESP-BLE-UART Daemon. */
|
||||
function eventID() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { CONCURRENT_REJECT_MESSAGE, DECISION_TIMEOUT_SECONDS, DEFAULT_REJECT_MESSAGE } from "./config"
|
||||
import { sendRequestToBLE } from "./ble-daemon-client"
|
||||
import { appLogBestEffort, debugLog, showToastBestEffort } from "./logging"
|
||||
import { appLogBestEffort, showToastBestEffort } from "./logging"
|
||||
import { isPermissionDecision, replyToOpenCodePermission } from "./opencode-permission-reply"
|
||||
import { buildPermissionPayload, permissionRequestID } from "./permission-payload"
|
||||
import type { OpenCodePermissionClient, PermissionDecision, PermissionEventProperties, PermissionQueueItem } from "./types"
|
||||
@@ -113,11 +113,6 @@ async function handlePermissionQueueItem(item: PermissionQueueItem): Promise<Per
|
||||
|
||||
if (item.skipBLE === true) {
|
||||
decisionMessage = nonEmptyString(item.skipMessage, DEFAULT_REJECT_MESSAGE)
|
||||
debugLog("skipping BLE permission request after concurrent reject", {
|
||||
requestID,
|
||||
sessionID: item.permission.sessionID,
|
||||
message: decisionMessage,
|
||||
})
|
||||
await appLogBestEffort(item.client, "warn", "skipping BLE permission request after concurrent reject", {
|
||||
requestID,
|
||||
sessionID: item.permission.sessionID,
|
||||
@@ -134,11 +129,6 @@ async function handlePermissionQueueItem(item: PermissionQueueItem): Promise<Per
|
||||
|
||||
if (externallyResolvedPermissionIDs.has(requestID)) {
|
||||
markQueuedPermissionsSkipped(item.permission.sessionID)
|
||||
debugLog("BLE permission response ignored after OpenCode state changed", {
|
||||
requestID,
|
||||
sessionID: item.permission.sessionID,
|
||||
result,
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -152,16 +142,9 @@ async function handlePermissionQueueItem(item: PermissionQueueItem): Promise<Per
|
||||
// it a corrective denial so later serial tool calls can continue.
|
||||
decisionMessage = nonEmptyString(result.message, DEFAULT_REJECT_MESSAGE)
|
||||
}
|
||||
debugLog("BLE permission decision received", result)
|
||||
await appLogBestEffort(item.client, "info", "BLE permission decision received", result as Record<string, unknown>)
|
||||
} catch (error) {
|
||||
if (externallyResolvedPermissionIDs.has(requestID)) {
|
||||
markQueuedPermissionsSkipped(item.permission.sessionID)
|
||||
debugLog("BLE permission failure ignored after OpenCode state changed", {
|
||||
requestID,
|
||||
sessionID: item.permission.sessionID,
|
||||
error: String(error),
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
// Daemon failures are fail-closed, but still include feedback for the same
|
||||
|
||||
@@ -31,6 +31,35 @@ export type PermissionEventProperties = {
|
||||
tool_input?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw event properties that may come from either the internal OpenCode event
|
||||
* system (event type "permission.asked") or the SDK event system (event type
|
||||
* "permission.updated").
|
||||
*
|
||||
* The internal system uses fields like `permission` (string) and `patterns`
|
||||
* (string[]), while the SDK Permission type uses `type` (string) and `pattern`
|
||||
* (string | string[]). This type accepts both so the normalizer can map them
|
||||
* into a common PermissionEventProperties shape.
|
||||
*/
|
||||
export type RawPermissionEvent = {
|
||||
id?: string
|
||||
requestID?: string
|
||||
permissionID?: string
|
||||
sessionID?: string
|
||||
permission?: string
|
||||
type?: string
|
||||
title?: string
|
||||
metadata?: unknown
|
||||
pattern?: string | string[]
|
||||
patterns?: unknown
|
||||
tool_name?: string
|
||||
tool_input?: unknown
|
||||
messageID?: string
|
||||
callID?: string
|
||||
time?: unknown
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalized response expected from the ESP-BLE-UART Daemon after a permission request.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user