@agentdeck/bridge 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/fm-helper/agentdeck-fm-helper +0 -0
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +5 -2
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/runner.d.ts +11 -0
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +60 -4
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/settings.d.ts +10 -0
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +14 -0
- package/dist/apme/settings.js.map +1 -1
- package/dist/awaiting-overlay.d.ts +44 -10
- package/dist/awaiting-overlay.d.ts.map +1 -1
- package/dist/awaiting-overlay.js +96 -28
- package/dist/awaiting-overlay.js.map +1 -1
- package/dist/bridge-core.d.ts +10 -0
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +47 -12
- package/dist/bridge-core.js.map +1 -1
- package/dist/card-feed.d.ts +107 -0
- package/dist/card-feed.d.ts.map +1 -0
- package/dist/card-feed.js +277 -0
- package/dist/card-feed.js.map +1 -0
- package/dist/card-modules.d.ts +87 -0
- package/dist/card-modules.d.ts.map +1 -0
- package/dist/card-modules.js +179 -0
- package/dist/card-modules.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -0
- package/dist/cli.js.map +1 -1
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +1025 -50
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-ws-client.d.ts +2 -1
- package/dist/daemon-ws-client.d.ts.map +1 -1
- package/dist/daemon-ws-client.js +2 -1
- package/dist/daemon-ws-client.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/device-photo.d.ts +86 -0
- package/dist/device-photo.d.ts.map +1 -0
- package/dist/device-photo.js +200 -0
- package/dist/device-photo.js.map +1 -0
- package/dist/device-voice-reply.d.ts +124 -0
- package/dist/device-voice-reply.d.ts.map +1 -0
- package/dist/device-voice-reply.js +236 -0
- package/dist/device-voice-reply.js.map +1 -0
- package/dist/device-voice.d.ts +56 -0
- package/dist/device-voice.d.ts.map +1 -0
- package/dist/device-voice.js +134 -0
- package/dist/device-voice.js.map +1 -0
- package/dist/esp32-serial.d.ts +40 -0
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +109 -13
- package/dist/esp32-serial.js.map +1 -1
- package/dist/foundation-models-helper.d.ts +30 -0
- package/dist/foundation-models-helper.d.ts.map +1 -1
- package/dist/foundation-models-helper.js +45 -0
- package/dist/foundation-models-helper.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mdns.d.ts +11 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +13 -0
- package/dist/mdns.js.map +1 -1
- package/dist/observed-inject.d.ts +119 -0
- package/dist/observed-inject.d.ts.map +1 -0
- package/dist/observed-inject.js +405 -0
- package/dist/observed-inject.js.map +1 -0
- package/dist/passive-observer.d.ts +14 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +35 -5
- package/dist/passive-observer.js.map +1 -1
- package/dist/peripheral-mapping.d.ts +65 -0
- package/dist/peripheral-mapping.d.ts.map +1 -0
- package/dist/peripheral-mapping.js +115 -0
- package/dist/peripheral-mapping.js.map +1 -0
- package/dist/session-aggregator.d.ts +4 -1
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +5 -5
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-id-resolve.d.ts +17 -0
- package/dist/session-id-resolve.d.ts.map +1 -0
- package/dist/session-id-resolve.js +35 -0
- package/dist/session-id-resolve.js.map +1 -0
- package/dist/session-registry.d.ts +12 -0
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +31 -0
- package/dist/session-registry.js.map +1 -1
- package/dist/session-transcript-timeline.d.ts +7 -0
- package/dist/session-transcript-timeline.d.ts.map +1 -1
- package/dist/session-transcript-timeline.js +16 -6
- package/dist/session-transcript-timeline.js.map +1 -1
- package/dist/timeline-store.js +2 -2
- package/dist/timeline-store.js.map +1 -1
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +14 -7
- package/dist/usage-event.js.map +1 -1
- package/dist/voice.d.ts +13 -10
- package/dist/voice.d.ts.map +1 -1
- package/dist/voice.js +26 -272
- package/dist/voice.js.map +1 -1
- package/dist/ws-server.d.ts +14 -0
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +66 -1
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +303 -0
- package/package.json +3 -3
- package/dist/d200h/hid-protocol.d.ts +0 -62
- package/dist/d200h/hid-protocol.d.ts.map +0 -1
- package/dist/d200h/hid-protocol.js +0 -124
- package/dist/d200h/hid-protocol.js.map +0 -1
- package/dist/d200h/image-renderer.d.ts +0 -24
- package/dist/d200h/image-renderer.d.ts.map +0 -1
- package/dist/d200h/image-renderer.js +0 -321
- package/dist/d200h/image-renderer.js.map +0 -1
- package/dist/hook-migration.d.ts +0 -2
- package/dist/hook-migration.d.ts.map +0 -1
- package/dist/hook-migration.js +0 -104
- package/dist/hook-migration.js.map +0 -1
- package/dist/modules/d200h-module.d.ts +0 -65
- package/dist/modules/d200h-module.d.ts.map +0 -1
- package/dist/modules/d200h-module.js +0 -495
- package/dist/modules/d200h-module.js.map +0 -1
- package/dist/modules/d200h-renderer.d.ts +0 -7
- package/dist/modules/d200h-renderer.d.ts.map +0 -1
- package/dist/modules/d200h-renderer.js +0 -321
- package/dist/modules/d200h-renderer.js.map +0 -1
- package/dist/modules/trmnl-module.d.ts +0 -38
- package/dist/modules/trmnl-module.d.ts.map +0 -1
- package/dist/modules/trmnl-module.js +0 -111
- package/dist/modules/trmnl-module.js.map +0 -1
- package/dist/trmnl/byos-server.d.ts +0 -39
- package/dist/trmnl/byos-server.d.ts.map +0 -1
- package/dist/trmnl/byos-server.js +0 -301
- package/dist/trmnl/byos-server.js.map +0 -1
- package/dist/trmnl/frame-cache.d.ts +0 -37
- package/dist/trmnl/frame-cache.d.ts.map +0 -1
- package/dist/trmnl/frame-cache.js +0 -157
- package/dist/trmnl/frame-cache.js.map +0 -1
- package/dist/trmnl/image-renderer.d.ts +0 -27
- package/dist/trmnl/image-renderer.d.ts.map +0 -1
- package/dist/trmnl/image-renderer.js +0 -235
- package/dist/trmnl/image-renderer.js.map +0 -1
- package/dist/trmnl/trmnl-settings.d.ts +0 -96
- package/dist/trmnl/trmnl-settings.d.ts.map +0 -1
- package/dist/trmnl/trmnl-settings.js +0 -177
- package/dist/trmnl/trmnl-settings.js.map +0 -1
- package/dist/trmnl/trmnl-telemetry.d.ts +0 -47
- package/dist/trmnl/trmnl-telemetry.d.ts.map +0 -1
- package/dist/trmnl/trmnl-telemetry.js +0 -56
- package/dist/trmnl/trmnl-telemetry.js.map +0 -1
- package/dist/whisper-server-manager.d.ts +0 -19
- package/dist/whisper-server-manager.d.ts.map +0 -1
- package/dist/whisper-server-manager.js +0 -171
- package/dist/whisper-server-manager.js.map +0 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card Feed pull sync (M6) — the HTTP counterpart to the WS live mode for
|
|
3
|
+
* wake-sync-sleep battery clients (XTeink X3/X4). Pure builders/appliers; the
|
|
4
|
+
* HTTP routes live in daemon-server.ts. Contract: shared/src/protocol.ts
|
|
5
|
+
* § Card Feed Pull Sync + docs/esp32-client-contract.md § Pull sync.
|
|
6
|
+
*
|
|
7
|
+
* M6 derives every card from a live session (the same rows `sessions_list`
|
|
8
|
+
* broadcasts). M7 generalizes to daemon card modules (NUDGE / QUEST / …) that
|
|
9
|
+
* will produce `day`-class cards; until then no producer emits `day`.
|
|
10
|
+
*/
|
|
11
|
+
import { CARD_FEED_IDLE_PULL_SEC, CARD_FEED_ACTIVE_PULL_SEC, } from '@agentdeck/shared';
|
|
12
|
+
import { buildModuleCards, applyModuleChoice, DEFAULT_CARD_MODULES, } from './card-modules.js';
|
|
13
|
+
/** A pulled permission gate is answerable only while the PreToolUse long-poll
|
|
14
|
+
* still holds the hook open (~60s) — pulled copies are honest about that. */
|
|
15
|
+
export const PERMISSION_GATE_TTL_MS = 60_000;
|
|
16
|
+
/** Awaiting PTY prompts stay interactive until the awaiting backstop reaps
|
|
17
|
+
* them (AWAITING_STUCK_TIMEOUT_MS) — same 10 min bound. */
|
|
18
|
+
export const AWAITING_PROMPT_TTL_MS = 10 * 60 * 1000;
|
|
19
|
+
const isAwaitingState = (s) => typeof s.state === 'string' && s.state.startsWith('awaiting');
|
|
20
|
+
/** Offline-validity class for a session-derived card. Permission gates and
|
|
21
|
+
* interactive prompts are `live` (grey out offline, TTL-expire); everything
|
|
22
|
+
* else is a read-only `info` row. */
|
|
23
|
+
export function classifySessionCard(s, now) {
|
|
24
|
+
if (s.requestId)
|
|
25
|
+
return { actionClass: 'live', expiresAt: now + PERMISSION_GATE_TTL_MS };
|
|
26
|
+
if (isAwaitingState(s))
|
|
27
|
+
return { actionClass: 'live', expiresAt: now + AWAITING_PROMPT_TTL_MS };
|
|
28
|
+
return { actionClass: 'info' };
|
|
29
|
+
}
|
|
30
|
+
export function buildCardFeed(sessions, now = Date.now(), modules = DEFAULT_CARD_MODULES) {
|
|
31
|
+
const cards = sessions.map((s) => ({
|
|
32
|
+
cardId: `session:${s.id}`,
|
|
33
|
+
...classifySessionCard(s, now),
|
|
34
|
+
session: s,
|
|
35
|
+
}));
|
|
36
|
+
// Module cards (M7) come after the session projections: what is happening
|
|
37
|
+
// outranks what the daemon wants to say about it. A client that predates
|
|
38
|
+
// modules skips bodies it doesn't recognise.
|
|
39
|
+
cards.push(...buildModuleCards({ sessions, now }, modules));
|
|
40
|
+
const active = sessions.some((s) => s.state === 'processing' || isAwaitingState(s));
|
|
41
|
+
const d = new Date(now);
|
|
42
|
+
const serverHm = `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
43
|
+
return {
|
|
44
|
+
type: 'card_feed',
|
|
45
|
+
rev: 1,
|
|
46
|
+
serverTime: now,
|
|
47
|
+
serverHm,
|
|
48
|
+
nextPullSec: active ? CARD_FEED_ACTIVE_PULL_SEC : CARD_FEED_IDLE_PULL_SEC,
|
|
49
|
+
cards,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const OUTBOX_ACTIONS = new Set([
|
|
53
|
+
'permission_decision', 'select_option', 'respond', 'send_prompt', 'dismiss', 'card_choice',
|
|
54
|
+
]);
|
|
55
|
+
function sessionIdOf(d) {
|
|
56
|
+
if (typeof d.sessionId === 'string' && d.sessionId)
|
|
57
|
+
return d.sessionId;
|
|
58
|
+
if (typeof d.cardId === 'string' && d.cardId.startsWith('session:'))
|
|
59
|
+
return d.cardId.slice('session:'.length);
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
function applyOne(d, deps) {
|
|
63
|
+
const cardId = typeof d.cardId === 'string' ? d.cardId : '';
|
|
64
|
+
const fail = (status, reason) => ({ cardId, status, reason });
|
|
65
|
+
if (!cardId || typeof d.action !== 'string' || !OUTBOX_ACTIONS.has(d.action)) {
|
|
66
|
+
return fail('rejected', 'malformed decision');
|
|
67
|
+
}
|
|
68
|
+
// Device-local dismissal — acknowledged so the device can drop it; the
|
|
69
|
+
// daemon has nothing to mutate (dismissal memory lives on the device).
|
|
70
|
+
if (d.action === 'dismiss')
|
|
71
|
+
return { cardId, status: 'applied' };
|
|
72
|
+
// Module card (M7): the authoring module owns what the choice means. This is
|
|
73
|
+
// the `day` class in practice — the press may have happened hours ago and
|
|
74
|
+
// offline, so nothing here is validated against live session state.
|
|
75
|
+
if (d.action === 'card_choice') {
|
|
76
|
+
const outcome = applyModuleChoice(d, { sessions: deps.sessions, now: deps.now ?? Date.now() }, deps.modules);
|
|
77
|
+
return { cardId, status: outcome.status, ...(outcome.reason ? { reason: outcome.reason } : {}) };
|
|
78
|
+
}
|
|
79
|
+
if (d.action === 'permission_decision') {
|
|
80
|
+
if (typeof d.requestId !== 'string' || !d.requestId || (d.decision !== 'allow' && d.decision !== 'deny')) {
|
|
81
|
+
return fail('rejected', 'requestId and decision required');
|
|
82
|
+
}
|
|
83
|
+
// A gate that is no longer held must NOT be re-resolved — the terminal
|
|
84
|
+
// already answered it, or the hook long-poll gave up. Honest expiry.
|
|
85
|
+
if (!deps.isPendingRequest(d.requestId))
|
|
86
|
+
return fail('expired', 'permission gate no longer pending');
|
|
87
|
+
deps.dispatch({ type: 'permission_decision', requestId: d.requestId, decision: d.decision });
|
|
88
|
+
return { cardId, status: 'applied' };
|
|
89
|
+
}
|
|
90
|
+
const sessionId = sessionIdOf(d);
|
|
91
|
+
if (!sessionId)
|
|
92
|
+
return fail('rejected', 'sessionId required');
|
|
93
|
+
const session = deps.sessions.find((s) => s.id === sessionId);
|
|
94
|
+
if (!session)
|
|
95
|
+
return fail('unknown_card', 'session not in current roster');
|
|
96
|
+
if (d.action === 'select_option' || d.action === 'respond') {
|
|
97
|
+
// Option decisions answer a *specific* prompt. The session must still be
|
|
98
|
+
// awaiting, and when the device echoed the question it must match the
|
|
99
|
+
// session's current one — an hour-old index must never press a different,
|
|
100
|
+
// newer prompt.
|
|
101
|
+
if (!isAwaitingState(session) && !session.requestId) {
|
|
102
|
+
return fail('expired', 'session is no longer awaiting');
|
|
103
|
+
}
|
|
104
|
+
if (typeof d.question === 'string' && d.question && session.question && d.question !== session.question) {
|
|
105
|
+
return fail('expired', 'prompt changed since the decision was recorded');
|
|
106
|
+
}
|
|
107
|
+
if (d.action === 'select_option') {
|
|
108
|
+
if (typeof d.index !== 'number' || !Number.isInteger(d.index) || d.index < 0) {
|
|
109
|
+
return fail('rejected', 'index required');
|
|
110
|
+
}
|
|
111
|
+
deps.dispatch({ type: 'select_option', index: d.index, sessionId });
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
if (typeof d.value !== 'string' || !d.value)
|
|
115
|
+
return fail('rejected', 'value required');
|
|
116
|
+
deps.dispatch({ type: 'session_command', sessionId, command: { type: 'respond', value: d.value } });
|
|
117
|
+
}
|
|
118
|
+
return { cardId, status: 'applied' };
|
|
119
|
+
}
|
|
120
|
+
// send_prompt — deliverable to any alive session; observed sessions queue
|
|
121
|
+
// it as a turn-end directive via the session_command steering path.
|
|
122
|
+
if (typeof d.text !== 'string' || !d.text)
|
|
123
|
+
return fail('rejected', 'text required');
|
|
124
|
+
deps.dispatch({ type: 'session_command', sessionId, command: { type: 'send_prompt', text: d.text } });
|
|
125
|
+
return { cardId, status: 'applied' };
|
|
126
|
+
}
|
|
127
|
+
// ===== Pull observability — the only trace a sleeping client leaves =====
|
|
128
|
+
//
|
|
129
|
+
// A wake-sync-sleep client that has nothing queued sends exactly one request
|
|
130
|
+
// per wake: `GET /feed`. It carries no board id and no body, so without this
|
|
131
|
+
// the daemon cannot answer the question the power ladder is judged by — *did
|
|
132
|
+
// the timer wake actually fire, and how far did the internal clock drift?*
|
|
133
|
+
// The tracker turns each pull into that measurement by comparing the gap
|
|
134
|
+
// against the cadence the daemon itself advertised on the previous pull.
|
|
135
|
+
/** Observed cadence within this fraction of the advertised `nextPullSec`
|
|
136
|
+
* counts as honoured — the device really slept and its timer really woke it.
|
|
137
|
+
* The XTeink boards run a drifty internal timer (no usable RTC alarm), so
|
|
138
|
+
* the band is generous; anything outside it is a wake worth looking at. */
|
|
139
|
+
export const PULL_CADENCE_TOLERANCE = 0.25;
|
|
140
|
+
/** `::ffff:192.168.68.77` and `::1` are the same peers as their v4 forms. */
|
|
141
|
+
export function normalizeClientIp(ip) {
|
|
142
|
+
const s = (ip || '').trim();
|
|
143
|
+
if (s.startsWith('::ffff:'))
|
|
144
|
+
return s.slice('::ffff:'.length);
|
|
145
|
+
if (s === '::1')
|
|
146
|
+
return '127.0.0.1';
|
|
147
|
+
return s;
|
|
148
|
+
}
|
|
149
|
+
/** In-memory (daemon-lifetime) record of card-feed pulls, keyed by client IP.
|
|
150
|
+
* Deliberately not persisted: it answers "is the cadence working *now*". */
|
|
151
|
+
export class FeedPullTracker {
|
|
152
|
+
states = new Map();
|
|
153
|
+
history = [];
|
|
154
|
+
historyLimit;
|
|
155
|
+
/** Kept past the WiFi registry's 1h TTL: a client that sleeps for an hour
|
|
156
|
+
* ages out of the roster but must not lose its name here. */
|
|
157
|
+
boards = new Map();
|
|
158
|
+
intervalWindow = 16;
|
|
159
|
+
constructor(opts = {}) {
|
|
160
|
+
this.historyLimit = opts.historyLimit ?? 32;
|
|
161
|
+
}
|
|
162
|
+
/** Bind a board id to a client IP (outbox `board`, or a WiFi WS lookup). */
|
|
163
|
+
noteBoard(client, board) {
|
|
164
|
+
const ip = normalizeClientIp(client);
|
|
165
|
+
if (!ip || !board)
|
|
166
|
+
return;
|
|
167
|
+
this.boards.set(ip, board);
|
|
168
|
+
const st = this.states.get(ip);
|
|
169
|
+
if (st)
|
|
170
|
+
st.board = board;
|
|
171
|
+
}
|
|
172
|
+
record(client, info) {
|
|
173
|
+
const ip = normalizeClientIp(client);
|
|
174
|
+
const at = info.now ?? Date.now();
|
|
175
|
+
const prev = this.states.get(ip);
|
|
176
|
+
const board = this.boards.get(ip) ?? prev?.board;
|
|
177
|
+
const event = { client: ip, at, cards: info.cards, nextPullSec: info.nextPullSec };
|
|
178
|
+
if (board)
|
|
179
|
+
event.board = board;
|
|
180
|
+
if (prev) {
|
|
181
|
+
const sinceLastSec = Math.round((at - prev.lastPullAt) / 1000);
|
|
182
|
+
event.sinceLastSec = sinceLastSec;
|
|
183
|
+
const expectedSec = prev.lastNextPullSec;
|
|
184
|
+
if (expectedSec > 0) {
|
|
185
|
+
event.expectedSec = expectedSec;
|
|
186
|
+
event.driftPct = Math.round(((sinceLastSec - expectedSec) / expectedSec) * 1000) / 1000;
|
|
187
|
+
event.cadenceHonoured = Math.abs(event.driftPct) <= PULL_CADENCE_TOLERANCE;
|
|
188
|
+
}
|
|
189
|
+
prev.pulls += 1;
|
|
190
|
+
prev.lastPullAt = at;
|
|
191
|
+
prev.lastNextPullSec = info.nextPullSec;
|
|
192
|
+
prev.lastIntervalSec = sinceLastSec;
|
|
193
|
+
prev.intervals.push(sinceLastSec);
|
|
194
|
+
if (prev.intervals.length > this.intervalWindow)
|
|
195
|
+
prev.intervals.shift();
|
|
196
|
+
if (event.cadenceHonoured)
|
|
197
|
+
prev.cadenceHonouredCount += 1;
|
|
198
|
+
if (board)
|
|
199
|
+
prev.board = board;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
this.states.set(ip, {
|
|
203
|
+
board,
|
|
204
|
+
pulls: 1,
|
|
205
|
+
firstPullAt: at,
|
|
206
|
+
lastPullAt: at,
|
|
207
|
+
lastNextPullSec: info.nextPullSec,
|
|
208
|
+
intervals: [],
|
|
209
|
+
cadenceHonouredCount: 0,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
this.history.push(event);
|
|
213
|
+
if (this.history.length > this.historyLimit)
|
|
214
|
+
this.history.shift();
|
|
215
|
+
return event;
|
|
216
|
+
}
|
|
217
|
+
/** Most recent pulls, newest first. */
|
|
218
|
+
recent(limit = 8) {
|
|
219
|
+
return this.history.slice(-limit).reverse();
|
|
220
|
+
}
|
|
221
|
+
clients() {
|
|
222
|
+
const out = [];
|
|
223
|
+
for (const [client, st] of this.states) {
|
|
224
|
+
const sorted = [...st.intervals].sort((a, b) => a - b);
|
|
225
|
+
const median = sorted.length
|
|
226
|
+
? (sorted.length % 2
|
|
227
|
+
? sorted[(sorted.length - 1) / 2]
|
|
228
|
+
: Math.round((sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2))
|
|
229
|
+
: undefined;
|
|
230
|
+
out.push({
|
|
231
|
+
client,
|
|
232
|
+
...(st.board ? { board: st.board } : {}),
|
|
233
|
+
pulls: st.pulls,
|
|
234
|
+
firstPullAt: st.firstPullAt,
|
|
235
|
+
lastPullAt: st.lastPullAt,
|
|
236
|
+
lastNextPullSec: st.lastNextPullSec,
|
|
237
|
+
...(st.lastIntervalSec !== undefined ? { lastIntervalSec: st.lastIntervalSec } : {}),
|
|
238
|
+
...(median !== undefined ? { medianIntervalSec: median } : {}),
|
|
239
|
+
cadenceHonouredCount: st.cadenceHonouredCount,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return out.sort((a, b) => b.lastPullAt - a.lastPullAt);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/** One-line log form. `+3.2%` is the sleeping client's timer drift against the
|
|
246
|
+
* cadence the daemon advertised — the M6 power-ladder verification signal. */
|
|
247
|
+
export function formatFeedPull(ev) {
|
|
248
|
+
const who = ev.board ? `${ev.board} (${ev.client})` : ev.client;
|
|
249
|
+
const head = `card feed pull from ${who}: ${ev.cards} card${ev.cards === 1 ? '' : 's'}, next ${ev.nextPullSec}s`;
|
|
250
|
+
if (ev.sinceLastSec === undefined)
|
|
251
|
+
return `${head} — first pull`;
|
|
252
|
+
if (ev.expectedSec === undefined)
|
|
253
|
+
return `${head} — ${ev.sinceLastSec}s since last pull`;
|
|
254
|
+
const pct = `${ev.driftPct !== undefined && ev.driftPct >= 0 ? '+' : ''}${((ev.driftPct ?? 0) * 100).toFixed(1)}%`;
|
|
255
|
+
const verdict = ev.cadenceHonoured ? 'cadence ok' : 'off cadence';
|
|
256
|
+
return `${head} — ${ev.sinceLastSec}s since last pull (expected ${ev.expectedSec}s, ${pct}, ${verdict})`;
|
|
257
|
+
}
|
|
258
|
+
/** Apply a pushed outbox batch. Results keep request order; every decision is
|
|
259
|
+
* acknowledged (the device deletes acknowledged entries regardless of
|
|
260
|
+
* status — a rejection is terminal, not retryable). */
|
|
261
|
+
export function applyOutboxDecisions(req, deps) {
|
|
262
|
+
const decisions = Array.isArray(req?.decisions) ? req.decisions : [];
|
|
263
|
+
const results = decisions.map((d) => {
|
|
264
|
+
try {
|
|
265
|
+
return applyOne(d ?? {}, deps);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
return {
|
|
269
|
+
cardId: typeof d?.cardId === 'string' ? d.cardId : '',
|
|
270
|
+
status: 'error',
|
|
271
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return { ok: results.every((r) => r.status !== 'error'), results };
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=card-feed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card-feed.js","sourceRoot":"","sources":["../src/card-feed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card modules (M7) — the daemon's own card producers.
|
|
3
|
+
*
|
|
4
|
+
* M6 cards are all projections of a live session: the daemon reports what is
|
|
5
|
+
* happening and the device renders it. A module card inverts that — the daemon
|
|
6
|
+
* *authors* a card (a checkpoint, a digest, a nudge) and the device carries it
|
|
7
|
+
* as a prepared decision, answerable even while offline.
|
|
8
|
+
*
|
|
9
|
+
* Contract: shared/src/protocol.ts § Card modules. Two rules hold here and are
|
|
10
|
+
* enforced structurally rather than by convention, because a firmware that
|
|
11
|
+
* receives a malformed card has no good move:
|
|
12
|
+
*
|
|
13
|
+
* 1. **≤4 choices, always.** Slot 1 of the four front buttons is the device's
|
|
14
|
+
* own *Later*, so a module binds at most three. `sealModuleCard` clamps.
|
|
15
|
+
* 2. **Bytes, not characters.** Every text field is trimmed to a UTF-8 byte
|
|
16
|
+
* budget on a code-point boundary — a firmware `char[]` truncates
|
|
17
|
+
* mid-sequence into broken glyphs otherwise.
|
|
18
|
+
*
|
|
19
|
+
* Routing: a module card's `cardId` is `module:<moduleId>:<key>`, which is how
|
|
20
|
+
* an outbox `card_choice` recorded hours ago finds its way back to the module
|
|
21
|
+
* that authored it.
|
|
22
|
+
*/
|
|
23
|
+
import type { SessionInfo, FeedCard, CardChoice, CardModuleId, CardActionClass, OutboxDecision, OutboxDecisionStatus } from '@agentdeck/shared';
|
|
24
|
+
/** Text budgets, in UTF-8 bytes, matched to the device's card buffers
|
|
25
|
+
* (`question[160]`, `PromptOption.label[80]` in the fork's agent_state.h).
|
|
26
|
+
* A re-port that grows a buffer bumps these together with it. */
|
|
27
|
+
export declare const CARD_TITLE_MAX_BYTES = 24;
|
|
28
|
+
export declare const CARD_QUESTION_MAX_BYTES = 160;
|
|
29
|
+
export declare const CARD_CONTEXT_LINE_MAX_BYTES = 96;
|
|
30
|
+
export declare const CARD_CHOICE_LABEL_MAX_BYTES = 40;
|
|
31
|
+
export declare const CARD_ID_PREFIX = "module:";
|
|
32
|
+
export declare function moduleCardId(module: CardModuleId, key: string): string;
|
|
33
|
+
/** Split `module:<moduleId>:<key>`. Returns undefined for session card ids. */
|
|
34
|
+
export declare function parseModuleCardId(cardId: string): {
|
|
35
|
+
module: string;
|
|
36
|
+
key: string;
|
|
37
|
+
} | undefined;
|
|
38
|
+
/** What a module returns; ids/limits are applied by `sealModuleCard`. */
|
|
39
|
+
export interface ModuleCardDraft {
|
|
40
|
+
/** Unique within the module. Combined into `module:<id>:<key>`. */
|
|
41
|
+
key: string;
|
|
42
|
+
actionClass: CardActionClass;
|
|
43
|
+
expiresAt?: number;
|
|
44
|
+
title: string;
|
|
45
|
+
question: string;
|
|
46
|
+
context?: string[];
|
|
47
|
+
choices?: CardChoice[];
|
|
48
|
+
sessionId?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The single chokepoint every module card passes through. Clamps rather than
|
|
52
|
+
* rejects: a module that over-produces should still show its question, minus
|
|
53
|
+
* the parts that cannot fit a four-button e-ink card.
|
|
54
|
+
*/
|
|
55
|
+
export declare function sealModuleCard(module: CardModuleId, draft: ModuleCardDraft): FeedCard;
|
|
56
|
+
export interface CardModuleContext {
|
|
57
|
+
/** The same enriched roster `sessions_list` broadcasts. */
|
|
58
|
+
sessions: SessionInfo[];
|
|
59
|
+
now: number;
|
|
60
|
+
}
|
|
61
|
+
export interface CardChoiceOutcome {
|
|
62
|
+
status: OutboxDecisionStatus;
|
|
63
|
+
reason?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface CardModule {
|
|
66
|
+
id: CardModuleId;
|
|
67
|
+
/** Cards this module wants in the feed right now. Called per pull, so it
|
|
68
|
+
* must be cheap and must not block. */
|
|
69
|
+
build(ctx: CardModuleContext): ModuleCardDraft[];
|
|
70
|
+
/** Apply a choice the user pressed, possibly hours ago and offline. Modules
|
|
71
|
+
* that produce no choices need not implement it. The returned status is
|
|
72
|
+
* terminal — the device drops the decision either way. */
|
|
73
|
+
apply?(decision: OutboxDecision & {
|
|
74
|
+
choiceId?: string;
|
|
75
|
+
}, key: string, ctx: CardModuleContext): CardChoiceOutcome;
|
|
76
|
+
}
|
|
77
|
+
export declare const threadModule: CardModule;
|
|
78
|
+
/** Modules that run by default. Order is feed order after the session cards. */
|
|
79
|
+
export declare const DEFAULT_CARD_MODULES: CardModule[];
|
|
80
|
+
/** Build every module's cards, sealed. A module that throws is skipped — one
|
|
81
|
+
* bad producer must not cost the device its whole feed. */
|
|
82
|
+
export declare function buildModuleCards(ctx: CardModuleContext, modules?: CardModule[]): FeedCard[];
|
|
83
|
+
/** Route a `card_choice` decision back to the module that authored the card. */
|
|
84
|
+
export declare function applyModuleChoice(decision: OutboxDecision & {
|
|
85
|
+
choiceId?: string;
|
|
86
|
+
}, ctx: CardModuleContext, modules?: CardModule[]): CardChoiceOutcome;
|
|
87
|
+
//# sourceMappingURL=card-modules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card-modules.d.ts","sourceRoot":"","sources":["../src/card-modules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAER,UAAU,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAO3B;;kEAEkE;AAClE,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAM7F;AAED,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,eAAe,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,GAAG,QAAQ,CA4BrF;AAED,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,YAAY,CAAC;IACjB;4CACwC;IACxC,KAAK,CAAC,GAAG,EAAE,iBAAiB,GAAG,eAAe,EAAE,CAAC;IACjD;;+DAE2D;IAC3D,KAAK,CAAC,CACJ,QAAQ,EAAE,cAAc,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAChD,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,iBAAiB,GACrB,iBAAiB,CAAC;CACtB;AA6BD,eAAO,MAAM,YAAY,EAAE,UAiC1B,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,EAAE,UAAU,EAAmB,CAAC;AAEjE;4DAC4D;AAC5D,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,iBAAiB,EACtB,OAAO,GAAE,UAAU,EAAyB,GAC3C,QAAQ,EAAE,CAYZ;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAChD,GAAG,EAAE,iBAAiB,EACtB,OAAO,GAAE,UAAU,EAAyB,GAC3C,iBAAiB,CAgBnB"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card modules (M7) — the daemon's own card producers.
|
|
3
|
+
*
|
|
4
|
+
* M6 cards are all projections of a live session: the daemon reports what is
|
|
5
|
+
* happening and the device renders it. A module card inverts that — the daemon
|
|
6
|
+
* *authors* a card (a checkpoint, a digest, a nudge) and the device carries it
|
|
7
|
+
* as a prepared decision, answerable even while offline.
|
|
8
|
+
*
|
|
9
|
+
* Contract: shared/src/protocol.ts § Card modules. Two rules hold here and are
|
|
10
|
+
* enforced structurally rather than by convention, because a firmware that
|
|
11
|
+
* receives a malformed card has no good move:
|
|
12
|
+
*
|
|
13
|
+
* 1. **≤4 choices, always.** Slot 1 of the four front buttons is the device's
|
|
14
|
+
* own *Later*, so a module binds at most three. `sealModuleCard` clamps.
|
|
15
|
+
* 2. **Bytes, not characters.** Every text field is trimmed to a UTF-8 byte
|
|
16
|
+
* budget on a code-point boundary — a firmware `char[]` truncates
|
|
17
|
+
* mid-sequence into broken glyphs otherwise.
|
|
18
|
+
*
|
|
19
|
+
* Routing: a module card's `cardId` is `module:<moduleId>:<key>`, which is how
|
|
20
|
+
* an outbox `card_choice` recorded hours ago finds its way back to the module
|
|
21
|
+
* that authored it.
|
|
22
|
+
*/
|
|
23
|
+
import { CARD_MAX_CHOICES, CARD_MAX_CONTEXT_LINES, truncateUtf8Bytes, } from '@agentdeck/shared';
|
|
24
|
+
/** Text budgets, in UTF-8 bytes, matched to the device's card buffers
|
|
25
|
+
* (`question[160]`, `PromptOption.label[80]` in the fork's agent_state.h).
|
|
26
|
+
* A re-port that grows a buffer bumps these together with it. */
|
|
27
|
+
export const CARD_TITLE_MAX_BYTES = 24;
|
|
28
|
+
export const CARD_QUESTION_MAX_BYTES = 160;
|
|
29
|
+
export const CARD_CONTEXT_LINE_MAX_BYTES = 96;
|
|
30
|
+
export const CARD_CHOICE_LABEL_MAX_BYTES = 40;
|
|
31
|
+
export const CARD_ID_PREFIX = 'module:';
|
|
32
|
+
export function moduleCardId(module, key) {
|
|
33
|
+
return `${CARD_ID_PREFIX}${module}:${key}`;
|
|
34
|
+
}
|
|
35
|
+
/** Split `module:<moduleId>:<key>`. Returns undefined for session card ids. */
|
|
36
|
+
export function parseModuleCardId(cardId) {
|
|
37
|
+
if (typeof cardId !== 'string' || !cardId.startsWith(CARD_ID_PREFIX))
|
|
38
|
+
return undefined;
|
|
39
|
+
const rest = cardId.slice(CARD_ID_PREFIX.length);
|
|
40
|
+
const sep = rest.indexOf(':');
|
|
41
|
+
if (sep <= 0)
|
|
42
|
+
return undefined;
|
|
43
|
+
return { module: rest.slice(0, sep), key: rest.slice(sep + 1) };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The single chokepoint every module card passes through. Clamps rather than
|
|
47
|
+
* rejects: a module that over-produces should still show its question, minus
|
|
48
|
+
* the parts that cannot fit a four-button e-ink card.
|
|
49
|
+
*/
|
|
50
|
+
export function sealModuleCard(module, draft) {
|
|
51
|
+
const choices = (draft.choices ?? [])
|
|
52
|
+
.filter((c) => c && typeof c.id === 'string' && c.id && typeof c.label === 'string' && c.label)
|
|
53
|
+
.slice(0, CARD_MAX_CHOICES)
|
|
54
|
+
.map((c) => ({
|
|
55
|
+
id: c.id,
|
|
56
|
+
label: truncateUtf8Bytes(c.label, CARD_CHOICE_LABEL_MAX_BYTES),
|
|
57
|
+
...(c.intent ? { intent: c.intent } : {}),
|
|
58
|
+
}));
|
|
59
|
+
const context = (draft.context ?? [])
|
|
60
|
+
.filter((l) => typeof l === 'string' && l.length > 0)
|
|
61
|
+
.slice(0, CARD_MAX_CONTEXT_LINES)
|
|
62
|
+
.map((l) => truncateUtf8Bytes(l, CARD_CONTEXT_LINE_MAX_BYTES));
|
|
63
|
+
const body = {
|
|
64
|
+
module,
|
|
65
|
+
title: truncateUtf8Bytes(draft.title, CARD_TITLE_MAX_BYTES),
|
|
66
|
+
question: truncateUtf8Bytes(draft.question, CARD_QUESTION_MAX_BYTES),
|
|
67
|
+
...(context.length ? { context } : {}),
|
|
68
|
+
...(choices.length ? { choices } : {}),
|
|
69
|
+
...(draft.sessionId ? { sessionId: draft.sessionId } : {}),
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
cardId: moduleCardId(module, draft.key),
|
|
73
|
+
actionClass: draft.actionClass,
|
|
74
|
+
...(draft.expiresAt !== undefined ? { expiresAt: draft.expiresAt } : {}),
|
|
75
|
+
module: body,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// ===== THREAD — the reference module =====
|
|
79
|
+
//
|
|
80
|
+
// The checkpoint card: where the open threads stopped. It is `info` (no
|
|
81
|
+
// choices) and derives entirely from the live roster, so it introduces no new
|
|
82
|
+
// state, no persistence and no product policy — its job is to prove the module
|
|
83
|
+
// path end to end. NUDGE / QUEST (the first `day`-class, answerable modules)
|
|
84
|
+
// need a store and an authoring path, and are deliberately not guessed at here.
|
|
85
|
+
/** A thread is worth checkpointing only if it is a real, alive session. */
|
|
86
|
+
const THREAD_MAX_LINES = CARD_MAX_CONTEXT_LINES;
|
|
87
|
+
function threadLine(s) {
|
|
88
|
+
const project = s.projectName || s.agentType || 'session';
|
|
89
|
+
if (typeof s.state === 'string' && s.state.startsWith('awaiting')) {
|
|
90
|
+
return `${project} — waiting on you`;
|
|
91
|
+
}
|
|
92
|
+
if (s.state === 'processing') {
|
|
93
|
+
const mins = typeof s.elapsedSec === 'number' && s.elapsedSec > 0 ? Math.round(s.elapsedSec / 60) : 0;
|
|
94
|
+
const tool = typeof s.currentTool === 'string' && s.currentTool ? ` ${s.currentTool}` : '';
|
|
95
|
+
return `${project} — working${tool}${mins > 0 ? ` ${mins}m` : ''}`;
|
|
96
|
+
}
|
|
97
|
+
// Idle: what it was last doing is the checkpoint — that is the whole point of
|
|
98
|
+
// the card. `currentTask` is the daemon-computed milestone line.
|
|
99
|
+
const task = typeof s.currentTask === 'string' && s.currentTask ? s.currentTask : '';
|
|
100
|
+
return task ? `${project} — ${task}` : `${project} — idle`;
|
|
101
|
+
}
|
|
102
|
+
export const threadModule = {
|
|
103
|
+
id: 'thread',
|
|
104
|
+
build(ctx) {
|
|
105
|
+
const alive = ctx.sessions.filter((s) => s.alive !== false);
|
|
106
|
+
if (alive.length === 0)
|
|
107
|
+
return [];
|
|
108
|
+
// Attention first, then work in progress, then the rest: the top lines are
|
|
109
|
+
// the ones worth reading if only the first line is legible across a room.
|
|
110
|
+
const rank = (s) => {
|
|
111
|
+
if (typeof s.state === 'string' && s.state.startsWith('awaiting'))
|
|
112
|
+
return 0;
|
|
113
|
+
if (s.state === 'processing')
|
|
114
|
+
return 1;
|
|
115
|
+
return 2;
|
|
116
|
+
};
|
|
117
|
+
const ordered = [...alive].sort((a, b) => rank(a) - rank(b));
|
|
118
|
+
const shown = ordered.slice(0, THREAD_MAX_LINES);
|
|
119
|
+
const hidden = ordered.length - shown.length;
|
|
120
|
+
const waiting = alive.filter((s) => typeof s.state === 'string' && s.state.startsWith('awaiting')).length;
|
|
121
|
+
const question = waiting > 0
|
|
122
|
+
? `${waiting} of ${alive.length} threads need you`
|
|
123
|
+
: `${alive.length} thread${alive.length === 1 ? '' : 's'} open`;
|
|
124
|
+
const context = shown.map((s) => threadLine(s));
|
|
125
|
+
// An honest count beats a silently truncated list — the device shows one
|
|
126
|
+
// screen, so say what did not fit rather than implying this is all of it.
|
|
127
|
+
if (hidden > 0)
|
|
128
|
+
context[context.length - 1] = `${context[context.length - 1]} (+${hidden} more)`;
|
|
129
|
+
return [{
|
|
130
|
+
key: 'open',
|
|
131
|
+
actionClass: 'info',
|
|
132
|
+
title: 'THREAD',
|
|
133
|
+
question,
|
|
134
|
+
context,
|
|
135
|
+
}];
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
/** Modules that run by default. Order is feed order after the session cards. */
|
|
139
|
+
export const DEFAULT_CARD_MODULES = [threadModule];
|
|
140
|
+
/** Build every module's cards, sealed. A module that throws is skipped — one
|
|
141
|
+
* bad producer must not cost the device its whole feed. */
|
|
142
|
+
export function buildModuleCards(ctx, modules = DEFAULT_CARD_MODULES) {
|
|
143
|
+
const out = [];
|
|
144
|
+
for (const m of modules) {
|
|
145
|
+
let drafts;
|
|
146
|
+
try {
|
|
147
|
+
drafts = m.build(ctx) ?? [];
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
for (const d of drafts)
|
|
153
|
+
out.push(sealModuleCard(m.id, d));
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
/** Route a `card_choice` decision back to the module that authored the card. */
|
|
158
|
+
export function applyModuleChoice(decision, ctx, modules = DEFAULT_CARD_MODULES) {
|
|
159
|
+
const parsed = parseModuleCardId(decision.cardId);
|
|
160
|
+
if (!parsed)
|
|
161
|
+
return { status: 'rejected', reason: 'not a module card id' };
|
|
162
|
+
if (typeof decision.choiceId !== 'string' || !decision.choiceId) {
|
|
163
|
+
return { status: 'rejected', reason: 'choiceId required' };
|
|
164
|
+
}
|
|
165
|
+
const mod = modules.find((m) => m.id === parsed.module);
|
|
166
|
+
// Unknown module, or a read-only one: the card is gone as far as the daemon
|
|
167
|
+
// is concerned, which is terminal for the device either way.
|
|
168
|
+
if (!mod)
|
|
169
|
+
return { status: 'unknown_card', reason: `no module '${parsed.module}'` };
|
|
170
|
+
if (!mod.apply)
|
|
171
|
+
return { status: 'rejected', reason: `module '${parsed.module}' takes no choices` };
|
|
172
|
+
try {
|
|
173
|
+
return mod.apply(decision, parsed.key, ctx);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
return { status: 'error', reason: err instanceof Error ? err.message : String(err) };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=card-modules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card-modules.js","sourceRoot":"","sources":["../src/card-modules.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2LpC,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAM,CAAC;AAOtF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AAoBD,QAAA,MAAM,OAAO,SAAgB,CAAC;AA4/D9B,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzE;AAED,OAAO,EAAE,OAAO,EAAE,CAAC"}
|