@spirobel/monero-wallet-api 0.5.4 → 0.6.0

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.
Files changed (91) hide show
  1. package/dist/actionlog/actionLogOpened.d.ts +96 -1
  2. package/dist/actionlog/actionLogOpened.js +386 -1
  3. package/dist/actionlog/actionLogTypes.d.ts +114 -0
  4. package/dist/actionlog/actionLogTypes.js +198 -0
  5. package/dist/actionlog/backend/idbBackend.d.ts +13 -0
  6. package/dist/actionlog/backend/idbBackend.js +109 -0
  7. package/dist/actionlog/backend/jsonlBackend.d.ts +19 -0
  8. package/dist/actionlog/backend/jsonlBackend.js +110 -0
  9. package/dist/actionlog/backend/memoryBackend.d.ts +11 -0
  10. package/dist/actionlog/backend/memoryBackend.js +31 -0
  11. package/dist/actionlog/backend/sqliteBackend.d.ts +14 -0
  12. package/dist/actionlog/backend/sqliteBackend.js +96 -0
  13. package/dist/actionlog/backend.d.ts +12 -0
  14. package/dist/actionlog/backend.js +1 -0
  15. package/dist/actionlog/idbBackend.d.ts +19 -0
  16. package/dist/actionlog/idbBackend.js +98 -0
  17. package/dist/actionlog/jsonlBackend.d.ts +19 -0
  18. package/dist/actionlog/jsonlBackend.js +75 -0
  19. package/dist/actionlog/memoryBackend.d.ts +14 -0
  20. package/dist/actionlog/memoryBackend.js +38 -0
  21. package/dist/actionlog/openMaps.d.ts +11 -0
  22. package/dist/actionlog/openMaps.js +32 -0
  23. package/dist/actionlog/sqliteBackend.d.ts +18 -0
  24. package/dist/actionlog/sqliteBackend.js +105 -0
  25. package/dist/actionlog/toolContext.d.ts +36 -0
  26. package/dist/actionlog/toolContext.js +1 -0
  27. package/dist/actionlog/types.d.ts +62 -0
  28. package/dist/actionlog/types.js +62 -0
  29. package/dist/api.d.ts +4 -0
  30. package/dist/api.js +3 -0
  31. package/dist/io/sleep.d.ts +1 -1
  32. package/dist/io/sleep.js +16 -1
  33. package/dist/scanning-syncing/backgroundWorker.d.ts +2 -0
  34. package/dist/scanning-syncing/backgroundWorker.js +92 -10
  35. package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +33 -2
  36. package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.js +2 -2
  37. package/dist/scanning-syncing/connectionStatus.d.ts +8 -0
  38. package/dist/scanning-syncing/connectionStatus.js +50 -0
  39. package/dist/scanning-syncing/connectionStatusOpened.d.ts +3 -1
  40. package/dist/scanning-syncing/connectionStatusOpened.js +22 -14
  41. package/dist/scanning-syncing/openWallet.d.ts +4 -1
  42. package/dist/scanning-syncing/openWallet.js +3 -0
  43. package/dist/scanning-syncing/scanresult/scanCache.d.ts +5 -1
  44. package/dist/scanning-syncing/scanresult/scanCache.js +11 -2
  45. package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +56 -5
  46. package/dist/scanning-syncing/scanresult/scanCacheOpened.js +422 -83
  47. package/dist/scanning-syncing/scanresult/scanCoordination.d.ts +7 -3
  48. package/dist/scanning-syncing/scanresult/scanCoordination.js +126 -28
  49. package/dist/scanning-syncing/scanresult/scanLoop.d.ts +1 -2
  50. package/dist/scanning-syncing/scanresult/scanLoop.js +0 -13
  51. package/dist/scanning-syncing/scanresult/scanResult.js +17 -12
  52. package/dist/scanning-syncing/scanresult/scanStats.js +8 -5
  53. package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
  54. package/dist/scanning-syncing/worker-mains/coordinator-main.d.ts +1 -0
  55. package/dist/scanning-syncing/worker-mains/coordinator-main.js +16 -1
  56. package/dist/scanning-syncing/worker-mains/cpubound-main.js +3 -2
  57. package/dist/scanning-syncing/worker-mains/worker.js +45 -2
  58. package/dist/tools/calls/001.d.ts +20 -0
  59. package/dist/tools/calls/001.js +236 -0
  60. package/dist/tools/calls/002-content.d.ts +46 -0
  61. package/dist/tools/calls/002-content.js +135 -0
  62. package/dist/tools/calls/002-fail-cleanup.d.ts +27 -0
  63. package/dist/tools/calls/002-fail-cleanup.js +60 -0
  64. package/dist/tools/calls/002.d.ts +64 -0
  65. package/dist/tools/calls/002.js +353 -0
  66. package/dist/tools/content.d.ts +22 -0
  67. package/dist/tools/content.js +41 -0
  68. package/dist/tools/counterparty.d.ts +3 -0
  69. package/dist/tools/counterparty.js +1 -0
  70. package/dist/tools/download-public-suffix.d.ts +1 -0
  71. package/dist/tools/download-public-suffix.js +20 -0
  72. package/dist/tools/globals.d.ts +196 -0
  73. package/dist/tools/globals.js +14 -0
  74. package/dist/tools/monero-tools-content.d.ts +29 -0
  75. package/dist/tools/monero-tools-content.js +80 -0
  76. package/dist/tools/monero-tools.d.ts +27 -52
  77. package/dist/tools/monero-tools.js +127 -222
  78. package/dist/tools/public-suffix-list.d.ts +1 -0
  79. package/dist/tools/public-suffix-list.js +10336 -0
  80. package/dist/tools/runtimeMessages.d.ts +4 -0
  81. package/dist/tools/runtimeMessages.js +18 -0
  82. package/dist/tools/toolActionLogContext.d.ts +33 -0
  83. package/dist/tools/toolActionLogContext.js +1 -0
  84. package/dist/tools/toolArm.d.ts +47 -0
  85. package/dist/tools/toolArm.js +1 -0
  86. package/dist/tools/ui.d.ts +2 -0
  87. package/dist/tools/ui.js +1 -0
  88. package/dist/tools/worker.d.ts +1 -0
  89. package/dist/tools/worker.js +1 -0
  90. package/dist/wasm-processing/wasmFile.js +2 -2
  91. package/package.json +8 -1
@@ -1 +1,96 @@
1
- export {};
1
+ import { type ActionLogBackend, type ActionLogEvent, type InvocationState, type InvocationStatus, type ToolPermission } from "./actionLogTypes";
2
+ import type { ParsedMoneroToolInvocation } from "../tools/monero-tools";
3
+ import { type ExtensionPort, type ToolNotice } from "../tools/globals";
4
+ export type ActionLogBackendKind = "sqlite" | "idb";
5
+ export type ExtensionMessageBus = "worker" | "ui";
6
+ export type ActionLogOpenedCreateOptions = {
7
+ path?: string;
8
+ backend?: ActionLogBackendKind;
9
+ backendInstance?: ActionLogBackend;
10
+ onChange?: (() => void) | null;
11
+ extensionMessageBus?: ExtensionMessageBus;
12
+ };
13
+ type BoundMco = {
14
+ feed: (params: any) => Promise<void>;
15
+ wallets?: unknown;
16
+ changeNodeUrl: (node_url: string) => Promise<void>;
17
+ changeStartHeight: (start_height: number | null) => Promise<void>;
18
+ changeNodeUrlAndStartHeight: (node_url?: string, start_height?: number | null) => Promise<void>;
19
+ buildWallets: () => Promise<void>;
20
+ stopWorker: () => Promise<void>;
21
+ makeSignSend: (params: {
22
+ primary_address: string;
23
+ payments: {
24
+ address: string;
25
+ amount: string;
26
+ }[];
27
+ input_indexes: string[];
28
+ }) => Promise<unknown>;
29
+ sweepSignSend: (params: {
30
+ primary_address: string;
31
+ address: string;
32
+ input_indexes: string[];
33
+ }) => Promise<unknown>;
34
+ rebroadcastTx: (params: {
35
+ primary_address: string;
36
+ tx_log_index: number;
37
+ }) => Promise<unknown>;
38
+ dismissSendPlate: (params: {
39
+ primary_address: string;
40
+ }) => Promise<unknown>;
41
+ setLogSettings: (logs?: "console" | "file" | "console-and-file" | "off" | null, logs_include?: string[] | null, logs_exclude?: string[] | null) => Promise<unknown>;
42
+ };
43
+ export declare class ActionLogOpened {
44
+ private backend;
45
+ private _onChange;
46
+ private ports;
47
+ private runtimeInstalled;
48
+ private bus;
49
+ private mco;
50
+ private invocationsCache;
51
+ private activeCache;
52
+ private noticeList;
53
+ private toolCallTail;
54
+ private constructor();
55
+ static create(options?: ActionLogOpenedCreateOptions): Promise<ActionLogOpened>;
56
+ setOnChange(cb: (() => void) | null): void;
57
+ bindMco(mco: BoundMco): void;
58
+ /** after a write, the invocation lists that the plates read. */
59
+ private refreshUiCache;
60
+ private toolCtx;
61
+ get extensionMessageBus(): ExtensionMessageBus | null;
62
+ private isUiBus;
63
+ private broadcastActionLogChanged;
64
+ append(partial: Omit<ActionLogEvent, "id" | "timestamp"> & Partial<Pick<ActionLogEvent, "id" | "timestamp">>): Promise<ActionLogEvent>;
65
+ /** one invocation by id. sqlite and idb select by primary key. */
66
+ invocation(invocationId: string): Promise<InvocationState | null>;
67
+ /** all invocations, or the active ones. */
68
+ invocations(status?: InvocationStatus): InvocationState[];
69
+ /** invoke and validate for one invocation id, in order. */
70
+ toolCall(invo: ParsedMoneroToolInvocation): Promise<void>;
71
+ /** first toolCall state transition: session_start invoked when no row. second state transition: validate_result validated if valid is not unverified. */
72
+ private applyToolCall;
73
+ /** only the newest active call per permission category stays open. */
74
+ private dismissOthersOfSamePermission;
75
+ /** open invocations for these permissions. the send and wallets plates show these. */
76
+ getActiveByPermissions(permissions: ToolPermission[]): InvocationState[];
77
+ getNoticesByPermissions(permissions: ToolPermission[]): ToolNotice[];
78
+ dismissNotice(noticeId: string): Promise<void>;
79
+ dismiss(invocationId: string): Promise<void>;
80
+ execute(invocationId: string, args?: Record<string, unknown>): Promise<void>;
81
+ getPort(invocationId: string): ExtensionPort | undefined;
82
+ private listenOnMessage;
83
+ installExtensionMessagebus(): void;
84
+ private installUiMessagebus;
85
+ private installWorkerMessagebus;
86
+ private handleUiMessage;
87
+ private handleWorkerMessage;
88
+ private dismissLocal;
89
+ private executeLocal;
90
+ private dismissNoticeLocal;
91
+ private openWalletUi;
92
+ private dispatchPortDisconnect;
93
+ }
94
+ export type { ActionLogBackend, ActionLogEvent, ActionLogEventType, ActionLogStage, InvocationDisplayStatus, InvocationState, ToolPermission, ToolWorkerContext, } from "./actionLogTypes";
95
+ export { Invocation } from "./actionLogTypes";
96
+ export type { ToolNotice, ToolUiCopy } from "../tools/globals";
@@ -1 +1,386 @@
1
- export {};
1
+ import { newEventId, nowTimestamp, } from "./actionLogTypes";
2
+ import { tools } from "../tools/monero-tools";
3
+ import { getExtensionRuntime, sendToBackground, } from "../tools/globals";
4
+ function armWorker(toolId) {
5
+ return tools[toolId]?.worker;
6
+ }
7
+ export class ActionLogOpened {
8
+ backend;
9
+ _onChange;
10
+ ports = new Map();
11
+ runtimeInstalled = false;
12
+ bus = null;
13
+ mco = null;
14
+ invocationsCache = [];
15
+ activeCache = [];
16
+ noticeList = [];
17
+ toolCallTail = new Map();
18
+ constructor(backend, _onChange) {
19
+ this.backend = backend;
20
+ this._onChange = _onChange;
21
+ }
22
+ static async create(options = {}) {
23
+ const backend = await openBackend(options);
24
+ const log = new ActionLogOpened(backend, options.onChange ?? null);
25
+ log.bus = options.extensionMessageBus ?? null;
26
+ if (log.bus)
27
+ log.installExtensionMessagebus();
28
+ await log.refreshUiCache();
29
+ return log;
30
+ }
31
+ setOnChange(cb) {
32
+ this._onChange = cb;
33
+ }
34
+ // mco wires itself after create
35
+ bindMco(mco) {
36
+ this.mco = mco;
37
+ }
38
+ /** after a write, the invocation lists that the plates read. */
39
+ async refreshUiCache() {
40
+ this.invocationsCache = await this.backend.invocations();
41
+ this.activeCache = await this.backend.invocations("active");
42
+ this.noticeList = [];
43
+ this._onChange?.();
44
+ }
45
+ toolCtx() {
46
+ return {
47
+ log: this,
48
+ getWallets: () => this.mco,
49
+ reloadWallets: () => this.mco?.buildWallets(),
50
+ getPort: (id) => this.ports.get(id),
51
+ };
52
+ }
53
+ get extensionMessageBus() {
54
+ return this.bus;
55
+ }
56
+ isUiBus() {
57
+ return this.bus === "ui";
58
+ }
59
+ broadcastActionLogChanged() {
60
+ void sendToBackground("actionLogChanged", null).catch(() => { });
61
+ }
62
+ async append(partial) {
63
+ const event = {
64
+ ...partial,
65
+ id: partial.id ?? newEventId(),
66
+ timestamp: partial.timestamp ?? nowTimestamp(),
67
+ };
68
+ await this.backend.append(event);
69
+ await this.refreshUiCache();
70
+ this.broadcastActionLogChanged();
71
+ return event;
72
+ }
73
+ /** one invocation by id. sqlite and idb select by primary key. */
74
+ async invocation(invocationId) {
75
+ return this.backend.invocation(invocationId);
76
+ }
77
+ /** all invocations, or the active ones. */
78
+ invocations(status) {
79
+ if (!status)
80
+ return [...this.invocationsCache];
81
+ if (status === "active")
82
+ return [...this.activeCache];
83
+ return this.invocationsCache.filter((s) => s.lastType === status);
84
+ }
85
+ /** invoke and validate for one invocation id, in order. */
86
+ async toolCall(invo) {
87
+ const id = invo.invocation_id;
88
+ const prev = this.toolCallTail.get(id) ?? Promise.resolve();
89
+ const next = prev.then(() => this.applyToolCall(invo));
90
+ this.toolCallTail.set(id, next.catch(() => { }));
91
+ await next;
92
+ }
93
+ /** first toolCall state transition: session_start invoked when no row. second state transition: validate_result validated if valid is not unverified. */
94
+ async applyToolCall(invo) {
95
+ const w = armWorker(invo.tool.tool_id);
96
+ const ctx = this.toolCtx();
97
+ const isNew = !(await this.invocation(invo.invocation_id));
98
+ if (isNew) {
99
+ // a new call takes over its permission category. older active calls of the same permission are dismissed
100
+ await this.dismissOthersOfSamePermission(invo);
101
+ await w?.invoke_write?.(ctx, invo);
102
+ }
103
+ if (invo.valid !== "unverified")
104
+ await w?.validate_write?.(ctx, invo);
105
+ }
106
+ /** only the newest active call per permission category stays open. */
107
+ async dismissOthersOfSamePermission(invo) {
108
+ const id = invo.tool.tool_id;
109
+ const perms = tools[id]?.permissions ?? [];
110
+ if (!perms.length)
111
+ return;
112
+ await this.refreshUiCache();
113
+ const open = this.getActiveByPermissions(perms);
114
+ for (const row of open) {
115
+ if (row.invocationId === invo.invocation_id)
116
+ continue;
117
+ await this.dismiss(row.invocationId);
118
+ }
119
+ }
120
+ /** open invocations for these permissions. the send and wallets plates show these. */
121
+ getActiveByPermissions(permissions) {
122
+ const want = new Set(permissions);
123
+ const toolIds = Object.keys(tools).filter((id) => tools[id].permissions.some((p) => want.has(p)));
124
+ const idSet = new Set(toolIds);
125
+ return this.invocations("active").filter((o) => idSet.has(o.toolId));
126
+ }
127
+ getNoticesByPermissions(permissions) {
128
+ const want = new Set(permissions);
129
+ return this.noticeList.filter((n) => want.has(n.permission));
130
+ }
131
+ async dismissNotice(noticeId) {
132
+ if (this.isUiBus()) {
133
+ await sendToBackground("dismissNotice", { noticeId }).catch(() => { });
134
+ await this.refreshUiCache();
135
+ return;
136
+ }
137
+ await this.dismissNoticeLocal(noticeId);
138
+ }
139
+ async dismiss(invocationId) {
140
+ if (this.isUiBus()) {
141
+ await sendToBackground("dismiss", { invocationId }).catch(() => { });
142
+ return;
143
+ }
144
+ await this.dismissLocal(invocationId);
145
+ }
146
+ async execute(invocationId, args = {}) {
147
+ if (this.isUiBus()) {
148
+ await sendToBackground("execute", { invocationId, args }).catch(() => { });
149
+ return;
150
+ }
151
+ await this.executeLocal(invocationId, args);
152
+ }
153
+ getPort(invocationId) {
154
+ return this.ports.get(invocationId);
155
+ }
156
+ listenOnMessage(handle) {
157
+ const rt = getExtensionRuntime();
158
+ if (!rt)
159
+ return;
160
+ rt.onMessage.addListener((msg, sender, sendResponse) => {
161
+ const done = handle(msg, sender).then((result) => {
162
+ if (result !== undefined)
163
+ sendResponse?.(result);
164
+ return result;
165
+ });
166
+ void done;
167
+ return true;
168
+ });
169
+ }
170
+ installExtensionMessagebus() {
171
+ if (this.runtimeInstalled)
172
+ return;
173
+ if (this.bus === "worker")
174
+ this.installWorkerMessagebus();
175
+ else if (this.bus === "ui")
176
+ this.installUiMessagebus();
177
+ }
178
+ installUiMessagebus() {
179
+ this.runtimeInstalled = true;
180
+ this.listenOnMessage((env) => this.handleUiMessage(env));
181
+ }
182
+ installWorkerMessagebus() {
183
+ const rt = getExtensionRuntime();
184
+ if (!rt)
185
+ return;
186
+ this.runtimeInstalled = true;
187
+ this.listenOnMessage((env, sender) => this.handleWorkerMessage(env, sender));
188
+ rt.onConnect.addListener((port) => {
189
+ const invocationId = port.name;
190
+ const existing = this.ports.get(invocationId);
191
+ if (existing)
192
+ existing.disconnect();
193
+ this.ports.set(invocationId, port);
194
+ port.onDisconnect.addListener(() => {
195
+ this.ports.delete(invocationId);
196
+ void this.dispatchPortDisconnect(invocationId);
197
+ });
198
+ });
199
+ }
200
+ async handleUiMessage(env) {
201
+ if (!env?.kind)
202
+ return {};
203
+ if (env.kind === "actionLogChanged") {
204
+ if (this.backend.reload) {
205
+ await this.backend.reload();
206
+ await this.refreshUiCache();
207
+ }
208
+ return {};
209
+ }
210
+ if (env.kind === "walletCacheChanged") {
211
+ const raw = typeof env.payload === "string"
212
+ ? JSON.parse(env.payload, (key, value) => {
213
+ if (key === "amount")
214
+ return BigInt(value);
215
+ return value;
216
+ })
217
+ : env.payload;
218
+ await this.mco?.feed(raw);
219
+ return {};
220
+ }
221
+ // Unhandled kinds (worker.* queries especially) get no answer here.
222
+ // The worker answers, and the first answer wins. A local {} would win
223
+ // the race and hide the worker response.
224
+ return;
225
+ }
226
+ async handleWorkerMessage(env, sender) {
227
+ if (!env?.kind)
228
+ return {};
229
+ const ctx = this.toolCtx();
230
+ switch (env.kind) {
231
+ case "openSidebar":
232
+ this.openWalletUi(sender);
233
+ return {};
234
+ case "worker.setNodeUrl": {
235
+ const p = env.payload;
236
+ if (p?.node_url != null)
237
+ await this.mco?.changeNodeUrl(p.node_url);
238
+ return {};
239
+ }
240
+ case "worker.setStartHeight": {
241
+ const p = env.payload;
242
+ if (p && "start_height" in p)
243
+ await this.mco?.changeStartHeight(p.start_height ?? null);
244
+ return {};
245
+ }
246
+ case "worker.changeNodeUrlAndStartHeight": {
247
+ const p = env.payload;
248
+ await this.mco?.changeNodeUrlAndStartHeight(p?.node_url, p?.start_height);
249
+ return {};
250
+ }
251
+ case "worker.buildWallets":
252
+ await this.mco?.buildWallets();
253
+ return {};
254
+ case "worker.stopWorker":
255
+ await this.mco?.stopWorker();
256
+ return {};
257
+ case "worker.makeSignSend": {
258
+ const p = env.payload;
259
+ await this.mco?.makeSignSend(p);
260
+ return {};
261
+ }
262
+ case "worker.sweepSignSend": {
263
+ const p = env.payload;
264
+ await this.mco?.sweepSignSend(p);
265
+ return {};
266
+ }
267
+ case "worker.rebroadcastTx": {
268
+ const p = env.payload;
269
+ await this.mco?.rebroadcastTx(p);
270
+ return {};
271
+ }
272
+ case "worker.dismissSendPlate": {
273
+ const p = env.payload;
274
+ await this.mco?.dismissSendPlate(p);
275
+ return {};
276
+ }
277
+ case "worker.setLogSettings": {
278
+ const p = env.payload;
279
+ await this.mco?.setLogSettings(p?.logs, p?.logs_include, p?.logs_exclude);
280
+ return {};
281
+ }
282
+ case "worker.getTxLogs": {
283
+ const wallets = this.mco?.wallets;
284
+ const rows = (wallets ?? []).map((w) => ({
285
+ primary_address: w.primary_address,
286
+ tx_logs: (w.tx_logs ?? []).map((t, index) => {
287
+ const out = { ...t, index };
288
+ delete out.signed_tx;
289
+ return out;
290
+ }),
291
+ }));
292
+ // chrome messages must be plain JSON. cache rows can hold bigints.
293
+ const clean = JSON.parse(JSON.stringify(rows, (_k, v) => typeof v === "bigint" ? v.toString() : v));
294
+ return { tx_logs: clean };
295
+ }
296
+ case "toolCall": {
297
+ await this.toolCall(env.payload);
298
+ return {};
299
+ }
300
+ case "dismiss": {
301
+ const p = env.payload;
302
+ await this.dismissLocal(p.invocationId);
303
+ return {};
304
+ }
305
+ case "execute": {
306
+ const p = env.payload;
307
+ await this.executeLocal(p.invocationId, p.args ?? {});
308
+ return {};
309
+ }
310
+ case "dismissNotice": {
311
+ const p = env.payload;
312
+ await this.dismissNoticeLocal(p.noticeId);
313
+ return {};
314
+ }
315
+ case "shareViewkeyOK": {
316
+ const p = env.payload;
317
+ for (const id of Object.keys(tools)) {
318
+ await armWorker(id)?.execute_ok?.(ctx, p);
319
+ }
320
+ return {};
321
+ }
322
+ case "shareViewkeyFAILED": {
323
+ for (const id of Object.keys(tools)) {
324
+ await armWorker(id)?.execute_fail?.(ctx, env.payload);
325
+ }
326
+ return {};
327
+ }
328
+ default:
329
+ return {};
330
+ }
331
+ }
332
+ async dismissLocal(invocationId) {
333
+ const invo = await this.invocation(invocationId);
334
+ if (!invo)
335
+ return;
336
+ await armWorker(invo.toolId)?.accept_no?.(this.toolCtx(), invocationId);
337
+ }
338
+ async executeLocal(invocationId, args) {
339
+ const invo = await this.invocation(invocationId);
340
+ if (!invo)
341
+ return;
342
+ const w = armWorker(invo.toolId);
343
+ const ctx = this.toolCtx();
344
+ await w?.accept_yes?.(ctx, invocationId, args);
345
+ await w?.execute_run?.(ctx, invocationId, args);
346
+ }
347
+ async dismissNoticeLocal(_noticeId) {
348
+ await this.refreshUiCache();
349
+ }
350
+ openWalletUi(sender) {
351
+ const g = globalThis;
352
+ const api = g.browser ?? g.chrome;
353
+ void api?.sidebarAction?.open?.().catch(() => { });
354
+ const windowId = sender?.tab?.windowId;
355
+ if (windowId != null) {
356
+ void api?.sidePanel?.open?.({ windowId }).catch(() => { });
357
+ }
358
+ }
359
+ async dispatchPortDisconnect(invocationId) {
360
+ const invo = await this.invocation(invocationId);
361
+ if (!invo)
362
+ return;
363
+ await armWorker(invo.toolId)?.execute_abort?.(this.toolCtx(), invocationId);
364
+ }
365
+ }
366
+ function defaultBackendKind() {
367
+ // indexedDB shim sets this. unset means bun/node.
368
+ return globalThis.areWeInTheBrowser === true ? "idb" : "sqlite";
369
+ }
370
+ async function openBackend(options) {
371
+ if (options.backendInstance)
372
+ return options.backendInstance;
373
+ const kind = options.backend ?? defaultBackendKind();
374
+ if (kind === "idb") {
375
+ const { ActionLogIdbBackend } = await import("./backend/idbBackend");
376
+ const path = options.path ?? "actionlog";
377
+ return ActionLogIdbBackend.open(path);
378
+ }
379
+ if (kind === "sqlite") {
380
+ const { ActionLogSqliteBackend } = await import("./backend/sqliteBackend");
381
+ const path = options.path ?? "actionlog.sqlite";
382
+ return ActionLogSqliteBackend.open(path);
383
+ }
384
+ throw new Error(`unknown action log backend: ${kind}`);
385
+ }
386
+ export { Invocation } from "./actionLogTypes";
@@ -0,0 +1,114 @@
1
+ import type { ToolId } from "../tools/monero-tools";
2
+ import type { ExtensionPort, ToolInvocationValidity, ToolPermission } from "../tools/globals";
3
+ import type { ActionLogOpened } from "./actionLogOpened";
4
+ export type { ToolPermission };
5
+ export type ActionLogEventType = "session_start" | "validate_start" | "validate_result" | "accept" | "dismiss" | "execute_start" | "execute_error" | "execute_result" | "aborted";
6
+ export type ActionLogStage = "recognized" | "invoked" | "validated" | "accepted" | "executed";
7
+ export type ActionLogEvent = {
8
+ type: ActionLogEventType;
9
+ id: string;
10
+ timestamp: string;
11
+ invocationId: string;
12
+ stage: ActionLogStage;
13
+ toolId: ToolId;
14
+ valid?: ToolInvocationValidity;
15
+ amount?: string;
16
+ address?: string;
17
+ no_check?: boolean;
18
+ wallet_slot?: number;
19
+ context_domain?: string;
20
+ destination_domain?: string;
21
+ context_href?: string;
22
+ found_in?: "link" | "linkText";
23
+ link?: string;
24
+ linkText?: string;
25
+ wallet_to_send_from_pa?: string;
26
+ ok?: boolean;
27
+ error?: string;
28
+ [key: string]: unknown;
29
+ };
30
+ export type InvocationDisplayStatus = "done" | "dismissed" | "aborted" | "failed" | "in progress";
31
+ export declare class Invocation {
32
+ invocationId: string;
33
+ toolId: ToolId;
34
+ stage: ActionLogStage;
35
+ lastType: ActionLogEventType;
36
+ valid?: ToolInvocationValidity;
37
+ amount?: string;
38
+ address?: string;
39
+ no_check?: boolean;
40
+ wallet_slot?: number;
41
+ context_domain?: string;
42
+ destination_domain?: string;
43
+ context_href?: string;
44
+ found_in?: "link" | "linkText";
45
+ link?: string;
46
+ linkText?: string;
47
+ timestamp?: string;
48
+ wallet_to_send_from_pa?: string;
49
+ ok?: boolean;
50
+ error?: string;
51
+ events: ActionLogEvent[];
52
+ /** an Invocation from stored fields or from advanceInvo. */
53
+ constructor(init: {
54
+ invocationId: string;
55
+ toolId: ToolId;
56
+ stage: ActionLogStage;
57
+ lastType: ActionLogEventType;
58
+ events?: ActionLogEvent[];
59
+ valid?: ToolInvocationValidity;
60
+ amount?: string;
61
+ address?: string;
62
+ no_check?: boolean;
63
+ wallet_slot?: number;
64
+ context_domain?: string;
65
+ destination_domain?: string;
66
+ context_href?: string;
67
+ found_in?: "link" | "linkText";
68
+ link?: string;
69
+ linkText?: string;
70
+ timestamp?: string;
71
+ wallet_to_send_from_pa?: string;
72
+ ok?: boolean;
73
+ error?: string;
74
+ });
75
+ /** the label on the list and detail pages. a failed send is failed, not done. */
76
+ get status(): InvocationDisplayStatus;
77
+ }
78
+ export type InvocationState = Invocation;
79
+ export declare const TERMINAL_EVENT_TYPES: ReadonlySet<ActionLogEventType>;
80
+ export declare function isTerminalEventType(type: ActionLogEventType): boolean;
81
+ export declare function newEventId(): string;
82
+ export declare function nowTimestamp(): string;
83
+ export type ToolWorkerContext = {
84
+ log: ActionLogOpened;
85
+ getWallets?: () => unknown;
86
+ reloadWallets?: () => void | Promise<void>;
87
+ getPort: (invocationId: string) => ExtensionPort | undefined;
88
+ };
89
+ /** apply one event to the current invocation state. advances invocation state. */
90
+ export declare function advanceInvo(prev: InvocationState | null, event: ActionLogEvent): InvocationState;
91
+ /** still live. false for dismiss, execute_result, aborted. */
92
+ export declare function isActive(state: InvocationState): boolean;
93
+ export type InvocationStatus = "active" | ActionLogEventType;
94
+ /** the status column: active, or lastType when the invocation ended. */
95
+ export declare function invocationStatus(state: InvocationState): InvocationStatus;
96
+ export type ActionLogBackend = {
97
+ append(event: ActionLogEvent): Promise<void>;
98
+ invocation(invocationId: string): Promise<InvocationState | null>;
99
+ invocations(status?: InvocationStatus): Promise<InvocationState[]>;
100
+ reload?(): Promise<void>;
101
+ };
102
+ export declare class ActiveInvocations {
103
+ byInvocation: Map<string, Invocation>;
104
+ byTool: Map<"001" | "002", string>;
105
+ /** update the invo for this event. byTool keeps one live invo per tool. */
106
+ apply(event: ActionLogEvent): Invocation;
107
+ /** invocations from sqlite or idb into byInvocation and byTool. */
108
+ loadStates(states: InvocationState[]): void;
109
+ clear(): void;
110
+ /** the invocation for this id. */
111
+ invocation(invocationId: string): InvocationState | null;
112
+ /** all invocations, or filter. active reads byTool (one live invo per tool). */
113
+ invocations(status?: InvocationStatus): InvocationState[];
114
+ }