@gonvex/cli 0.4.1 → 0.5.1
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/README.md +18 -3
- package/dist/function-catalog.d.ts +19 -0
- package/dist/function-catalog.js +107 -0
- package/dist/function-catalog.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +47 -3
- package/dist/index.js.map +1 -1
- package/dist/manifest-types.d.ts +13 -0
- package/dist/module-artifact.d.ts +1 -1
- package/dist/module-artifact.js +66 -11
- package/dist/module-artifact.js.map +1 -1
- package/dist/templates/vite-react/agent-api.d.ts +77 -0
- package/dist/templates/vite-react/agent-api.ndjson +4 -0
- package/dist/templates/vite-react/gonvex/_build/module.js +172 -7
- package/dist/templates/vite-react/gonvex/_generated/api.ts +109 -2
- package/dist/templates/vite-react/gonvex/_generated/manifest.json +285 -11
- package/dist/templates/vite-react/gonvex/_generated/module.json +147 -8
- package/dist/templates/vite-react/gonvex/_generated/react.ts +1 -1
- package/dist/templates/vite-react/gonvex/index.ts +1 -1
- package/dist/templates/vite-react/gonvex/messages.ts +59 -6
- package/dist/templates/vite-react/migrations/0002_message_timestamp_default.sql +3 -0
- package/package.json +3 -3
|
@@ -122,6 +122,56 @@ const validateReplicaCollection = (value, path) => {
|
|
|
122
122
|
if (value.mode !== void 0 && value.mode !== "eager" && value.mode !== "progressive") throw new Error(`replica collection ${path} has an invalid completeness mode`);
|
|
123
123
|
if (value.orderDirection !== void 0 && value.orderDirection !== "asc" && value.orderDirection !== "desc") throw new Error(`replica collection ${path} has an invalid order direction`);
|
|
124
124
|
};
|
|
125
|
+
const validateActionCapabilities = (profile, value, path) => {
|
|
126
|
+
if (value === void 0) return;
|
|
127
|
+
if (!isRecord(value)) throw new Error(`action ${path} capabilities must be an object`);
|
|
128
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
129
|
+
"networkOrigins",
|
|
130
|
+
"secrets",
|
|
131
|
+
"tools",
|
|
132
|
+
"scheduler",
|
|
133
|
+
"storage",
|
|
134
|
+
"sandbox",
|
|
135
|
+
"functions"
|
|
136
|
+
]);
|
|
137
|
+
for (const field of Object.keys(value)) if (!allowed.has(field)) throw new Error(`action ${path} capabilities has unsupported field ${field}`);
|
|
138
|
+
if (value.networkOrigins !== void 0) {
|
|
139
|
+
if (!Array.isArray(value.networkOrigins) || value.networkOrigins.length === 0) throw new Error(`action ${path} networkOrigins must be a non-empty array`);
|
|
140
|
+
const seen = /* @__PURE__ */ new Set();
|
|
141
|
+
for (const origin of value.networkOrigins) {
|
|
142
|
+
if (typeof origin !== "string") throw new Error(`action ${path} networkOrigins must contain strings`);
|
|
143
|
+
let parsed;
|
|
144
|
+
try {
|
|
145
|
+
parsed = new URL(origin);
|
|
146
|
+
} catch {
|
|
147
|
+
throw new Error(`action ${path} network origin ${JSON.stringify(origin)} is invalid`);
|
|
148
|
+
}
|
|
149
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:" || parsed.origin !== origin || parsed.username || parsed.password) throw new Error(`action ${path} network origin ${JSON.stringify(origin)} must be an exact HTTP(S) origin`);
|
|
150
|
+
if (seen.has(origin)) throw new Error(`action ${path} declares duplicate network origin ${origin}`);
|
|
151
|
+
seen.add(origin);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (value.secrets !== void 0) {
|
|
155
|
+
if (!Array.isArray(value.secrets) || value.secrets.some((name) => typeof name !== "string" || !/^[A-Z][A-Z0-9_]*$/.test(name))) throw new Error(`action ${path} secrets must be uppercase environment names`);
|
|
156
|
+
}
|
|
157
|
+
if (value.tools !== void 0) {
|
|
158
|
+
if (profile !== "agent") throw new Error(`action ${path} tools require profile "agent"`);
|
|
159
|
+
if (!isRecord(value.tools) || Object.keys(value.tools).length === 0) throw new Error(`agent action ${path} tools must be a non-empty object`);
|
|
160
|
+
for (const [name, binding] of Object.entries(value.tools)) if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) || !isRecord(binding) || binding.kind !== "query" && binding.kind !== "reducer" || typeof binding.function !== "string" || !binding.function.trim()) throw new Error(`agent action ${path} has an invalid tool binding ${JSON.stringify(name)}`);
|
|
161
|
+
}
|
|
162
|
+
if (value.scheduler !== void 0 && value.scheduler !== true) throw new Error(`action ${path} scheduler must be true when declared`);
|
|
163
|
+
if (value.storage !== void 0 && value.storage !== true) throw new Error(`action ${path} storage must be true when declared`);
|
|
164
|
+
if (value.functions !== void 0) {
|
|
165
|
+
if (profile !== "agent") throw new Error(`action ${path} functions require profile "agent"`);
|
|
166
|
+
if (value.functions !== true) throw new Error(`action ${path} functions must be true when declared`);
|
|
167
|
+
}
|
|
168
|
+
if (value.sandbox !== void 0) {
|
|
169
|
+
if (profile !== "agent") throw new Error(`action ${path} sandbox requires profile "agent"`);
|
|
170
|
+
if (!isRecord(value.sandbox)) throw new Error(`action ${path} sandbox must be an object`);
|
|
171
|
+
for (const field of Object.keys(value.sandbox)) if (field !== "duckdb") throw new Error(`action ${path} sandbox has unsupported field ${field}`);
|
|
172
|
+
if (value.sandbox.duckdb !== void 0 && value.sandbox.duckdb !== true) throw new Error(`action ${path} sandbox.duckdb must be true when declared`);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
125
175
|
const validateStructuredQueryPlan = (value, path) => {
|
|
126
176
|
if (!isRecord(value) || typeof value.table !== "string" || !value.table.trim() || typeof value.key !== "string" || !value.key.trim() || !Array.isArray(value.columns) || value.columns.length === 0 || value.columns.some((column) => typeof column !== "string" || !column.trim())) throw new Error(`one-shot query ${path} requires a structured live query plan with a table, key, and columns`);
|
|
127
177
|
if (!value.columns.includes(value.key)) throw new Error(`one-shot query ${path} live query plan columns must include its key`);
|
|
@@ -246,24 +296,56 @@ const validateVisibilityPlan = (value, path) => {
|
|
|
246
296
|
const setPath = `${path}.sets.${name}`;
|
|
247
297
|
validateExactObject(candidate, setPath, [
|
|
248
298
|
"table",
|
|
299
|
+
"alias",
|
|
249
300
|
"select",
|
|
301
|
+
"selectFrom",
|
|
250
302
|
"joins",
|
|
251
303
|
"where"
|
|
252
304
|
]);
|
|
253
305
|
requireVisibilityString(candidate.table, `${setPath}.table`);
|
|
306
|
+
if (candidate.alias !== void 0) requireVisibilityString(candidate.alias, `${setPath}.alias`);
|
|
254
307
|
requireVisibilityString(candidate.select, `${setPath}.select`);
|
|
308
|
+
if (candidate.selectFrom !== void 0) requireVisibilityString(candidate.selectFrom, `${setPath}.selectFrom`);
|
|
255
309
|
if (!Array.isArray(candidate.joins)) throw new Error(`${setPath}.joins must be an array`);
|
|
256
|
-
candidate.joins
|
|
310
|
+
const joins = candidate.joins;
|
|
311
|
+
joins.forEach((join, index) => {
|
|
257
312
|
const joinPath = `${setPath}.joins[${index}]`;
|
|
258
313
|
validateExactObject(join, joinPath, [
|
|
259
314
|
"table",
|
|
315
|
+
"alias",
|
|
316
|
+
"leftAlias",
|
|
260
317
|
"leftColumn",
|
|
261
318
|
"rightColumn"
|
|
262
319
|
]);
|
|
263
320
|
requireVisibilityString(join.table, `${joinPath}.table`);
|
|
321
|
+
if (join.alias !== void 0) requireVisibilityString(join.alias, `${joinPath}.alias`);
|
|
322
|
+
if (join.leftAlias !== void 0) requireVisibilityString(join.leftAlias, `${joinPath}.leftAlias`);
|
|
264
323
|
requireVisibilityString(join.leftColumn, `${joinPath}.leftColumn`);
|
|
265
324
|
requireVisibilityString(join.rightColumn, `${joinPath}.rightColumn`);
|
|
266
325
|
});
|
|
326
|
+
const occurrences = [{
|
|
327
|
+
table: candidate.table,
|
|
328
|
+
alias: candidate.alias ?? candidate.table,
|
|
329
|
+
explicit: candidate.alias !== void 0
|
|
330
|
+
}, ...joins.map((join) => ({
|
|
331
|
+
table: join.table,
|
|
332
|
+
alias: join.alias ?? join.table,
|
|
333
|
+
explicit: join.alias !== void 0
|
|
334
|
+
}))];
|
|
335
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
336
|
+
const tableCounts = /* @__PURE__ */ new Map();
|
|
337
|
+
for (const occurrence of occurrences) tableCounts.set(occurrence.table, (tableCounts.get(occurrence.table) ?? 0) + 1);
|
|
338
|
+
occurrences.forEach((occurrence, index) => {
|
|
339
|
+
if ((tableCounts.get(occurrence.table) ?? 0) > 1 && !occurrence.explicit) throw new Error(`${setPath} repeats table ${occurrence.table}; every occurrence requires an explicit alias`);
|
|
340
|
+
if (aliases.has(occurrence.alias)) throw new Error(`${setPath} repeats logical alias ${occurrence.alias}`);
|
|
341
|
+
if (index > 0) {
|
|
342
|
+
const leftAlias = joins[index - 1].leftAlias;
|
|
343
|
+
if (leftAlias !== void 0 && !aliases.has(leftAlias)) throw new Error(`${setPath}.joins[${index - 1}].leftAlias must reference an earlier occurrence`);
|
|
344
|
+
}
|
|
345
|
+
aliases.add(occurrence.alias);
|
|
346
|
+
});
|
|
347
|
+
const selectFrom = candidate.selectFrom ?? occurrences[0].alias;
|
|
348
|
+
if (!aliases.has(selectFrom)) throw new Error(`${setPath}.selectFrom references unknown alias ${selectFrom}`);
|
|
267
349
|
if (!Array.isArray(candidate.where)) throw new Error(`${setPath}.where must be an array`);
|
|
268
350
|
candidate.where.forEach((constraint, index) => {
|
|
269
351
|
const constraintPath = `${setPath}.where[${index}]`;
|
|
@@ -273,6 +355,7 @@ const validateVisibilityPlan = (value, path) => {
|
|
|
273
355
|
"context"
|
|
274
356
|
]);
|
|
275
357
|
requireVisibilityString(constraint.table, `${constraintPath}.table`);
|
|
358
|
+
if (!aliases.has(constraint.table)) throw new Error(`${constraintPath}.table references unknown alias ${constraint.table}`);
|
|
276
359
|
requireVisibilityString(constraint.column, `${constraintPath}.column`);
|
|
277
360
|
validateVisibilityContext(constraint.context, `${constraintPath}.context`);
|
|
278
361
|
});
|
|
@@ -320,6 +403,7 @@ const queryDefinition = (options, deliveryOverride) => {
|
|
|
320
403
|
}
|
|
321
404
|
return freeze({
|
|
322
405
|
kind: "query",
|
|
406
|
+
internal: options.internal,
|
|
323
407
|
delivery,
|
|
324
408
|
liveQueryPlan,
|
|
325
409
|
replica,
|
|
@@ -327,6 +411,10 @@ const queryDefinition = (options, deliveryOverride) => {
|
|
|
327
411
|
handler: options.run
|
|
328
412
|
});
|
|
329
413
|
};
|
|
414
|
+
/** Declare an executable one-shot, live, or replica query export. */
|
|
415
|
+
function query(options = {}) {
|
|
416
|
+
return queryDefinition(options);
|
|
417
|
+
}
|
|
330
418
|
/** Declare an executable live query export with a structured live plan. */
|
|
331
419
|
function liveQuery(options = {}) {
|
|
332
420
|
return queryDefinition({
|
|
@@ -350,6 +438,15 @@ const reducerDefinition = (options, internal = false) => {
|
|
|
350
438
|
function reducer(options) {
|
|
351
439
|
return reducerDefinition(options);
|
|
352
440
|
}
|
|
441
|
+
/** Declare an executable action export. */
|
|
442
|
+
function action(options = {}) {
|
|
443
|
+
validateActionCapabilities(options.profile ?? "standard", options.capabilities, options.name?.trim() || "<export>");
|
|
444
|
+
return freeze({
|
|
445
|
+
kind: "action",
|
|
446
|
+
options: executableOptions(options),
|
|
447
|
+
handler: options.run
|
|
448
|
+
});
|
|
449
|
+
}
|
|
353
450
|
//#endregion
|
|
354
451
|
//#region gonvex/messages.ts
|
|
355
452
|
const messagesVisibility = visibility({
|
|
@@ -383,7 +480,44 @@ const list = liveQuery({
|
|
|
383
480
|
},
|
|
384
481
|
run: async () => []
|
|
385
482
|
});
|
|
483
|
+
const get = query({
|
|
484
|
+
interactive: true,
|
|
485
|
+
description: "Read one visible message by ID.",
|
|
486
|
+
agent: {
|
|
487
|
+
tags: ["messages"],
|
|
488
|
+
confirmation: "none"
|
|
489
|
+
},
|
|
490
|
+
args: schema.object({ id: schema.id("messages") }),
|
|
491
|
+
result: schema.array(schema.object({
|
|
492
|
+
id: schema.id("messages"),
|
|
493
|
+
body: schema.string(),
|
|
494
|
+
author: schema.string(),
|
|
495
|
+
created_at: schema.datetime()
|
|
496
|
+
})),
|
|
497
|
+
liveQueryPlan: {
|
|
498
|
+
table: "messages",
|
|
499
|
+
key: "id",
|
|
500
|
+
columns: [
|
|
501
|
+
"id",
|
|
502
|
+
"body",
|
|
503
|
+
"author",
|
|
504
|
+
"created_at"
|
|
505
|
+
],
|
|
506
|
+
where: {
|
|
507
|
+
operator: "eq",
|
|
508
|
+
column: "id",
|
|
509
|
+
value: { argument: "id" }
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
run: async () => []
|
|
513
|
+
});
|
|
386
514
|
const send = reducer({
|
|
515
|
+
interactive: true,
|
|
516
|
+
description: "Send a message as the acting tenant member.",
|
|
517
|
+
agent: {
|
|
518
|
+
tags: ["messages"],
|
|
519
|
+
confirmation: "none"
|
|
520
|
+
},
|
|
387
521
|
args: schema.object({ body: schema.string() }),
|
|
388
522
|
result: schema.object({
|
|
389
523
|
id: schema.id("messages"),
|
|
@@ -396,12 +530,43 @@ const send = reducer({
|
|
|
396
530
|
reason: "server assigns the message id"
|
|
397
531
|
},
|
|
398
532
|
nonOptimisticReason: "server assigns the message id",
|
|
399
|
-
run: async ({
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
533
|
+
run: async ({ db, member }, args) => {
|
|
534
|
+
if (!member || member.status !== "active") throw new Error("active tenant membership required");
|
|
535
|
+
return db.insert("messages", {
|
|
536
|
+
id: crypto.randomUUID(),
|
|
537
|
+
body: args.body,
|
|
538
|
+
author: member.displayName ?? member.id
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
const echo = action({
|
|
543
|
+
interactive: true,
|
|
544
|
+
description: "Return the supplied message without changing durable state.",
|
|
545
|
+
agent: {
|
|
546
|
+
tags: ["messages"],
|
|
547
|
+
confirmation: "none"
|
|
548
|
+
},
|
|
549
|
+
args: schema.object({ message: schema.string() }),
|
|
550
|
+
result: schema.object({ message: schema.string() }),
|
|
551
|
+
run: async (_ctx, args) => args
|
|
552
|
+
});
|
|
553
|
+
/** Minimal reference for testing permission-equivalent delegated invocation. */
|
|
554
|
+
const agentInvoke = action({
|
|
555
|
+
profile: "agent",
|
|
556
|
+
interactive: false,
|
|
557
|
+
description: "Invoke one function from the active interactive catalog.",
|
|
558
|
+
capabilities: { functions: true },
|
|
559
|
+
args: schema.object({
|
|
560
|
+
path: schema.string(),
|
|
561
|
+
args: schema.any(),
|
|
562
|
+
artifactHash: schema.string()
|
|
563
|
+
}),
|
|
564
|
+
result: schema.any(),
|
|
565
|
+
run: (ctx, args) => ctx.functions.invoke({
|
|
566
|
+
path: args.path,
|
|
567
|
+
args: args.args,
|
|
568
|
+
artifactHash: args.artifactHash
|
|
404
569
|
})
|
|
405
570
|
});
|
|
406
571
|
//#endregion
|
|
407
|
-
export { list, messagesVisibility, send };
|
|
572
|
+
export { agentInvoke, echo, get, list, messagesVisibility, send };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by gonvex dev. Do not edit.
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { control as gonvexControl, type LiveQueryPlan } from "@gonvex/client";
|
|
4
4
|
|
|
5
5
|
export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
|
|
6
6
|
export type FunctionKind = "query" | "reducer" | "action";
|
|
@@ -22,6 +22,27 @@ export type FunctionReference<Kind extends FunctionKind = FunctionKind, Args = J
|
|
|
22
22
|
readonly optimistic?: { readonly transaction?: OptimisticTransactionDefinition };
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
export type MessagesAgentInvokeArgs = {
|
|
26
|
+
path: string;
|
|
27
|
+
args: JsonValue;
|
|
28
|
+
artifactHash: string;
|
|
29
|
+
};
|
|
30
|
+
export type MessagesAgentInvokeResult = JsonValue;
|
|
31
|
+
export type MessagesEchoArgs = {
|
|
32
|
+
message: string;
|
|
33
|
+
};
|
|
34
|
+
export type MessagesEchoResult = {
|
|
35
|
+
message: string;
|
|
36
|
+
};
|
|
37
|
+
export type MessagesGetArgs = {
|
|
38
|
+
id: string;
|
|
39
|
+
};
|
|
40
|
+
export type MessagesGetResult = Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
body: string;
|
|
43
|
+
author: string;
|
|
44
|
+
created_at: string;
|
|
45
|
+
}>;
|
|
25
46
|
export type MessagesListArgs = { };
|
|
26
47
|
export type MessagesListResult = Array<{
|
|
27
48
|
id: string;
|
|
@@ -41,6 +62,83 @@ export type MessagesSendResult = {
|
|
|
41
62
|
|
|
42
63
|
export const api = {
|
|
43
64
|
messages: {
|
|
65
|
+
agentInvoke: {
|
|
66
|
+
args: {
|
|
67
|
+
fields: {
|
|
68
|
+
args: {
|
|
69
|
+
kind: "any",
|
|
70
|
+
},
|
|
71
|
+
artifactHash: {
|
|
72
|
+
kind: "string",
|
|
73
|
+
},
|
|
74
|
+
path: {
|
|
75
|
+
kind: "string",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
kind: "object",
|
|
79
|
+
},
|
|
80
|
+
kind: "action",
|
|
81
|
+
path: "messages.agentInvoke",
|
|
82
|
+
result: {
|
|
83
|
+
kind: "any",
|
|
84
|
+
},
|
|
85
|
+
} as unknown as FunctionReference<"action", MessagesAgentInvokeArgs, MessagesAgentInvokeResult>,
|
|
86
|
+
echo: {
|
|
87
|
+
args: {
|
|
88
|
+
fields: {
|
|
89
|
+
message: {
|
|
90
|
+
kind: "string",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
kind: "object",
|
|
94
|
+
},
|
|
95
|
+
kind: "action",
|
|
96
|
+
path: "messages.echo",
|
|
97
|
+
result: {
|
|
98
|
+
fields: {
|
|
99
|
+
message: {
|
|
100
|
+
kind: "string",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
kind: "object",
|
|
104
|
+
},
|
|
105
|
+
} as unknown as FunctionReference<"action", MessagesEchoArgs, MessagesEchoResult>,
|
|
106
|
+
get: {
|
|
107
|
+
args: {
|
|
108
|
+
fields: {
|
|
109
|
+
id: {
|
|
110
|
+
entity: "messages",
|
|
111
|
+
kind: "id",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
kind: "object",
|
|
115
|
+
},
|
|
116
|
+
delivery: "oneShot",
|
|
117
|
+
kind: "query",
|
|
118
|
+
path: "messages.get",
|
|
119
|
+
result: {
|
|
120
|
+
items: {
|
|
121
|
+
fields: {
|
|
122
|
+
author: {
|
|
123
|
+
kind: "string",
|
|
124
|
+
},
|
|
125
|
+
body: {
|
|
126
|
+
kind: "string",
|
|
127
|
+
},
|
|
128
|
+
created_at: {
|
|
129
|
+
format: "datetime",
|
|
130
|
+
kind: "string",
|
|
131
|
+
},
|
|
132
|
+
id: {
|
|
133
|
+
entity: "messages",
|
|
134
|
+
kind: "id",
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
kind: "object",
|
|
138
|
+
},
|
|
139
|
+
kind: "array",
|
|
140
|
+
},
|
|
141
|
+
} as unknown as FunctionReference<"query", MessagesGetArgs, MessagesGetResult>,
|
|
44
142
|
list: {
|
|
45
143
|
args: {
|
|
46
144
|
fields: {
|
|
@@ -128,18 +226,27 @@ export const api = {
|
|
|
128
226
|
},
|
|
129
227
|
} as const;
|
|
130
228
|
|
|
131
|
-
export const
|
|
229
|
+
export const control = gonvexControl;
|
|
230
|
+
|
|
231
|
+
export const internal = {
|
|
232
|
+
} as const;
|
|
132
233
|
export type Api = typeof api;
|
|
133
234
|
|
|
134
235
|
export const optimisticTransactions: Record<string, OptimisticTransactionDefinition> = {
|
|
135
236
|
};
|
|
136
237
|
|
|
137
238
|
export type ApiArgs = {
|
|
239
|
+
"messages.agentInvoke": MessagesAgentInvokeArgs;
|
|
240
|
+
"messages.echo": MessagesEchoArgs;
|
|
241
|
+
"messages.get": MessagesGetArgs;
|
|
138
242
|
"messages.list": MessagesListArgs;
|
|
139
243
|
"messages.send": MessagesSendArgs;
|
|
140
244
|
};
|
|
141
245
|
|
|
142
246
|
export type ApiResults = {
|
|
247
|
+
"messages.agentInvoke": MessagesAgentInvokeResult;
|
|
248
|
+
"messages.echo": MessagesEchoResult;
|
|
249
|
+
"messages.get": MessagesGetResult;
|
|
143
250
|
"messages.list": MessagesListResult;
|
|
144
251
|
"messages.send": MessagesSendResult;
|
|
145
252
|
};
|