@aaronsb/google-workspace-mcp 4.6.1 → 4.7.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.
- package/build/accounts/oauth.js +6 -1
- package/build/accounts/oauth.js.map +1 -1
- package/build/factory/manifest/gmail.yaml +211 -6
- package/build/factory/safety.js +35 -11
- package/build/factory/safety.js.map +1 -1
- package/build/server/call-context.d.ts +12 -0
- package/build/server/call-context.js +20 -0
- package/build/server/call-context.js.map +1 -0
- package/build/server/handler.d.ts +4 -0
- package/build/server/handler.js +9 -1
- package/build/server/handler.js.map +1 -1
- package/build/server/preview-gate.d.ts +36 -0
- package/build/server/preview-gate.js +105 -0
- package/build/server/preview-gate.js.map +1 -0
- package/build/services/gmail/filters.d.ts +88 -0
- package/build/services/gmail/filters.js +504 -0
- package/build/services/gmail/filters.js.map +1 -0
- package/build/services/gmail/hydrate.d.ts +29 -0
- package/build/services/gmail/hydrate.js +116 -0
- package/build/services/gmail/hydrate.js.map +1 -0
- package/build/services/gmail/patch.js +72 -116
- package/build/services/gmail/patch.js.map +1 -1
- package/build/version.d.ts +1 -1
- package/build/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preview gate for standing-effect writes (ADR-309).
|
|
3
|
+
*
|
|
4
|
+
* A preview mints a token over the exact payload it evaluated; the write refuses without
|
|
5
|
+
* one. The token is an HMAC under a key made at process start, so nothing is stored and
|
|
6
|
+
* nothing needs cleaning up. A restart invalidates every outstanding token, and the remedy
|
|
7
|
+
* is to preview again.
|
|
8
|
+
*
|
|
9
|
+
* A token proves the evaluation was returned to the caller. It does not prove a person
|
|
10
|
+
* read it; that is the MCP client's per-call permission prompt.
|
|
11
|
+
*/
|
|
12
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
13
|
+
import { currentCallId } from './call-context.js';
|
|
14
|
+
const KEY = randomBytes(32);
|
|
15
|
+
/** How long a preview stays good. Match counts and conflicts drift as mail arrives. */
|
|
16
|
+
export const PREVIEW_TTL_MS = 30 * 60_000;
|
|
17
|
+
/** Tolerated clock skew for a token that claims to come from the future. */
|
|
18
|
+
const SKEW_MS = 60_000;
|
|
19
|
+
/** JSON with object keys sorted at every depth, so equal payloads serialize equally. */
|
|
20
|
+
export function canonicalJson(value) {
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
23
|
+
if (value && typeof value === 'object') {
|
|
24
|
+
const entries = Object.entries(value)
|
|
25
|
+
.filter(([, v]) => v !== undefined)
|
|
26
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
27
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
28
|
+
}
|
|
29
|
+
return JSON.stringify(value);
|
|
30
|
+
}
|
|
31
|
+
function mac(headerPart, account, payload) {
|
|
32
|
+
return createHmac('sha256', KEY)
|
|
33
|
+
.update(headerPart).update('\n')
|
|
34
|
+
.update(account.toLowerCase()).update('\n')
|
|
35
|
+
.update(canonicalJson(payload))
|
|
36
|
+
.digest();
|
|
37
|
+
}
|
|
38
|
+
/** Mint a token for `kind`, bound to this account, this payload, now, and this call. */
|
|
39
|
+
export function mintPreviewToken(kind, account, payload, now = Date.now()) {
|
|
40
|
+
const header = { k: kind, t: now, c: currentCallId() };
|
|
41
|
+
const headerPart = Buffer.from(JSON.stringify(header)).toString('base64url');
|
|
42
|
+
return `${headerPart}.${mac(headerPart, account, payload).toString('base64url')}`;
|
|
43
|
+
}
|
|
44
|
+
/** Check a token against the write about to happen. */
|
|
45
|
+
export function checkPreviewToken(token, kind, account, payload, now = Date.now()) {
|
|
46
|
+
if (typeof token !== 'string' || token.trim() === '')
|
|
47
|
+
return { ok: false, failure: 'missing' };
|
|
48
|
+
const [headerPart, macPart, extra] = token.trim().split('.');
|
|
49
|
+
if (!headerPart || !macPart || extra !== undefined)
|
|
50
|
+
return { ok: false, failure: 'malformed' };
|
|
51
|
+
let header;
|
|
52
|
+
try {
|
|
53
|
+
header = JSON.parse(Buffer.from(headerPart, 'base64url').toString('utf-8'));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return { ok: false, failure: 'malformed' };
|
|
57
|
+
}
|
|
58
|
+
if (typeof header?.k !== 'string' || typeof header.t !== 'number' || typeof header.c !== 'number') {
|
|
59
|
+
return { ok: false, failure: 'malformed' };
|
|
60
|
+
}
|
|
61
|
+
// The kind is read before the MAC because sibling operations sign different payloads, so
|
|
62
|
+
// a token meant for one always fails the other's MAC and would be reported as a changed
|
|
63
|
+
// parameter. Reading the unverified header here can only choose which refusal to give;
|
|
64
|
+
// nothing in it is trusted until the MAC agrees.
|
|
65
|
+
if (header.k !== kind)
|
|
66
|
+
return { ok: false, failure: 'wrong-kind', issuedFor: header.k };
|
|
67
|
+
const expected = mac(headerPart, account, payload);
|
|
68
|
+
const given = Buffer.from(macPart, 'base64url');
|
|
69
|
+
const genuine = given.length === expected.length && timingSafeEqual(given, expected);
|
|
70
|
+
if (!genuine)
|
|
71
|
+
return { ok: false, failure: 'mismatch' };
|
|
72
|
+
if (header.t > now + SKEW_MS || now - header.t > PREVIEW_TTL_MS)
|
|
73
|
+
return { ok: false, failure: 'expired' };
|
|
74
|
+
if (header.c !== 0 && header.c === currentCallId())
|
|
75
|
+
return { ok: false, failure: 'same-call' };
|
|
76
|
+
return { ok: true };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The sentence a refused write returns.
|
|
80
|
+
*
|
|
81
|
+
* @param write The operation refused, e.g. `createFilter`.
|
|
82
|
+
* @param preview The operation that mints its token, e.g. `previewFilter`.
|
|
83
|
+
*/
|
|
84
|
+
export function describeTokenFailure(check, write, preview) {
|
|
85
|
+
const again = `Call \`${preview}\` with these exact parameters and pass the previewToken it returns.`;
|
|
86
|
+
switch (check.failure) {
|
|
87
|
+
case 'missing':
|
|
88
|
+
return `\`${write}\` needs a previewToken. It changes what happens to mail that has not arrived yet, ` +
|
|
89
|
+
`so it runs only after a preview of this exact change has been returned. ${again}`;
|
|
90
|
+
case 'malformed':
|
|
91
|
+
return `The previewToken is not one this server issued. ${again}`;
|
|
92
|
+
case 'wrong-kind':
|
|
93
|
+
return `This previewToken came from a preview for ${check.issuedFor ?? 'another operation'}, not \`${write}\`. ${again}`;
|
|
94
|
+
case 'mismatch':
|
|
95
|
+
return `This previewToken does not match this change on this account. Either a parameter changed since ` +
|
|
96
|
+
`the preview, the token came from another account, or the server restarted. ${again}`;
|
|
97
|
+
case 'expired':
|
|
98
|
+
return `This previewToken has expired; previews are good for ${PREVIEW_TTL_MS / 60_000} minutes because ` +
|
|
99
|
+
`what they match changes as mail arrives. ${again}`;
|
|
100
|
+
case 'same-call':
|
|
101
|
+
return `This previewToken was minted earlier in this same tool call, so the preview was never returned ` +
|
|
102
|
+
`before \`${write}\` ran. Run the preview as its own call, read it, then call \`${write}\`.`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=preview-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-gate.js","sourceRoot":"","sources":["../../src/server/preview-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;AAE5B,uFAAuF;AACvF,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,MAAM,CAAC;AAE1C,4EAA4E;AAC5E,MAAM,OAAO,GAAG,MAAM,CAAC;AAiBvB,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3E,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;aAClC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC9F,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,GAAG,CAAC,UAAkB,EAAE,OAAe,EAAE,OAAgB;IAChE,OAAO,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC;SAC7B,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;SAC/B,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;SAC1C,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;SAC9B,MAAM,EAAE,CAAC;AACd,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAe,EAAE,OAAgB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAChG,MAAM,MAAM,GAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,CAAC;IACpE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7E,OAAO,GAAG,UAAU,IAAI,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;AACpF,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,iBAAiB,CAC/B,KAAc,EACd,IAAY,EACZ,OAAe,EACf,OAAgB,EAChB,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAE/F,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAE/F,IAAI,MAAmB,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAgB,CAAC;IAC7F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7C,CAAC;IACD,IAAI,OAAO,MAAM,EAAE,CAAC,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAClG,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7C,CAAC;IAED,yFAAyF;IACzF,wFAAwF;IACxF,uFAAuF;IACvF,iDAAiD;IACjD,IAAI,MAAM,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;IACxF,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrF,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACxD,IAAI,MAAM,CAAC,CAAC,GAAG,GAAG,GAAG,OAAO,IAAI,GAAG,GAAG,MAAM,CAAC,CAAC,GAAG,cAAc;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAC1G,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,aAAa,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC/F,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAyC,EAAE,KAAa,EAAE,OAAe;IAC5G,MAAM,KAAK,GAAG,UAAU,OAAO,sEAAsE,CAAC;IACtG,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;QACtB,KAAK,SAAS;YACZ,OAAO,KAAK,KAAK,qFAAqF;gBACpG,2EAA2E,KAAK,EAAE,CAAC;QACvF,KAAK,WAAW;YACd,OAAO,mDAAmD,KAAK,EAAE,CAAC;QACpE,KAAK,YAAY;YACf,OAAO,6CAA6C,KAAK,CAAC,SAAS,IAAI,mBAAmB,WAAW,KAAK,OAAO,KAAK,EAAE,CAAC;QAC3H,KAAK,UAAU;YACb,OAAO,iGAAiG;gBACtG,8EAA8E,KAAK,EAAE,CAAC;QAC1F,KAAK,SAAS;YACZ,OAAO,wDAAwD,cAAc,GAAG,MAAM,mBAAmB;gBACvG,4CAA4C,KAAK,EAAE,CAAC;QACxD,KAAK,WAAW;YACd,OAAO,iGAAiG;gBACtG,YAAY,KAAK,iEAAiE,KAAK,KAAK,CAAC;IACnG,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail filters — routing applied to mail as it arrives. ADR-309, #202.
|
|
3
|
+
*
|
|
4
|
+
* A filter is a standing-effect write: it keeps acting after the call, on messages the
|
|
5
|
+
* call never saw. So the lifecycle is gated. `previewFilter` evaluates a filter against the
|
|
6
|
+
* mail already there and mints a token for that exact filter; `createFilter` and
|
|
7
|
+
* `updateFilter` refuse without it. Every action Gmail supports stays available, and the
|
|
8
|
+
* preview is where a dangerous one (trash, forward) is explained.
|
|
9
|
+
*
|
|
10
|
+
* Gmail has no `filters.update`. `updateFilter` creates the replacement first and deletes
|
|
11
|
+
* the original after, so a failure part-way leaves two filters, never none.
|
|
12
|
+
*
|
|
13
|
+
* Parameters are flat, like every other operation on this tool: criteria are `match*`,
|
|
14
|
+
* actions are `addLabelIds`, `removeLabelIds` and `forwardTo`.
|
|
15
|
+
*/
|
|
16
|
+
import { type HandlerResponse } from '../../server/formatting/markdown.js';
|
|
17
|
+
export interface FilterCriteria {
|
|
18
|
+
from?: string;
|
|
19
|
+
to?: string;
|
|
20
|
+
subject?: string;
|
|
21
|
+
query?: string;
|
|
22
|
+
negatedQuery?: string;
|
|
23
|
+
hasAttachment?: boolean;
|
|
24
|
+
excludeChats?: boolean;
|
|
25
|
+
size?: number;
|
|
26
|
+
sizeComparison?: 'larger' | 'smaller';
|
|
27
|
+
}
|
|
28
|
+
export interface FilterAction {
|
|
29
|
+
addLabelIds?: string[];
|
|
30
|
+
removeLabelIds?: string[];
|
|
31
|
+
forward?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Field names match Gmail's `Filter` resource, so a spec is also the request body. */
|
|
34
|
+
export interface FilterSpec {
|
|
35
|
+
criteria: FilterCriteria;
|
|
36
|
+
action: FilterAction;
|
|
37
|
+
}
|
|
38
|
+
/** A filter as Google returns it, before normalizing. */
|
|
39
|
+
type RawFilter = {
|
|
40
|
+
criteria?: Record<string, unknown>;
|
|
41
|
+
action?: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Reduce any filter, from our params or from Google, to one canonical spec, so two
|
|
45
|
+
* filters that do the same thing compare equal and a token minted over one matches the
|
|
46
|
+
* other. Empty strings, false booleans and empty label lists are absent.
|
|
47
|
+
*/
|
|
48
|
+
export declare function normalizeSpec(raw: RawFilter): FilterSpec;
|
|
49
|
+
/** Build and validate the filter a call describes. Throws a sentence naming what is missing. */
|
|
50
|
+
export declare function buildFilterSpec(params: Record<string, unknown>): FilterSpec;
|
|
51
|
+
/** The Gmail search a filter's criteria amount to. Filters and search share one syntax. */
|
|
52
|
+
export declare function criteriaToQuery(c: FilterCriteria): string;
|
|
53
|
+
/** The tool params that describe this filter, for recreating or editing it. */
|
|
54
|
+
export declare function specToParams(spec: FilterSpec): Record<string, string | number | boolean>;
|
|
55
|
+
interface Effect {
|
|
56
|
+
/** What happens, in a clause: "skips the inbox". */
|
|
57
|
+
text: string;
|
|
58
|
+
/** Why it is worth a second look, shown in full previews only. */
|
|
59
|
+
detail?: string;
|
|
60
|
+
/** A consequence worth a second look before approving: disposal, or mail leaving the account. */
|
|
61
|
+
warn?: boolean;
|
|
62
|
+
}
|
|
63
|
+
export declare function describeActions(action: FilterAction, labelNames: Map<string, string>): Effect[];
|
|
64
|
+
/** `filters` — every filter on the account, one line each. */
|
|
65
|
+
export declare function handleListFilters(_params: Record<string, unknown>, account: string): Promise<HandlerResponse>;
|
|
66
|
+
/** `getFilter` — one filter, in words, with the parameters that recreate or edit it. */
|
|
67
|
+
export declare function handleGetFilter(params: Record<string, unknown>, account: string): Promise<HandlerResponse>;
|
|
68
|
+
/**
|
|
69
|
+
* `previewFilter` — evaluate a filter without creating it, and mint the token the write
|
|
70
|
+
* needs. With `filterId`, evaluates replacing that filter and mints an update token.
|
|
71
|
+
*
|
|
72
|
+
* Problems that would make the write fail or do nothing (an unknown label, an unverified
|
|
73
|
+
* forwarding address, an exact duplicate) are errors and withhold the token. Consequences
|
|
74
|
+
* worth a second look are shown, not refused.
|
|
75
|
+
*/
|
|
76
|
+
export declare function handlePreviewFilter(params: Record<string, unknown>, account: string): Promise<HandlerResponse>;
|
|
77
|
+
/** `createFilter` — create a filter its preview has already shown. */
|
|
78
|
+
export declare function handleCreateFilter(params: Record<string, unknown>, account: string): Promise<HandlerResponse>;
|
|
79
|
+
/**
|
|
80
|
+
* `updateFilter` — replace a filter with the one its preview showed.
|
|
81
|
+
*
|
|
82
|
+
* The token covers the filter being replaced as it was at preview time, so a filter that
|
|
83
|
+
* changed in the meantime is not replaced on the strength of a preview of something else.
|
|
84
|
+
*/
|
|
85
|
+
export declare function handleUpdateFilter(params: Record<string, unknown>, account: string): Promise<HandlerResponse>;
|
|
86
|
+
/** `deleteFilter` — stop a filter, and return what it was so it can be put back. */
|
|
87
|
+
export declare function handleDeleteFilter(params: Record<string, unknown>, account: string): Promise<HandlerResponse>;
|
|
88
|
+
export {};
|