feat: operator command channel via Nostr (manual dispense)
Add a Nostr-native operator command channel using Kind 21003 (CLINK Manage) events. Operators listed in OPERATOR_PUBKEYS can send encrypted commands to the machine. Phase 1 implements manual dispense: operator sends a dispense command, machine verifies sender, checks it's idle, performs a direct HAL dispense (bypassing state machine), and records the transaction. When ref_txid is provided, the referenced failed transaction is updated to status 'remediated', closing the loop on dispense errors. Changes: - CLINK types: add 'machine' resource, MachineDispenseRequest type - CLINK client: support operator pubkey list (string | string[]) - Runtime config: VITE_OPERATOR_PUBKEYS env var - Schema v4→v5: manual_dispense type, remediated_by column - Lightning services: wire onManagement callback - ATM store: handleManagementCommand with idle check + remediation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
88a52cf769
commit
e03782803b
11 changed files with 274 additions and 24 deletions
|
|
@ -25,6 +25,7 @@ export interface RuntimeConfig {
|
|||
dispenserDevice?: string
|
||||
cassettes?: string
|
||||
allowMockFallback: boolean
|
||||
operatorPubkeys: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,8 +55,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||
getCashbox: () => ipcRenderer.invoke('state:get-cashbox'),
|
||||
recordTransaction: (tx: {
|
||||
txid: string
|
||||
type: 'cash_in' | 'cash_out'
|
||||
status: 'complete' | 'dispense_error' | 'partial'
|
||||
type: 'cash_in' | 'cash_out' | 'manual_dispense'
|
||||
status: 'complete' | 'dispense_error' | 'partial' | 'remediated'
|
||||
fiatCents: number
|
||||
sats: number
|
||||
feeSats: number
|
||||
|
|
@ -74,6 +75,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||
error?: string | null
|
||||
}) => ipcRenderer.invoke('state:record-transaction', tx),
|
||||
emptyCashbox: () => ipcRenderer.invoke('state:empty-cashbox'),
|
||||
remediateTransaction: (txid: string, remediatedByTxid: string): Promise<boolean> =>
|
||||
ipcRenderer.invoke('state:remediate-transaction', txid, remediatedByTxid),
|
||||
|
||||
// HAL hardware (runs in main process, exposed via IPC)
|
||||
halInit: (config: any): Promise<{ success: boolean; error?: string }> =>
|
||||
|
|
@ -147,6 +150,7 @@ declare global {
|
|||
error?: string | null
|
||||
}) => Promise<void>
|
||||
emptyCashbox: () => Promise<void>
|
||||
remediateTransaction: (txid: string, remediatedByTxid: string) => Promise<boolean>
|
||||
// HAL hardware IPC
|
||||
halInit: (config: any) => Promise<{ success: boolean; error?: string }>
|
||||
halDispense: (amounts: any) => Promise<any>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue