@hedeqiang/dsh-nl2sql 0.11.1 → 0.12.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/CHANGELOG.md +66 -31
- package/README.md +144 -58
- package/README.zh-CN.md +175 -0
- package/channels/dingtalk.js +3 -3
- package/channels/index.js +1 -1
- package/channels/lark.js +35 -7
- package/channels/message.js +19 -2
- package/channels/telegram.js +20 -4
- package/channels/wecom.js +2 -2
- package/im.js +1 -1
- package/index.js +3 -1
- package/lark-bot.js +1 -1
- package/lib/client.js +188 -28
- package/package.json +8 -4
- package/service/access.js +38 -1
- package/service/bots.js +77 -10
- package/service/channels.js +23 -7
- package/service/push.js +2 -2
- package/service/suggestions.js +1 -1
- package/README.en.md +0 -63
- /package/docs/{CHANNELS.md → channels.md} +0 -0
package/channels/lark.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lark / Feishu channel adapter (see docs/
|
|
2
|
+
* Lark / Feishu channel adapter (see docs/channels.md).
|
|
3
3
|
*
|
|
4
4
|
* Kinds:
|
|
5
5
|
* - lark_webhook: a group custom bot (webhook URL + optional signing secret).
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { createHash, createHmac } from 'node:crypto'
|
|
18
18
|
import { ChannelError, request } from './http.js'
|
|
19
|
-
import { MD_TABLE, cellText, chartOption, chartable, dualAxis, kpiText, isKpi, num, parseMdTable, renderChartPng, resultMeta } from './message.js'
|
|
19
|
+
import { MD_TABLE, cellText, chartOption, chartable, dualAxis, kpiText, isKpi, needsAttachment, num, omittedNote, parseMdTable, renderChartPng, resultMeta } from './message.js'
|
|
20
20
|
|
|
21
21
|
export { ChannelError, ChannelError as ImError, chartOption, chartable, dualAxis, renderChartPng }
|
|
22
22
|
|
|
@@ -677,6 +677,18 @@ async function uploadImage(channel, secrets, png) {
|
|
|
677
677
|
return json.data.image_key
|
|
678
678
|
}
|
|
679
679
|
|
|
680
|
+
/** Upload a file (≤ 30 MB) for a file message; returns file_key. https://open.feishu.cn/document/server-docs/im-v1/file/create */
|
|
681
|
+
async function uploadFile(channel, secrets, buf, fileName, fileType = 'xls') {
|
|
682
|
+
const token = await pushToken(channel, secrets)
|
|
683
|
+
const form = new FormData()
|
|
684
|
+
form.set('file_type', fileType)
|
|
685
|
+
form.set('file_name', fileName)
|
|
686
|
+
form.set('file', new Blob([buf], { type: 'application/octet-stream' }), fileName)
|
|
687
|
+
const { status, json } = await request(`${hostFor(channel)}/open-apis/im/v1/files`, { method: 'POST', headers: { authorization: `Bearer ${token}` }, body: form, timeoutMs: 60000, label: '飞书' })
|
|
688
|
+
if (json.code !== 0) throw larkPushError(json, status)
|
|
689
|
+
return json.data.file_key
|
|
690
|
+
}
|
|
691
|
+
|
|
680
692
|
// ===========================================================================
|
|
681
693
|
// Cards (JSON 2.0)
|
|
682
694
|
// ===========================================================================
|
|
@@ -687,6 +699,7 @@ function tableElement(result, maxRows) {
|
|
|
687
699
|
const cols = result.columns.slice(0, 8)
|
|
688
700
|
return {
|
|
689
701
|
tag: 'table',
|
|
702
|
+
// page_size is 1..10 per the card spec; more rows are paged in the client.
|
|
690
703
|
page_size: Math.min(10, Math.max(1, Math.min(maxRows, result.rows.length))),
|
|
691
704
|
row_height: 'low',
|
|
692
705
|
header_style: { background_style: 'grey', bold: true, text_size: 'normal' },
|
|
@@ -820,7 +833,7 @@ export function actionElement(spec) {
|
|
|
820
833
|
* Build a JSON 2.0 card from an OutboundMessage.
|
|
821
834
|
* Results may carry `imageKey` (uploaded PNG) and `actions` (ActionSpec; legacy name `cardActions`).
|
|
822
835
|
*/
|
|
823
|
-
export function buildCard(message, { maxRows =
|
|
836
|
+
export function buildCard(message, { maxRows = 50, charts = true, actions = true } = {}) {
|
|
824
837
|
const elements = [...markdownElements(message.text)]
|
|
825
838
|
let tables = elements.filter(e => e.tag === 'table').length
|
|
826
839
|
let chartCount = 0
|
|
@@ -844,8 +857,12 @@ export function buildCard(message, { maxRows = 10, charts = true, actions = true
|
|
|
844
857
|
} else if (tables < 5) {
|
|
845
858
|
elements.push(tableElement(r, maxRows))
|
|
846
859
|
tables++
|
|
860
|
+
const note = omittedNote(r, Math.min(maxRows, r.rows.length), { attached: Boolean(r.attached) })
|
|
861
|
+
if (note) elements.push({ tag: 'markdown', content: `<font color='grey'>${note}</font>`, text_size: 'notation' })
|
|
847
862
|
} else {
|
|
848
863
|
elements.push({ tag: 'markdown', content: r.rows.slice(0, 5).map(row => row.slice(0, 6).map(v => cellText(v)).join(' | ')).join('\n') })
|
|
864
|
+
const note = omittedNote(r, Math.min(5, r.rows.length), { attached: Boolean(r.attached) })
|
|
865
|
+
if (note) elements.push({ tag: 'markdown', content: `<font color='grey'>${note}</font>`, text_size: 'notation' })
|
|
849
866
|
}
|
|
850
867
|
const spec = r.actions ?? r.cardActions
|
|
851
868
|
if (spec && actions) {
|
|
@@ -863,7 +880,7 @@ export function buildCard(message, { maxRows = 10, charts = true, actions = true
|
|
|
863
880
|
}
|
|
864
881
|
// Card bodies are limited to ~30 KB: shrink tables/charts until it fits.
|
|
865
882
|
const size = Buffer.byteLength(JSON.stringify(card))
|
|
866
|
-
if (size > CARD_LIMIT && maxRows >
|
|
883
|
+
if (size > CARD_LIMIT && maxRows > 5) return buildCard(message, { maxRows: Math.max(5, Math.floor(maxRows / 2)), charts, actions })
|
|
867
884
|
if (size > CARD_LIMIT && charts) return buildCard(message, { maxRows, charts: false, actions })
|
|
868
885
|
if (size > CARD_LIMIT) {
|
|
869
886
|
card.body.elements = elements.filter(e => e.tag !== 'table')
|
|
@@ -915,6 +932,7 @@ export async function sendMessage(channel, secrets, message) {
|
|
|
915
932
|
}
|
|
916
933
|
results.push(r)
|
|
917
934
|
}
|
|
935
|
+
if (channel.type === 'lark_app' && message.attachment?.buffer) for (let i = 0; i < results.length; i++) if (message.attachment.resultIndexes?.includes(i)) results[i] = { ...results[i], attached: true }
|
|
918
936
|
const card = buildCard({ ...message, results }, { charts: message.chartMode !== 'image' && message.chartMode !== 'table' })
|
|
919
937
|
if (channel.type === 'lark_webhook') {
|
|
920
938
|
if (!secrets.webhookUrl) throw new ChannelError('im/config', '缺少 Webhook 地址')
|
|
@@ -944,7 +962,17 @@ export async function sendMessage(channel, secrets, message) {
|
|
|
944
962
|
if (json.code !== 0 && !fatal.includes(json.code) && card.body.elements.some(e => e.tag === 'chart')) ({ status, json } = await send(buildCard({ ...message, results }, { charts: false })))
|
|
945
963
|
if (json.code !== 0 && !fatal.includes(json.code)) ({ status, json } = await send(legacyCard(card)))
|
|
946
964
|
if (json.code !== 0) throw larkPushError(json, status)
|
|
947
|
-
|
|
965
|
+
// Full data as an Excel file message after the card (app bots only; webhooks cannot send files).
|
|
966
|
+
let files = 0
|
|
967
|
+
if (message.attachment?.buffer) {
|
|
968
|
+
try {
|
|
969
|
+
const fileKey = await uploadFile(channel, secrets, message.attachment.buffer, message.attachment.fileName)
|
|
970
|
+
const fr = await postJson(url, { receive_id: channel.receiveId, msg_type: 'file', content: JSON.stringify({ file_key: fileKey }) }, { authorization: `Bearer ${token}` })
|
|
971
|
+
if (fr.json.code === 0) files++
|
|
972
|
+
else message.attachmentError = larkPushError(fr.json, fr.status).message
|
|
973
|
+
} catch (error) { message.attachmentError = error.message }
|
|
974
|
+
}
|
|
975
|
+
return { messageId: json.data?.message_id, images, files, ...(message.attachmentError ? { warning: `完整数据附件发送失败:${message.attachmentError}` } : {}) }
|
|
948
976
|
}
|
|
949
977
|
throw new ChannelError('im/config', `不支持的渠道类型:${channel.type}`)
|
|
950
978
|
}
|
|
@@ -1104,7 +1132,7 @@ export const kinds = [
|
|
|
1104
1132
|
{ key: 'webhookUrl', label: { zh: 'Webhook 地址', en: 'Webhook URL' }, type: 'secret', secret: true, required: true, placeholder: 'https://open.feishu.cn/open-apis/bot/v2/hook/…', pattern: WEBHOOK_RE.source },
|
|
1105
1133
|
{ key: 'signSecret', label: { zh: '签名密钥', en: 'Signing secret' }, type: 'secret', secret: true, help: { zh: '机器人开启「签名校验」时填写', en: 'Only when signature verification is enabled' } },
|
|
1106
1134
|
],
|
|
1107
|
-
capabilities: { push: true, markdown: 'full', charts: 'native', chartModes: ['native', 'table'], tables: 'native', buttons: false, bot: false, listTargets: false, mentionAll: true, maxMessageBytes: 28000 },
|
|
1135
|
+
capabilities: { push: true, markdown: 'full', charts: 'native', chartModes: ['native', 'table'], tables: 'native', tableRows: 50, files: false, buttons: false, bot: false, listTargets: false, mentionAll: true, maxMessageBytes: 28000 },
|
|
1108
1136
|
setupSteps: WEBHOOK_SETUP,
|
|
1109
1137
|
validate(input, existing) {
|
|
1110
1138
|
const config = {}
|
|
@@ -1141,7 +1169,7 @@ export const kinds = [
|
|
|
1141
1169
|
] },
|
|
1142
1170
|
{ key: 'receiveId', label: { zh: '接收者 ID', en: 'Receiver ID' }, type: 'text', required: true, placeholder: 'oc_…', target: true, help: { zh: '推送目标;选择群时可点「列出群聊」', en: 'Push target; use "List chats" to pick a chat' } },
|
|
1143
1171
|
],
|
|
1144
|
-
capabilities: { push: true, markdown: 'full', charts: 'native', chartImages: true, chartModes: ['native', 'image', 'table'], tables: 'native', buttons: true, bot: true, listTargets: true, mentionAll: true, maxMessageBytes: 28000 },
|
|
1172
|
+
capabilities: { push: true, markdown: 'full', charts: 'native', chartImages: true, chartModes: ['native', 'image', 'table'], tables: 'native', tableRows: 50, files: true, buttons: true, bot: true, listTargets: true, mentionAll: true, maxMessageBytes: 28000 },
|
|
1145
1173
|
setupSteps: APP_SETUP,
|
|
1146
1174
|
validate(input, existing) {
|
|
1147
1175
|
const c = input.config ?? {}
|
package/channels/message.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* OutboundMessage = { title, text?, results?, footer?, includeSql?, chartMode?, mentionAll?, template? }
|
|
5
5
|
* ResultBlock = { title, columns:[{name,kind}], rows, rowCount, truncated, chart, sql?, filters?, error?, actions? }
|
|
6
|
-
* (see docs/
|
|
6
|
+
* (see docs/channels.md)
|
|
7
7
|
*/
|
|
8
8
|
import * as echarts from 'echarts'
|
|
9
9
|
import { Resvg } from '@resvg/resvg-js'
|
|
@@ -42,6 +42,23 @@ export function isKpi(result) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** "12 行(已截断) · 已筛选:…" */
|
|
45
|
+
/**
|
|
46
|
+
* One line telling the reader how much of a result the message shows, or ''
|
|
47
|
+
* when everything is shown. `attached` = a full-data file follows.
|
|
48
|
+
*/
|
|
49
|
+
export function omittedNote(result, shown, { attached = false } = {}) {
|
|
50
|
+
const total = result.rowCount ?? result.rows?.length ?? 0
|
|
51
|
+
if (shown >= total && !result.truncated) return ''
|
|
52
|
+
const base = shown < total ? `共 ${total} 行,消息中显示前 ${shown} 行` : `查询结果已按行数上限截断(${total} 行)`
|
|
53
|
+
return attached ? `${base},完整数据见附件 Excel` : `${base};完整数据可在数据问答页面查看或导出 Excel`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Results whose rows do not all fit in a message (candidates for a full-data attachment). */
|
|
57
|
+
export function needsAttachment(result, shown) {
|
|
58
|
+
if (!result || result.error || !result.rows?.length) return false
|
|
59
|
+
return (result.rowCount ?? result.rows.length) > shown
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
export function resultMeta(result) {
|
|
46
63
|
return `${result.rowCount ?? result.rows?.length ?? 0} 行${result.truncated ? '(已截断)' : ''}${result.filters?.length ? ` · 已筛选:${result.filters.map(f => f.description).join(';')}` : ''}`
|
|
47
64
|
}
|
|
@@ -204,7 +221,7 @@ export function resultAsMarkdownTable(result, maxRows = 10, { maxCols = 8 } = {}
|
|
|
204
221
|
...rows.map(r => `| ${cols.map((c, i) => mdCell(c.kind === 'number' && Number.isFinite(Number(r[i])) && r[i] !== null && r[i] !== '' ? Number(r[i]).toLocaleString('en-US', { maximumFractionDigits: 4 }) : r[i])).join(' | ')} |`),
|
|
205
222
|
]
|
|
206
223
|
const total = result.rowCount ?? result.rows?.length ?? 0
|
|
207
|
-
if (total > rows.length) lines.push('', `…共 ${total} 行,仅显示前 ${rows.length}
|
|
224
|
+
if (total > rows.length) lines.push('', `…共 ${total} 行,仅显示前 ${rows.length} 行${result.attached ? ',完整数据见附件 Excel' : ''}`)
|
|
208
225
|
return lines.join('\n')
|
|
209
226
|
}
|
|
210
227
|
|
package/channels/telegram.js
CHANGED
|
@@ -280,7 +280,7 @@ export function inlineKeyboard(spec) {
|
|
|
280
280
|
|
|
281
281
|
// ------------------------------------------------------------ rendering
|
|
282
282
|
|
|
283
|
-
function resultMarkdown(r, { includeSql, maxRows =
|
|
283
|
+
function resultMarkdown(r, { includeSql, maxRows = 20 } = {}) {
|
|
284
284
|
const head = `**${r.title || '查询结果'}** ${resultMeta(r)}`
|
|
285
285
|
if (r.error) return `${head}\n${r.error}`
|
|
286
286
|
const lines = [head]
|
|
@@ -353,6 +353,16 @@ function createSender(api) {
|
|
|
353
353
|
return msg?.message_id
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
async function sendDocument(chatId, buf, fileName, { caption, threadId } = {}) {
|
|
357
|
+
const form = new FormData()
|
|
358
|
+
form.append('chat_id', String(chatId))
|
|
359
|
+
form.append('document', new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), fileName)
|
|
360
|
+
if (caption) form.append('caption', caption.slice(0, 1024))
|
|
361
|
+
if (threadId) form.append('message_thread_id', String(threadId))
|
|
362
|
+
const msg = await api.call('sendDocument', undefined, { form, timeoutMs: 60000 })
|
|
363
|
+
return msg?.message_id
|
|
364
|
+
}
|
|
365
|
+
|
|
356
366
|
/** Deliver an OutboundMessage. Returns { messageId, images }. */
|
|
357
367
|
async function deliver(chatId, message, { replyTo, threadId, renderChartPng = defaultRenderChartPng, logger, buttons = true } = {}) {
|
|
358
368
|
let images = 0
|
|
@@ -376,10 +386,16 @@ function createSender(api) {
|
|
|
376
386
|
messageId ??= id
|
|
377
387
|
reply = undefined
|
|
378
388
|
}
|
|
379
|
-
|
|
389
|
+
// Full data as an Excel document (Bot API sendDocument, ≤ 50 MB).
|
|
390
|
+
let files = 0
|
|
391
|
+
let warning
|
|
392
|
+
if (message.attachment?.buffer) {
|
|
393
|
+
try { await sendDocument(chatId, message.attachment.buffer, message.attachment.fileName, { caption: '完整数据', threadId }); files++ } catch (error) { warning = `完整数据附件发送失败:${error.message}` }
|
|
394
|
+
}
|
|
395
|
+
return { messageId: messageId === undefined ? undefined : String(messageId), images, files, ...(warning ? { warning } : {}) }
|
|
380
396
|
}
|
|
381
397
|
|
|
382
|
-
return { sendText, sendPhoto, deliver }
|
|
398
|
+
return { sendText, sendPhoto, sendDocument, deliver }
|
|
383
399
|
}
|
|
384
400
|
|
|
385
401
|
// ------------------------------------------------------------ validate
|
|
@@ -627,7 +643,7 @@ const telegramBot = {
|
|
|
627
643
|
{ key: 'chatId', type: 'text', required: false, target: true, label: { zh: 'Chat ID(推送目标)', en: 'Chat ID (push target)' }, placeholder: '-1001234567890 / @mychannel', pattern: CHAT_RE.source, help: { zh: '群组/超级群为负数(通常以 -100 开头),公开频道可填 @用户名;仅作机器人使用时可留空', en: 'Negative number for groups (usually -100…), or @username for public channels; may be empty for bot-only use' } },
|
|
628
644
|
{ key: 'apiBase', type: 'url', required: false, label: { zh: 'API 地址(可选)', en: 'API base URL (optional)' }, placeholder: DEFAULT_API_BASE, help: { zh: '自建 Bot API 服务器或反向代理地址;留空使用官方 api.telegram.org', en: 'Self-hosted Bot API server or proxy; empty = api.telegram.org' } },
|
|
629
645
|
],
|
|
630
|
-
capabilities: { push: true, markdown: 'basic', charts: 'image', chartModes: ['image', 'table'], tables: 'text', buttons: true, bot: true, listTargets: false, mentionAll: false, maxMessageBytes: TEXT_LIMIT },
|
|
646
|
+
capabilities: { push: true, markdown: 'basic', charts: 'image', chartModes: ['image', 'table'], tables: 'text', tableRows: 20, files: true, buttons: true, bot: true, listTargets: false, mentionAll: false, maxMessageBytes: TEXT_LIMIT },
|
|
631
647
|
setupSteps: {
|
|
632
648
|
zh: [
|
|
633
649
|
'在 Telegram 中打开 @BotFather,发送 /newbot 按提示创建机器人,复制 Bot Token。',
|
package/channels/wecom.js
CHANGED
|
@@ -522,7 +522,7 @@ const webhookKind = {
|
|
|
522
522
|
fields: [
|
|
523
523
|
{ key: 'webhookUrl', type: 'secret', secret: true, required: true, label: { zh: 'Webhook 地址', en: 'Webhook URL' }, placeholder: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=…', pattern: WEBHOOK_RE.source, help: { zh: '群聊 → 右上角 … → 消息推送 → 添加,复制 Webhook 地址', en: 'Group chat → … → Message push → Add, copy the webhook URL' } },
|
|
524
524
|
],
|
|
525
|
-
capabilities: { push: true, markdown: 'basic', charts: 'image', chartModes: ['image', 'table'], tables: 'markdown', buttons: false, bot: false, listTargets: false, mentionAll: true, maxMessageBytes: WEBHOOK_LIMIT },
|
|
525
|
+
capabilities: { push: true, markdown: 'basic', charts: 'image', chartModes: ['image', 'table'], tables: 'markdown', tableRows: 10, files: false, buttons: false, bot: false, listTargets: false, mentionAll: true, maxMessageBytes: WEBHOOK_LIMIT },
|
|
526
526
|
setupSteps: {
|
|
527
527
|
zh: [
|
|
528
528
|
'在企业微信群聊中点击右上角「…」→「消息推送」(旧版为「群机器人」)→「添加」,创建后复制 Webhook 地址。',
|
|
@@ -553,7 +553,7 @@ const aibotKind = {
|
|
|
553
553
|
{ key: 'chatId', type: 'text', required: false, label: { zh: '推送目标会话 ID(可选)', en: 'Push target chat id (optional)' }, help: { zh: '群聊 chatid(来自机器人收到的消息)或成员 userid;会话需先给机器人发过消息', en: 'Group chatid (from a received message) or member userid; the chat must have messaged the bot first' } },
|
|
554
554
|
{ key: 'chatType', type: 'select', required: false, label: { zh: '会话类型', en: 'Chat type' }, options: [{ value: 'group', label: { zh: '群聊', en: 'Group' } }, { value: 'single', label: { zh: '单聊', en: 'Single' } }] },
|
|
555
555
|
],
|
|
556
|
-
capabilities: { push: true, markdown: 'full', charts: 'none', chartModes: ['table'], tables: 'markdown', buttons: false, bot: true, listTargets: false, mentionAll: false, maxMessageBytes: AIBOT_LIMIT },
|
|
556
|
+
capabilities: { push: true, markdown: 'full', charts: 'none', chartModes: ['table'], tables: 'markdown', tableRows: 10, files: false, buttons: false, bot: true, listTargets: false, mentionAll: false, maxMessageBytes: AIBOT_LIMIT },
|
|
557
557
|
setupSteps: {
|
|
558
558
|
zh: [
|
|
559
559
|
'企业微信管理后台 → 应用管理 → 智能机器人 → 创建(或选择)机器人 → 开启「API 模式」并选择「长连接」。',
|
package/im.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Compatibility shim: IM delivery moved to channels/ (see docs/
|
|
2
|
+
* Compatibility shim: IM delivery moved to channels/ (see docs/channels.md).
|
|
3
3
|
* Platform-neutral helpers live in channels/message.js, Lark cards and push in
|
|
4
4
|
* channels/lark.js. `ImError` is the generic ChannelError.
|
|
5
5
|
*/
|
package/index.js
CHANGED
|
@@ -310,6 +310,7 @@ function registerSendTool(ctx, service) {
|
|
|
310
310
|
chartMode: { type: 'string', enum: ['native', 'image', 'table'], description: 'How attached results render: native card charts, PNG images, or tables. Omit to use the channel default; unsupported modes fall back to it.' },
|
|
311
311
|
resultIds: { type: 'array', items: { type: 'string' }, description: 'resultIds from nl2sql_query / nl2sql_chart / nl2sql_overview to include.' },
|
|
312
312
|
includeSql: { type: 'boolean', description: 'Also include each result\'s SQL.' },
|
|
313
|
+
attachFull: { type: 'boolean', description: 'Where the channel can send files (Lark app bot, Telegram), results with more rows than the message shows get the full data attached as an Excel file. Default true.' },
|
|
313
314
|
},
|
|
314
315
|
additionalProperties: false,
|
|
315
316
|
},
|
|
@@ -318,7 +319,7 @@ function registerSendTool(ctx, service) {
|
|
|
318
319
|
execute: async (args, exec) => {
|
|
319
320
|
try {
|
|
320
321
|
if (args.action === 'list') return { channels: service.listChannels().map(c => ({ ...pick(c, ['id', 'name', 'type', 'platform', 'description', 'isDefault']), ...(c.capabilities ? { capabilities: pick(c.capabilities, ['markdown', 'charts', 'tables', 'chartModes']) } : {}) })) }
|
|
321
|
-
return await service.sendToChannel({ channelId: args.channel, title: args.title, text: args.text, resultIds: args.resultIds ?? [], includeSql: args.includeSql, chartMode: args.chartMode, source: 'agent', sessionId: exec.agent?.id })
|
|
322
|
+
return await service.sendToChannel({ channelId: args.channel, title: args.title, text: args.text, resultIds: args.resultIds ?? [], includeSql: args.includeSql, attachFull: args.attachFull, chartMode: args.chartMode, source: 'agent', sessionId: exec.agent?.id })
|
|
322
323
|
} catch (error) {
|
|
323
324
|
throw error instanceof UserError ? new Error(error.message) : error
|
|
324
325
|
}
|
|
@@ -495,6 +496,7 @@ function registerRpc(ctx, service) {
|
|
|
495
496
|
ackSchemaChange: (p) => service.ackSchemaChange(p),
|
|
496
497
|
checkAllSchemas: () => service.checkAllSchemas(),
|
|
497
498
|
listUsers: () => service.listUsers(),
|
|
499
|
+
listSeenIdentities: (p) => service.listSeenIdentities(p ?? {}),
|
|
498
500
|
saveUser: (p) => service.saveUser(p),
|
|
499
501
|
deleteUser: (p) => service.deleteUser(p.id),
|
|
500
502
|
usageByActor: (p) => service.usageByActor(p?.days ?? 30),
|
package/lark-bot.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compatibility shim: the Lark long connection and OpenAPI helpers moved to
|
|
3
|
-
* channels/lark.js (see docs/
|
|
3
|
+
* channels/lark.js (see docs/channels.md).
|
|
4
4
|
*/
|
|
5
5
|
export {
|
|
6
6
|
addReaction, clearTenantTokenCache, createDedupe, createLarkBot, getBotInfo, getChatInfo, getTenantToken, getUserInfo,
|