bingocode 1.0.26 → 1.0.28

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 (65) hide show
  1. package/config/bingo-defaults/settings.json +2 -1
  2. package/package.json +1 -2
  3. package/src/server/services/providerService.ts +104 -0
  4. package/src/utils/managedEnv.ts +1 -17
  5. package/.github/FUNDING.yml +0 -1
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -44
  7. package/.github/ISSUE_TEMPLATE/config.yml +0 -1
  8. package/.github/ISSUE_TEMPLATE/question.md +0 -40
  9. package/.github/workflows/build-desktop-dev.yml +0 -210
  10. package/.github/workflows/deploy-docs.yml +0 -59
  11. package/.github/workflows/release-desktop.yml +0 -162
  12. package/.spine/user.yaml +0 -5
  13. package/.spine/workspace.yaml +0 -1
  14. package/adapters/common/__tests__/chat-queue.test.ts +0 -61
  15. package/adapters/common/__tests__/format.test.ts +0 -148
  16. package/adapters/common/__tests__/http-client.test.ts +0 -105
  17. package/adapters/common/__tests__/message-buffer.test.ts +0 -84
  18. package/adapters/common/__tests__/message-dedup.test.ts +0 -57
  19. package/adapters/common/__tests__/session-store.test.ts +0 -62
  20. package/adapters/common/__tests__/ws-bridge.test.ts +0 -177
  21. package/adapters/common/attachment/__tests__/attachment-limits.test.ts +0 -52
  22. package/adapters/common/attachment/__tests__/attachment-store.test.ts +0 -108
  23. package/adapters/common/attachment/__tests__/image-block-watcher.test.ts +0 -115
  24. package/adapters/feishu/__tests__/card-errors.test.ts +0 -194
  25. package/adapters/feishu/__tests__/cardkit.test.ts +0 -295
  26. package/adapters/feishu/__tests__/extract-payload.test.ts +0 -77
  27. package/adapters/feishu/__tests__/feishu.test.ts +0 -907
  28. package/adapters/feishu/__tests__/flush-controller.test.ts +0 -290
  29. package/adapters/feishu/__tests__/markdown-style.test.ts +0 -353
  30. package/adapters/feishu/__tests__/media.test.ts +0 -120
  31. package/adapters/feishu/__tests__/streaming-card.test.ts +0 -914
  32. package/adapters/telegram/__tests__/media.test.ts +0 -86
  33. package/adapters/telegram/__tests__/telegram.test.ts +0 -115
  34. package/adapters/tsconfig.json +0 -18
  35. package/bunfig.toml +0 -1
  36. package/preload.ts +0 -30
  37. package/scripts/count-app-loc.ts +0 -256
  38. package/scripts/release.ts +0 -130
  39. package/src/server/__tests__/conversation-service.test.ts +0 -173
  40. package/src/server/__tests__/conversations.test.ts +0 -458
  41. package/src/server/__tests__/cron-scheduler.test.ts +0 -575
  42. package/src/server/__tests__/e2e/business-flow.test.ts +0 -841
  43. package/src/server/__tests__/e2e/full-flow.test.ts +0 -357
  44. package/src/server/__tests__/fixtures/mock-sdk-cli.ts +0 -123
  45. package/src/server/__tests__/haha-oauth-api.test.ts +0 -146
  46. package/src/server/__tests__/haha-oauth-service.test.ts +0 -185
  47. package/src/server/__tests__/providers-real.test.ts +0 -244
  48. package/src/server/__tests__/providers.test.ts +0 -579
  49. package/src/server/__tests__/proxy-streaming.test.ts +0 -317
  50. package/src/server/__tests__/proxy-transform.test.ts +0 -469
  51. package/src/server/__tests__/real-llm-test.ts +0 -526
  52. package/src/server/__tests__/scheduled-tasks.test.ts +0 -371
  53. package/src/server/__tests__/sessions.test.ts +0 -786
  54. package/src/server/__tests__/settings.test.ts +0 -376
  55. package/src/server/__tests__/skills.test.ts +0 -125
  56. package/src/server/__tests__/tasks.test.ts +0 -171
  57. package/src/server/__tests__/team-watcher.test.ts +0 -400
  58. package/src/server/__tests__/teams.test.ts +0 -627
  59. package/src/server/middleware/cors.test.ts +0 -27
  60. package/src/utils/__tests__/cronFrequency.test.ts +0 -153
  61. package/src/utils/__tests__/cronTasks.test.ts +0 -204
  62. package/src/utils/computerUse/permissions.test.ts +0 -44
  63. package/stubs/ant-claude-for-chrome-mcp.ts +0 -24
  64. package/stubs/color-diff-napi.ts +0 -45
  65. package/tsconfig.json +0 -24
@@ -1,295 +0,0 @@
1
- /**
2
- * cardkit.ts 单元测试
3
- *
4
- * 不调用真实的 Lark API —— 用 mock client 捕获调用参数,验证:
5
- * - 每个函数构造的 payload 结构
6
- * - 非零 code 响应抛出 CardKitApiError(可被 card-errors 识别)
7
- * - 缺失关键字段时抛错
8
- * - sequence 正确传递
9
- */
10
-
11
- import { describe, it, expect } from 'bun:test'
12
- import {
13
- createCardEntity,
14
- sendCardAsMessage,
15
- streamCardContent,
16
- setCardStreamingMode,
17
- updateCardKitCard,
18
- CardKitApiError,
19
- STREAMING_ELEMENT_ID,
20
- } from '../cardkit.js'
21
- import { isCardRateLimitError, isCardTableLimitError } from '../card-errors.js'
22
-
23
- // ---------------------------------------------------------------------------
24
- // Mock client factory
25
- // ---------------------------------------------------------------------------
26
-
27
- type MockCall = { api: string; args: any }
28
-
29
- function makeMockClient(responses: Record<string, any>) {
30
- const calls: MockCall[] = []
31
- const recorder = (api: string, resp: any) => async (args: any) => {
32
- calls.push({ api, args })
33
- if (typeof resp === 'function') {
34
- return resp(args)
35
- }
36
- return resp
37
- }
38
- const client: any = {
39
- cardkit: {
40
- v1: {
41
- card: {
42
- create: recorder('cardkit.v1.card.create', responses['card.create']),
43
- settings: recorder('cardkit.v1.card.settings', responses['card.settings']),
44
- update: recorder('cardkit.v1.card.update', responses['card.update']),
45
- },
46
- cardElement: {
47
- content: recorder(
48
- 'cardkit.v1.cardElement.content',
49
- responses['cardElement.content'],
50
- ),
51
- },
52
- },
53
- },
54
- im: {
55
- message: {
56
- create: recorder('im.message.create', responses['im.message.create']),
57
- reply: recorder('im.message.reply', responses['im.message.reply']),
58
- },
59
- },
60
- }
61
- return { client, calls }
62
- }
63
-
64
- // ---------------------------------------------------------------------------
65
- // createCardEntity
66
- // ---------------------------------------------------------------------------
67
-
68
- describe('createCardEntity', () => {
69
- it('构造 card_json payload 并返回 card_id', async () => {
70
- const { client, calls } = makeMockClient({
71
- 'card.create': {
72
- code: 0,
73
- data: { card_id: 'ck_abc_123' },
74
- },
75
- })
76
- const card = { schema: '2.0', body: { elements: [] } }
77
- const id = await createCardEntity(client, card)
78
-
79
- expect(id).toBe('ck_abc_123')
80
- expect(calls.length).toBe(1)
81
- expect(calls[0]!.api).toBe('cardkit.v1.card.create')
82
- expect(calls[0]!.args.data.type).toBe('card_json')
83
- // data.data 应当是 card 的 JSON 字符串
84
- expect(calls[0]!.args.data.data).toBe(JSON.stringify(card))
85
- })
86
-
87
- it('兼容顶层 card_id(某些 SDK 包装层)', async () => {
88
- const { client } = makeMockClient({
89
- 'card.create': { code: 0, card_id: 'top_level_id' },
90
- })
91
- const id = await createCardEntity(client, {})
92
- expect(id).toBe('top_level_id')
93
- })
94
-
95
- it('non-zero code 抛 CardKitApiError', async () => {
96
- const { client } = makeMockClient({
97
- 'card.create': { code: 230099, msg: 'something failed' },
98
- })
99
- await expect(createCardEntity(client, {})).rejects.toThrow(CardKitApiError)
100
- })
101
-
102
- it('code=0 但缺 card_id 抛错', async () => {
103
- const { client } = makeMockClient({
104
- 'card.create': { code: 0, data: {} },
105
- })
106
- await expect(createCardEntity(client, {})).rejects.toThrow(/missing card_id/)
107
- })
108
- })
109
-
110
- // ---------------------------------------------------------------------------
111
- // sendCardAsMessage
112
- // ---------------------------------------------------------------------------
113
-
114
- describe('sendCardAsMessage', () => {
115
- it('无 replyTo: 走 im.message.create 使用 chat_id', async () => {
116
- const { client, calls } = makeMockClient({
117
- 'im.message.create': { data: { message_id: 'om_new_msg_1' } },
118
- })
119
- const mid = await sendCardAsMessage(client, 'oc_chat_123', 'ck_id_xyz')
120
- expect(mid).toBe('om_new_msg_1')
121
- expect(calls.length).toBe(1)
122
- expect(calls[0]!.api).toBe('im.message.create')
123
- expect(calls[0]!.args.params.receive_id_type).toBe('chat_id')
124
- expect(calls[0]!.args.data.receive_id).toBe('oc_chat_123')
125
- expect(calls[0]!.args.data.msg_type).toBe('interactive')
126
- // content 格式: {"type":"card","data":{"card_id":"xxx"}}
127
- const parsed = JSON.parse(calls[0]!.args.data.content)
128
- expect(parsed).toEqual({ type: 'card', data: { card_id: 'ck_id_xyz' } })
129
- })
130
-
131
- it('有 replyTo: 走 im.message.reply', async () => {
132
- const { client, calls } = makeMockClient({
133
- 'im.message.reply': { data: { message_id: 'om_reply_1' } },
134
- })
135
- const mid = await sendCardAsMessage(client, 'oc_chat_123', 'ck_id_xyz', 'om_parent')
136
- expect(mid).toBe('om_reply_1')
137
- expect(calls.length).toBe(1)
138
- expect(calls[0]!.api).toBe('im.message.reply')
139
- expect(calls[0]!.args.path.message_id).toBe('om_parent')
140
- const parsed = JSON.parse(calls[0]!.args.data.content)
141
- expect(parsed.data.card_id).toBe('ck_id_xyz')
142
- })
143
-
144
- it('缺 message_id 抛错', async () => {
145
- const { client } = makeMockClient({
146
- 'im.message.create': { data: {} },
147
- })
148
- await expect(sendCardAsMessage(client, 'c', 'ck')).rejects.toThrow(
149
- /missing message_id/,
150
- )
151
- })
152
- })
153
-
154
- // ---------------------------------------------------------------------------
155
- // streamCardContent
156
- // ---------------------------------------------------------------------------
157
-
158
- describe('streamCardContent', () => {
159
- it('构造 content + sequence payload,path 包含 card_id + element_id', async () => {
160
- const { client, calls } = makeMockClient({
161
- 'cardElement.content': { code: 0 },
162
- })
163
- await streamCardContent(client, 'ck_abc', STREAMING_ELEMENT_ID, 'hello', 42)
164
-
165
- expect(calls.length).toBe(1)
166
- expect(calls[0]!.api).toBe('cardkit.v1.cardElement.content')
167
- expect(calls[0]!.args.data).toEqual({ content: 'hello', sequence: 42 })
168
- expect(calls[0]!.args.path).toEqual({
169
- card_id: 'ck_abc',
170
- element_id: STREAMING_ELEMENT_ID,
171
- })
172
- })
173
-
174
- it('STREAMING_ELEMENT_ID 常量 = "streaming_content"', () => {
175
- expect(STREAMING_ELEMENT_ID).toBe('streaming_content')
176
- })
177
-
178
- it('230020 响应可被 isCardRateLimitError 识别', async () => {
179
- const { client } = makeMockClient({
180
- 'cardElement.content': { code: 230020, msg: 'rate limited' },
181
- })
182
- try {
183
- await streamCardContent(client, 'ck', 'el', 'x', 1)
184
- expect('should have thrown').toBe('but did not')
185
- } catch (err) {
186
- expect(err).toBeInstanceOf(CardKitApiError)
187
- expect(isCardRateLimitError(err)).toBe(true)
188
- }
189
- })
190
-
191
- it('230099 + table limit msg 可被 isCardTableLimitError 识别', async () => {
192
- const { client } = makeMockClient({
193
- 'cardElement.content': {
194
- code: 230099,
195
- msg: 'Failed to create card content, ext=ErrCode: 11310; ErrMsg: card table number over limit; ErrorValue: table; ',
196
- },
197
- })
198
- try {
199
- await streamCardContent(client, 'ck', 'el', 'x', 1)
200
- expect('should have thrown').toBe('but did not')
201
- } catch (err) {
202
- expect(err).toBeInstanceOf(CardKitApiError)
203
- expect(isCardTableLimitError(err)).toBe(true)
204
- }
205
- })
206
- })
207
-
208
- // ---------------------------------------------------------------------------
209
- // setCardStreamingMode
210
- // ---------------------------------------------------------------------------
211
-
212
- describe('setCardStreamingMode', () => {
213
- it('streaming_mode=false + sequence 正确传递', async () => {
214
- const { client, calls } = makeMockClient({
215
- 'card.settings': { code: 0 },
216
- })
217
- await setCardStreamingMode(client, 'ck_xxx', false, 99)
218
-
219
- expect(calls.length).toBe(1)
220
- expect(calls[0]!.api).toBe('cardkit.v1.card.settings')
221
- expect(calls[0]!.args.path).toEqual({ card_id: 'ck_xxx' })
222
- expect(calls[0]!.args.data.sequence).toBe(99)
223
- // settings 是 JSON 字符串
224
- const settings = JSON.parse(calls[0]!.args.data.settings)
225
- expect(settings).toEqual({ streaming_mode: false })
226
- })
227
-
228
- it('streaming_mode=true 也能工作', async () => {
229
- const { client, calls } = makeMockClient({
230
- 'card.settings': { code: 0 },
231
- })
232
- await setCardStreamingMode(client, 'ck', true, 1)
233
- const settings = JSON.parse(calls[0]!.args.data.settings)
234
- expect(settings).toEqual({ streaming_mode: true })
235
- })
236
- })
237
-
238
- // ---------------------------------------------------------------------------
239
- // updateCardKitCard
240
- // ---------------------------------------------------------------------------
241
-
242
- describe('updateCardKitCard', () => {
243
- it('把 card 包装成 card_json payload + sequence', async () => {
244
- const { client, calls } = makeMockClient({
245
- 'card.update': { code: 0 },
246
- })
247
- const card = { schema: '2.0', body: { elements: [{ tag: 'markdown', content: 'done' }] } }
248
- await updateCardKitCard(client, 'ck_final', card, 100)
249
-
250
- expect(calls.length).toBe(1)
251
- expect(calls[0]!.api).toBe('cardkit.v1.card.update')
252
- expect(calls[0]!.args.path).toEqual({ card_id: 'ck_final' })
253
- expect(calls[0]!.args.data.sequence).toBe(100)
254
- expect(calls[0]!.args.data.card.type).toBe('card_json')
255
- expect(calls[0]!.args.data.card.data).toBe(JSON.stringify(card))
256
- })
257
-
258
- it('非零 code 抛 CardKitApiError', async () => {
259
- const { client } = makeMockClient({
260
- 'card.update': { code: -1, msg: 'bad card' },
261
- })
262
- await expect(updateCardKitCard(client, 'ck', {}, 1)).rejects.toThrow(CardKitApiError)
263
- })
264
- })
265
-
266
- // ---------------------------------------------------------------------------
267
- // CardKitApiError
268
- // ---------------------------------------------------------------------------
269
-
270
- describe('CardKitApiError', () => {
271
- it('携带 code 和 msg,可被 parseCardApiError 识别', () => {
272
- const err = new CardKitApiError({
273
- api: 'card.update',
274
- code: 230020,
275
- msg: 'rate limited',
276
- context: 'seq=5',
277
- })
278
- expect(err.code).toBe(230020)
279
- expect(err.msg).toBe('rate limited')
280
- expect(err.name).toBe('CardKitApiError')
281
- expect(isCardRateLimitError(err)).toBe(true)
282
- })
283
-
284
- it('消息包含 api 名和 context', () => {
285
- const err = new CardKitApiError({
286
- api: 'cardElement.content',
287
- code: 230099,
288
- msg: 'oops',
289
- context: 'seq=3 len=100',
290
- })
291
- expect(err.message).toContain('cardElement.content')
292
- expect(err.message).toContain('230099')
293
- expect(err.message).toContain('seq=3 len=100')
294
- })
295
- })
@@ -1,77 +0,0 @@
1
- import { describe, it, expect } from 'bun:test'
2
- import { extractInboundPayload } from '../extract-payload.js'
3
-
4
- describe('extractInboundPayload', () => {
5
- it('pulls text out of a text message', () => {
6
- const result = extractInboundPayload(
7
- JSON.stringify({ text: 'hello world' }),
8
- 'text',
9
- )
10
- expect(result.text).toBe('hello world')
11
- expect(result.pendingDownloads).toEqual([])
12
- })
13
-
14
- it('pulls text out of a post (rich text) message', () => {
15
- const content = JSON.stringify({
16
- zh_cn: {
17
- content: [[{ tag: 'text', text: 'hi ' }, { tag: 'text', text: 'there' }]],
18
- },
19
- })
20
- const result = extractInboundPayload(content, 'post')
21
- expect(result.text).toBe('hi there')
22
- expect(result.pendingDownloads).toEqual([])
23
- })
24
-
25
- it('identifies an image message as a pending image download', () => {
26
- const content = JSON.stringify({ image_key: 'img_key_abc' })
27
- const result = extractInboundPayload(content, 'image')
28
- expect(result.text).toBe('')
29
- expect(result.pendingDownloads).toEqual([
30
- { kind: 'image', fileKey: 'img_key_abc' },
31
- ])
32
- })
33
-
34
- it('identifies a file message as a pending file download with file_name', () => {
35
- const content = JSON.stringify({
36
- file_key: 'file_key_xyz',
37
- file_name: 'spec.pdf',
38
- })
39
- const result = extractInboundPayload(content, 'file')
40
- expect(result.pendingDownloads).toEqual([
41
- { kind: 'file', fileKey: 'file_key_xyz', fileName: 'spec.pdf' },
42
- ])
43
- })
44
-
45
- it('identifies file_archive the same way as file', () => {
46
- const content = JSON.stringify({ file_key: 'fk1', file_name: 'x.zip' })
47
- const result = extractInboundPayload(content, 'file_archive')
48
- expect(result.pendingDownloads).toEqual([
49
- { kind: 'file', fileKey: 'fk1', fileName: 'x.zip' },
50
- ])
51
- })
52
-
53
- it('extracts img + file elements from a post message', () => {
54
- const content = JSON.stringify({
55
- zh_cn: {
56
- content: [
57
- [{ tag: 'text', text: 'look: ' }],
58
- [{ tag: 'img', image_key: 'img_post_1' }],
59
- [{ tag: 'text', text: ' and ' }],
60
- [{ tag: 'file', file_key: 'file_post_1', file_name: 'note.txt' }],
61
- ],
62
- },
63
- })
64
- const result = extractInboundPayload(content, 'post')
65
- expect(result.text).toBe('look: and ')
66
- expect(result.pendingDownloads).toEqual([
67
- { kind: 'image', fileKey: 'img_post_1' },
68
- { kind: 'file', fileKey: 'file_post_1', fileName: 'note.txt' },
69
- ])
70
- })
71
-
72
- it('returns empty on malformed JSON', () => {
73
- const result = extractInboundPayload('not json', 'text')
74
- expect(result.text).toBe('')
75
- expect(result.pendingDownloads).toEqual([])
76
- })
77
- })