shuvmaki 0.4.26
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/bin.js +70 -0
- package/dist/ai-tool-to-genai.js +210 -0
- package/dist/ai-tool-to-genai.test.js +267 -0
- package/dist/channel-management.js +97 -0
- package/dist/cli.js +709 -0
- package/dist/commands/abort.js +78 -0
- package/dist/commands/add-project.js +98 -0
- package/dist/commands/agent.js +152 -0
- package/dist/commands/ask-question.js +183 -0
- package/dist/commands/create-new-project.js +78 -0
- package/dist/commands/fork.js +186 -0
- package/dist/commands/model.js +313 -0
- package/dist/commands/permissions.js +126 -0
- package/dist/commands/queue.js +129 -0
- package/dist/commands/resume.js +145 -0
- package/dist/commands/session.js +142 -0
- package/dist/commands/share.js +80 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +161 -0
- package/dist/commands/user-command.js +145 -0
- package/dist/database.js +184 -0
- package/dist/discord-bot.js +384 -0
- package/dist/discord-utils.js +217 -0
- package/dist/escape-backticks.test.js +410 -0
- package/dist/format-tables.js +96 -0
- package/dist/format-tables.test.js +418 -0
- package/dist/genai-worker-wrapper.js +109 -0
- package/dist/genai-worker.js +297 -0
- package/dist/genai.js +232 -0
- package/dist/interaction-handler.js +144 -0
- package/dist/logger.js +51 -0
- package/dist/markdown.js +310 -0
- package/dist/markdown.test.js +262 -0
- package/dist/message-formatting.js +273 -0
- package/dist/message-formatting.test.js +73 -0
- package/dist/openai-realtime.js +228 -0
- package/dist/opencode.js +216 -0
- package/dist/session-handler.js +580 -0
- package/dist/system-message.js +61 -0
- package/dist/tools.js +356 -0
- package/dist/utils.js +85 -0
- package/dist/voice-handler.js +541 -0
- package/dist/voice.js +314 -0
- package/dist/worker-types.js +4 -0
- package/dist/xml.js +92 -0
- package/dist/xml.test.js +32 -0
- package/package.json +60 -0
- package/src/__snapshots__/compact-session-context-no-system.md +35 -0
- package/src/__snapshots__/compact-session-context.md +47 -0
- package/src/ai-tool-to-genai.test.ts +296 -0
- package/src/ai-tool-to-genai.ts +255 -0
- package/src/channel-management.ts +161 -0
- package/src/cli.ts +1010 -0
- package/src/commands/abort.ts +94 -0
- package/src/commands/add-project.ts +139 -0
- package/src/commands/agent.ts +201 -0
- package/src/commands/ask-question.ts +276 -0
- package/src/commands/create-new-project.ts +111 -0
- package/src/commands/fork.ts +257 -0
- package/src/commands/model.ts +402 -0
- package/src/commands/permissions.ts +146 -0
- package/src/commands/queue.ts +181 -0
- package/src/commands/resume.ts +230 -0
- package/src/commands/session.ts +184 -0
- package/src/commands/share.ts +96 -0
- package/src/commands/types.ts +25 -0
- package/src/commands/undo-redo.ts +213 -0
- package/src/commands/user-command.ts +178 -0
- package/src/database.ts +220 -0
- package/src/discord-bot.ts +513 -0
- package/src/discord-utils.ts +282 -0
- package/src/escape-backticks.test.ts +447 -0
- package/src/format-tables.test.ts +440 -0
- package/src/format-tables.ts +110 -0
- package/src/genai-worker-wrapper.ts +160 -0
- package/src/genai-worker.ts +366 -0
- package/src/genai.ts +321 -0
- package/src/interaction-handler.ts +187 -0
- package/src/logger.ts +57 -0
- package/src/markdown.test.ts +358 -0
- package/src/markdown.ts +365 -0
- package/src/message-formatting.test.ts +81 -0
- package/src/message-formatting.ts +340 -0
- package/src/openai-realtime.ts +363 -0
- package/src/opencode.ts +277 -0
- package/src/session-handler.ts +758 -0
- package/src/system-message.ts +62 -0
- package/src/tools.ts +428 -0
- package/src/utils.ts +118 -0
- package/src/voice-handler.ts +760 -0
- package/src/voice.ts +432 -0
- package/src/worker-types.ts +66 -0
- package/src/xml.test.ts +37 -0
- package/src/xml.ts +121 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { test, expect } from 'vitest';
|
|
2
|
+
import { formatMarkdownTables } from './format-tables.js';
|
|
3
|
+
test('formats simple table', () => {
|
|
4
|
+
const input = `| Name | Age |
|
|
5
|
+
| --- | --- |
|
|
6
|
+
| Alice | 30 |
|
|
7
|
+
| Bob | 25 |`;
|
|
8
|
+
const result = formatMarkdownTables(input);
|
|
9
|
+
expect(result).toMatchInlineSnapshot(`
|
|
10
|
+
"\`\`\`
|
|
11
|
+
Name Age
|
|
12
|
+
----- ---
|
|
13
|
+
Alice 30
|
|
14
|
+
Bob 25
|
|
15
|
+
\`\`\`
|
|
16
|
+
"
|
|
17
|
+
`);
|
|
18
|
+
});
|
|
19
|
+
test('formats table with varying column widths', () => {
|
|
20
|
+
const input = `| Item | Quantity | Price |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| Apples | 10 | $5 |
|
|
23
|
+
| Oranges | 3 | $2 |
|
|
24
|
+
| Bananas with long name | 100 | $15.99 |`;
|
|
25
|
+
const result = formatMarkdownTables(input);
|
|
26
|
+
expect(result).toMatchInlineSnapshot(`
|
|
27
|
+
"\`\`\`
|
|
28
|
+
Item Quantity Price
|
|
29
|
+
---------------------- -------- ------
|
|
30
|
+
Apples 10 $5
|
|
31
|
+
Oranges 3 $2
|
|
32
|
+
Bananas with long name 100 $15.99
|
|
33
|
+
\`\`\`
|
|
34
|
+
"
|
|
35
|
+
`);
|
|
36
|
+
});
|
|
37
|
+
test('strips bold formatting from cells', () => {
|
|
38
|
+
const input = `| Header | Value |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| **Bold text** | Normal |
|
|
41
|
+
| Mixed **bold** text | Another |`;
|
|
42
|
+
const result = formatMarkdownTables(input);
|
|
43
|
+
expect(result).toMatchInlineSnapshot(`
|
|
44
|
+
"\`\`\`
|
|
45
|
+
Header Value
|
|
46
|
+
--------------- -------
|
|
47
|
+
Bold text Normal
|
|
48
|
+
Mixed bold text Another
|
|
49
|
+
\`\`\`
|
|
50
|
+
"
|
|
51
|
+
`);
|
|
52
|
+
});
|
|
53
|
+
test('strips italic formatting from cells', () => {
|
|
54
|
+
const input = `| Header | Value |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| *Italic text* | Normal |
|
|
57
|
+
| _Also italic_ | Another |`;
|
|
58
|
+
const result = formatMarkdownTables(input);
|
|
59
|
+
expect(result).toMatchInlineSnapshot(`
|
|
60
|
+
"\`\`\`
|
|
61
|
+
Header Value
|
|
62
|
+
----------- -------
|
|
63
|
+
Italic text Normal
|
|
64
|
+
Also italic Another
|
|
65
|
+
\`\`\`
|
|
66
|
+
"
|
|
67
|
+
`);
|
|
68
|
+
});
|
|
69
|
+
test('extracts URL from links', () => {
|
|
70
|
+
const input = `| Name | Link |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| Google | [Click here](https://google.com) |
|
|
73
|
+
| GitHub | [GitHub Home](https://github.com) |`;
|
|
74
|
+
const result = formatMarkdownTables(input);
|
|
75
|
+
expect(result).toMatchInlineSnapshot(`
|
|
76
|
+
"\`\`\`
|
|
77
|
+
Name Link
|
|
78
|
+
------ ------------------
|
|
79
|
+
Google https://google.com
|
|
80
|
+
GitHub https://github.com
|
|
81
|
+
\`\`\`
|
|
82
|
+
"
|
|
83
|
+
`);
|
|
84
|
+
});
|
|
85
|
+
test('handles inline code in cells', () => {
|
|
86
|
+
const input = `| Function | Description |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| \`console.log\` | Logs to console |
|
|
89
|
+
| \`Array.map\` | Maps array items |`;
|
|
90
|
+
const result = formatMarkdownTables(input);
|
|
91
|
+
expect(result).toMatchInlineSnapshot(`
|
|
92
|
+
"\`\`\`
|
|
93
|
+
Function Description
|
|
94
|
+
----------- ----------------
|
|
95
|
+
console.log Logs to console
|
|
96
|
+
Array.map Maps array items
|
|
97
|
+
\`\`\`
|
|
98
|
+
"
|
|
99
|
+
`);
|
|
100
|
+
});
|
|
101
|
+
test('handles mixed formatting in single cell', () => {
|
|
102
|
+
const input = `| Description |
|
|
103
|
+
| --- |
|
|
104
|
+
| This has **bold**, *italic*, and \`code\` |
|
|
105
|
+
| Also [a link](https://example.com) here |`;
|
|
106
|
+
const result = formatMarkdownTables(input);
|
|
107
|
+
expect(result).toMatchInlineSnapshot(`
|
|
108
|
+
"\`\`\`
|
|
109
|
+
Description
|
|
110
|
+
-------------------------------
|
|
111
|
+
This has bold, italic, and code
|
|
112
|
+
Also https://example.com here
|
|
113
|
+
\`\`\`
|
|
114
|
+
"
|
|
115
|
+
`);
|
|
116
|
+
});
|
|
117
|
+
test('handles strikethrough text', () => {
|
|
118
|
+
const input = `| Status | Item |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| Done | ~~Deleted item~~ |
|
|
121
|
+
| Active | Normal item |`;
|
|
122
|
+
const result = formatMarkdownTables(input);
|
|
123
|
+
expect(result).toMatchInlineSnapshot(`
|
|
124
|
+
"\`\`\`
|
|
125
|
+
Status Item
|
|
126
|
+
------ ------------
|
|
127
|
+
Done Deleted item
|
|
128
|
+
Active Normal item
|
|
129
|
+
\`\`\`
|
|
130
|
+
"
|
|
131
|
+
`);
|
|
132
|
+
});
|
|
133
|
+
test('preserves content before table', () => {
|
|
134
|
+
const input = `Here is some text before the table.
|
|
135
|
+
|
|
136
|
+
| Col A | Col B |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| 1 | 2 |`;
|
|
139
|
+
const result = formatMarkdownTables(input);
|
|
140
|
+
expect(result).toMatchInlineSnapshot(`
|
|
141
|
+
"Here is some text before the table.
|
|
142
|
+
|
|
143
|
+
\`\`\`
|
|
144
|
+
Col A Col B
|
|
145
|
+
----- -----
|
|
146
|
+
1 2
|
|
147
|
+
\`\`\`
|
|
148
|
+
"
|
|
149
|
+
`);
|
|
150
|
+
});
|
|
151
|
+
test('preserves content after table', () => {
|
|
152
|
+
const input = `| Col A | Col B |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| 1 | 2 |
|
|
155
|
+
|
|
156
|
+
And here is text after.`;
|
|
157
|
+
const result = formatMarkdownTables(input);
|
|
158
|
+
expect(result).toMatchInlineSnapshot(`
|
|
159
|
+
"\`\`\`
|
|
160
|
+
Col A Col B
|
|
161
|
+
----- -----
|
|
162
|
+
1 2
|
|
163
|
+
\`\`\`
|
|
164
|
+
And here is text after."
|
|
165
|
+
`);
|
|
166
|
+
});
|
|
167
|
+
test('preserves content before and after table', () => {
|
|
168
|
+
const input = `Some intro text.
|
|
169
|
+
|
|
170
|
+
| Name | Value |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| Key | 123 |
|
|
173
|
+
|
|
174
|
+
Some outro text.`;
|
|
175
|
+
const result = formatMarkdownTables(input);
|
|
176
|
+
expect(result).toMatchInlineSnapshot(`
|
|
177
|
+
"Some intro text.
|
|
178
|
+
|
|
179
|
+
\`\`\`
|
|
180
|
+
Name Value
|
|
181
|
+
---- -----
|
|
182
|
+
Key 123
|
|
183
|
+
\`\`\`
|
|
184
|
+
Some outro text."
|
|
185
|
+
`);
|
|
186
|
+
});
|
|
187
|
+
test('handles multiple tables in same content', () => {
|
|
188
|
+
const input = `First table:
|
|
189
|
+
|
|
190
|
+
| A | B |
|
|
191
|
+
| --- | --- |
|
|
192
|
+
| 1 | 2 |
|
|
193
|
+
|
|
194
|
+
Some text between.
|
|
195
|
+
|
|
196
|
+
Second table:
|
|
197
|
+
|
|
198
|
+
| X | Y | Z |
|
|
199
|
+
| --- | --- | --- |
|
|
200
|
+
| a | b | c |`;
|
|
201
|
+
const result = formatMarkdownTables(input);
|
|
202
|
+
expect(result).toMatchInlineSnapshot(`
|
|
203
|
+
"First table:
|
|
204
|
+
|
|
205
|
+
\`\`\`
|
|
206
|
+
A B
|
|
207
|
+
- -
|
|
208
|
+
1 2
|
|
209
|
+
\`\`\`
|
|
210
|
+
Some text between.
|
|
211
|
+
|
|
212
|
+
Second table:
|
|
213
|
+
|
|
214
|
+
\`\`\`
|
|
215
|
+
X Y Z
|
|
216
|
+
- - -
|
|
217
|
+
a b c
|
|
218
|
+
\`\`\`
|
|
219
|
+
"
|
|
220
|
+
`);
|
|
221
|
+
});
|
|
222
|
+
test('handles empty cells', () => {
|
|
223
|
+
const input = `| Name | Optional |
|
|
224
|
+
| --- | --- |
|
|
225
|
+
| Alice | |
|
|
226
|
+
| | Bob |
|
|
227
|
+
| | |`;
|
|
228
|
+
const result = formatMarkdownTables(input);
|
|
229
|
+
expect(result).toMatchInlineSnapshot(`
|
|
230
|
+
"\`\`\`
|
|
231
|
+
Name Optional
|
|
232
|
+
----- --------
|
|
233
|
+
Alice
|
|
234
|
+
Bob
|
|
235
|
+
|
|
236
|
+
\`\`\`
|
|
237
|
+
"
|
|
238
|
+
`);
|
|
239
|
+
});
|
|
240
|
+
test('handles single column table', () => {
|
|
241
|
+
const input = `| Items |
|
|
242
|
+
| --- |
|
|
243
|
+
| Apple |
|
|
244
|
+
| Banana |
|
|
245
|
+
| Cherry |`;
|
|
246
|
+
const result = formatMarkdownTables(input);
|
|
247
|
+
expect(result).toMatchInlineSnapshot(`
|
|
248
|
+
"\`\`\`
|
|
249
|
+
Items
|
|
250
|
+
------
|
|
251
|
+
Apple
|
|
252
|
+
Banana
|
|
253
|
+
Cherry
|
|
254
|
+
\`\`\`
|
|
255
|
+
"
|
|
256
|
+
`);
|
|
257
|
+
});
|
|
258
|
+
test('handles single row table', () => {
|
|
259
|
+
const input = `| A | B | C | D |
|
|
260
|
+
| --- | --- | --- | --- |
|
|
261
|
+
| 1 | 2 | 3 | 4 |`;
|
|
262
|
+
const result = formatMarkdownTables(input);
|
|
263
|
+
expect(result).toMatchInlineSnapshot(`
|
|
264
|
+
"\`\`\`
|
|
265
|
+
A B C D
|
|
266
|
+
- - - -
|
|
267
|
+
1 2 3 4
|
|
268
|
+
\`\`\`
|
|
269
|
+
"
|
|
270
|
+
`);
|
|
271
|
+
});
|
|
272
|
+
test('handles nested formatting', () => {
|
|
273
|
+
const input = `| Description |
|
|
274
|
+
| --- |
|
|
275
|
+
| **Bold with *nested italic* inside** |
|
|
276
|
+
| *Italic with **nested bold** inside* |`;
|
|
277
|
+
const result = formatMarkdownTables(input);
|
|
278
|
+
expect(result).toMatchInlineSnapshot(`
|
|
279
|
+
"\`\`\`
|
|
280
|
+
Description
|
|
281
|
+
------------------------------
|
|
282
|
+
Bold with nested italic inside
|
|
283
|
+
Italic with nested bold inside
|
|
284
|
+
\`\`\`
|
|
285
|
+
"
|
|
286
|
+
`);
|
|
287
|
+
});
|
|
288
|
+
test('handles image references', () => {
|
|
289
|
+
const input = `| Icon | Name |
|
|
290
|
+
| --- | --- |
|
|
291
|
+
|  | Item 1 |
|
|
292
|
+
|  | Item 2 |`;
|
|
293
|
+
const result = formatMarkdownTables(input);
|
|
294
|
+
expect(result).toMatchInlineSnapshot(`
|
|
295
|
+
"\`\`\`
|
|
296
|
+
Icon Name
|
|
297
|
+
---------------------------- ------
|
|
298
|
+
https://example.com/icon.png Item 1
|
|
299
|
+
https://cdn.test.com/img.jpg Item 2
|
|
300
|
+
\`\`\`
|
|
301
|
+
"
|
|
302
|
+
`);
|
|
303
|
+
});
|
|
304
|
+
test('preserves code blocks alongside tables', () => {
|
|
305
|
+
const input = `Some code:
|
|
306
|
+
|
|
307
|
+
\`\`\`js
|
|
308
|
+
const x = 1
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
A table:
|
|
312
|
+
|
|
313
|
+
| Key | Value |
|
|
314
|
+
| --- | --- |
|
|
315
|
+
| a | 1 |
|
|
316
|
+
|
|
317
|
+
More code:
|
|
318
|
+
|
|
319
|
+
\`\`\`python
|
|
320
|
+
print("hello")
|
|
321
|
+
\`\`\``;
|
|
322
|
+
const result = formatMarkdownTables(input);
|
|
323
|
+
expect(result).toMatchInlineSnapshot(`
|
|
324
|
+
"Some code:
|
|
325
|
+
|
|
326
|
+
\`\`\`js
|
|
327
|
+
const x = 1
|
|
328
|
+
\`\`\`
|
|
329
|
+
|
|
330
|
+
A table:
|
|
331
|
+
|
|
332
|
+
\`\`\`
|
|
333
|
+
Key Value
|
|
334
|
+
--- -----
|
|
335
|
+
a 1
|
|
336
|
+
\`\`\`
|
|
337
|
+
More code:
|
|
338
|
+
|
|
339
|
+
\`\`\`python
|
|
340
|
+
print("hello")
|
|
341
|
+
\`\`\`"
|
|
342
|
+
`);
|
|
343
|
+
});
|
|
344
|
+
test('handles content without tables', () => {
|
|
345
|
+
const input = `Just some regular markdown.
|
|
346
|
+
|
|
347
|
+
- List item 1
|
|
348
|
+
- List item 2
|
|
349
|
+
|
|
350
|
+
**Bold text** and *italic*.`;
|
|
351
|
+
const result = formatMarkdownTables(input);
|
|
352
|
+
expect(result).toMatchInlineSnapshot(`
|
|
353
|
+
"Just some regular markdown.
|
|
354
|
+
|
|
355
|
+
- List item 1
|
|
356
|
+
- List item 2
|
|
357
|
+
|
|
358
|
+
**Bold text** and *italic*."
|
|
359
|
+
`);
|
|
360
|
+
});
|
|
361
|
+
test('handles complex real-world table', () => {
|
|
362
|
+
const input = `## API Endpoints
|
|
363
|
+
|
|
364
|
+
| Method | Endpoint | Description | Auth |
|
|
365
|
+
| --- | --- | --- | --- |
|
|
366
|
+
| GET | \`/api/users\` | List all users | [Bearer token](https://docs.example.com/auth) |
|
|
367
|
+
| POST | \`/api/users\` | Create **new** user | Required |
|
|
368
|
+
| DELETE | \`/api/users/:id\` | ~~Remove~~ *Deactivate* user | Admin only |`;
|
|
369
|
+
const result = formatMarkdownTables(input);
|
|
370
|
+
expect(result).toMatchInlineSnapshot(`
|
|
371
|
+
"## API Endpoints
|
|
372
|
+
|
|
373
|
+
\`\`\`
|
|
374
|
+
Method Endpoint Description Auth
|
|
375
|
+
------ -------------- ---------------------- -----------------------------
|
|
376
|
+
GET /api/users List all users https://docs.example.com/auth
|
|
377
|
+
POST /api/users Create new user Required
|
|
378
|
+
DELETE /api/users/:id Remove Deactivate user Admin only
|
|
379
|
+
\`\`\`
|
|
380
|
+
"
|
|
381
|
+
`);
|
|
382
|
+
});
|
|
383
|
+
test('handles unicode content', () => {
|
|
384
|
+
const input = `| Emoji | Name | Country |
|
|
385
|
+
| --- | --- | --- |
|
|
386
|
+
| 🍎 | Apple | 日本 |
|
|
387
|
+
| 🍊 | Orange | España |
|
|
388
|
+
| 🍌 | Banana | Ελλάδα |`;
|
|
389
|
+
const result = formatMarkdownTables(input);
|
|
390
|
+
expect(result).toMatchInlineSnapshot(`
|
|
391
|
+
"\`\`\`
|
|
392
|
+
Emoji Name Country
|
|
393
|
+
----- ------ -------
|
|
394
|
+
🍎 Apple 日本
|
|
395
|
+
🍊 Orange España
|
|
396
|
+
🍌 Banana Ελλάδα
|
|
397
|
+
\`\`\`
|
|
398
|
+
"
|
|
399
|
+
`);
|
|
400
|
+
});
|
|
401
|
+
test('handles numbers and special characters', () => {
|
|
402
|
+
const input = `| Price | Discount | Final |
|
|
403
|
+
| --- | --- | --- |
|
|
404
|
+
| $100.00 | -15% | $85.00 |
|
|
405
|
+
| €50,00 | -10% | €45,00 |
|
|
406
|
+
| £75.99 | N/A | £75.99 |`;
|
|
407
|
+
const result = formatMarkdownTables(input);
|
|
408
|
+
expect(result).toMatchInlineSnapshot(`
|
|
409
|
+
"\`\`\`
|
|
410
|
+
Price Discount Final
|
|
411
|
+
------- -------- ------
|
|
412
|
+
$100.00 -15% $85.00
|
|
413
|
+
€50,00 -10% €45,00
|
|
414
|
+
£75.99 N/A £75.99
|
|
415
|
+
\`\`\`
|
|
416
|
+
"
|
|
417
|
+
`);
|
|
418
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Main thread interface for the GenAI worker.
|
|
2
|
+
// Spawns and manages the worker thread, handling message passing for
|
|
3
|
+
// audio input/output, tool call completions, and graceful shutdown.
|
|
4
|
+
import { Worker } from 'node:worker_threads';
|
|
5
|
+
import { createLogger } from './logger.js';
|
|
6
|
+
const genaiWorkerLogger = createLogger('GENAI WORKER');
|
|
7
|
+
const genaiWrapperLogger = createLogger('GENAI WORKER WRAPPER');
|
|
8
|
+
export function createGenAIWorker(options) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const worker = new Worker(new URL('../dist/genai-worker.js', import.meta.url));
|
|
11
|
+
// Handle messages from worker
|
|
12
|
+
worker.on('message', (message) => {
|
|
13
|
+
switch (message.type) {
|
|
14
|
+
case 'assistantOpusPacket':
|
|
15
|
+
options.onAssistantOpusPacket(message.packet);
|
|
16
|
+
break;
|
|
17
|
+
case 'assistantStartSpeaking':
|
|
18
|
+
options.onAssistantStartSpeaking?.();
|
|
19
|
+
break;
|
|
20
|
+
case 'assistantStopSpeaking':
|
|
21
|
+
options.onAssistantStopSpeaking?.();
|
|
22
|
+
break;
|
|
23
|
+
case 'assistantInterruptSpeaking':
|
|
24
|
+
options.onAssistantInterruptSpeaking?.();
|
|
25
|
+
break;
|
|
26
|
+
case 'toolCallCompleted':
|
|
27
|
+
options.onToolCallCompleted?.(message);
|
|
28
|
+
break;
|
|
29
|
+
case 'error':
|
|
30
|
+
genaiWorkerLogger.error('Error:', message.error);
|
|
31
|
+
options.onError?.(message.error);
|
|
32
|
+
break;
|
|
33
|
+
case 'ready':
|
|
34
|
+
genaiWorkerLogger.log('Ready');
|
|
35
|
+
// Resolve with the worker interface
|
|
36
|
+
resolve({
|
|
37
|
+
sendRealtimeInput({ audio, audioStreamEnd }) {
|
|
38
|
+
worker.postMessage({
|
|
39
|
+
type: 'sendRealtimeInput',
|
|
40
|
+
audio,
|
|
41
|
+
audioStreamEnd,
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
sendTextInput(text) {
|
|
45
|
+
worker.postMessage({
|
|
46
|
+
type: 'sendTextInput',
|
|
47
|
+
text,
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
interrupt() {
|
|
51
|
+
worker.postMessage({
|
|
52
|
+
type: 'interrupt',
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
async stop() {
|
|
56
|
+
genaiWrapperLogger.log('Stopping worker...');
|
|
57
|
+
// Send stop message to trigger graceful shutdown
|
|
58
|
+
worker.postMessage({ type: 'stop' });
|
|
59
|
+
// Wait for worker to exit gracefully (with timeout)
|
|
60
|
+
await new Promise((resolve) => {
|
|
61
|
+
let resolved = false;
|
|
62
|
+
// Listen for worker exit
|
|
63
|
+
worker.once('exit', (code) => {
|
|
64
|
+
if (!resolved) {
|
|
65
|
+
resolved = true;
|
|
66
|
+
genaiWrapperLogger.log(`[GENAI WORKER WRAPPER] Worker exited with code ${code}`);
|
|
67
|
+
resolve();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
// Timeout after 5 seconds and force terminate
|
|
71
|
+
setTimeout(() => {
|
|
72
|
+
if (!resolved) {
|
|
73
|
+
resolved = true;
|
|
74
|
+
genaiWrapperLogger.log('[GENAI WORKER WRAPPER] Worker did not exit gracefully, terminating...');
|
|
75
|
+
worker.terminate().then(() => {
|
|
76
|
+
genaiWrapperLogger.log('Worker terminated');
|
|
77
|
+
resolve();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}, 5000);
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// Handle worker errors
|
|
88
|
+
worker.on('error', (error) => {
|
|
89
|
+
genaiWorkerLogger.error('Worker error:', error);
|
|
90
|
+
reject(error);
|
|
91
|
+
});
|
|
92
|
+
worker.on('exit', (code) => {
|
|
93
|
+
if (code !== 0) {
|
|
94
|
+
genaiWorkerLogger.error(`Worker stopped with exit code ${code}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
// Send initialization message
|
|
98
|
+
const initMessage = {
|
|
99
|
+
type: 'init',
|
|
100
|
+
directory: options.directory,
|
|
101
|
+
systemMessage: options.systemMessage,
|
|
102
|
+
guildId: options.guildId,
|
|
103
|
+
channelId: options.channelId,
|
|
104
|
+
appId: options.appId,
|
|
105
|
+
geminiApiKey: options.geminiApiKey,
|
|
106
|
+
};
|
|
107
|
+
worker.postMessage(initMessage);
|
|
108
|
+
});
|
|
109
|
+
}
|