kimaki 0.0.2 → 0.0.3
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/dist/bin.js +0 -0
- package/dist/bundle.js +110 -5
- package/package.json +17 -17
package/dist/bin.js
CHANGED
|
File without changes
|
package/dist/bundle.js
CHANGED
|
@@ -165,6 +165,14 @@ var init_video_to_ascii = __esm(() => {
|
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
// ../liveapi/src/utils.ts
|
|
168
|
+
async function closeAllAudioContexts() {
|
|
169
|
+
for (const [id, ctx] of map) {
|
|
170
|
+
if (ctx.state !== "closed") {
|
|
171
|
+
await ctx.close();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
map.clear();
|
|
175
|
+
}
|
|
168
176
|
function base64ToArrayBuffer(base64) {
|
|
169
177
|
var binaryString = atob(base64);
|
|
170
178
|
var bytes = new Uint8Array(binaryString.length);
|
|
@@ -195,7 +203,7 @@ var init_utils = __esm(() => {
|
|
|
195
203
|
return ctx2;
|
|
196
204
|
}
|
|
197
205
|
}
|
|
198
|
-
const ctx = new AudioContext(options);
|
|
206
|
+
const ctx = new globalThis.AudioContext(options);
|
|
199
207
|
if (options?.id) {
|
|
200
208
|
map.set(options.id, ctx);
|
|
201
209
|
}
|
|
@@ -208,7 +216,7 @@ var init_utils = __esm(() => {
|
|
|
208
216
|
return ctx2;
|
|
209
217
|
}
|
|
210
218
|
}
|
|
211
|
-
const ctx = new AudioContext(options);
|
|
219
|
+
const ctx = new globalThis.AudioContext(options);
|
|
212
220
|
if (options?.id) {
|
|
213
221
|
map.set(options.id, ctx);
|
|
214
222
|
}
|
|
@@ -441,10 +449,13 @@ var init_audio_recorder = __esm(() => {
|
|
|
441
449
|
stop() {
|
|
442
450
|
const handleStop = () => {
|
|
443
451
|
this.source?.disconnect();
|
|
452
|
+
this.recordingWorklet?.disconnect();
|
|
453
|
+
this.vuWorklet?.disconnect();
|
|
444
454
|
this.stream?.getTracks?.()?.forEach((track) => track.stop());
|
|
445
455
|
this.stream = undefined;
|
|
446
456
|
this.recordingWorklet = undefined;
|
|
447
457
|
this.vuWorklet = undefined;
|
|
458
|
+
this.recording = false;
|
|
448
459
|
};
|
|
449
460
|
if (this.starting) {
|
|
450
461
|
this.starting.then(handleStop);
|
|
@@ -452,6 +463,14 @@ var init_audio_recorder = __esm(() => {
|
|
|
452
463
|
}
|
|
453
464
|
handleStop();
|
|
454
465
|
}
|
|
466
|
+
async destroy() {
|
|
467
|
+
this.stop();
|
|
468
|
+
if (this.audioContext?.state !== "closed") {
|
|
469
|
+
await this.audioContext?.close();
|
|
470
|
+
}
|
|
471
|
+
this.audioContext = undefined;
|
|
472
|
+
this.removeAllListeners();
|
|
473
|
+
}
|
|
455
474
|
};
|
|
456
475
|
});
|
|
457
476
|
|
|
@@ -617,11 +636,77 @@ class AudioStreamer {
|
|
|
617
636
|
this.isStreamComplete = true;
|
|
618
637
|
this.onComplete();
|
|
619
638
|
}
|
|
639
|
+
async destroy() {
|
|
640
|
+
this.stop();
|
|
641
|
+
const worklets = registeredWorklets.get(this.context);
|
|
642
|
+
if (worklets) {
|
|
643
|
+
Object.values(worklets).forEach(({ node }) => {
|
|
644
|
+
if (node) {
|
|
645
|
+
node.disconnect();
|
|
646
|
+
node.port.close();
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
registeredWorklets.delete(this.context);
|
|
650
|
+
}
|
|
651
|
+
this.gainNode.disconnect();
|
|
652
|
+
}
|
|
620
653
|
}
|
|
621
654
|
var init_audio_streamer = __esm(() => {
|
|
622
655
|
init_audioworklet_registry();
|
|
623
656
|
});
|
|
624
657
|
|
|
658
|
+
// ../liveapi/src/cleanup.ts
|
|
659
|
+
function registerCleanupHandler(handler) {
|
|
660
|
+
cleanupHandlers.push(handler);
|
|
661
|
+
}
|
|
662
|
+
function unregisterCleanupHandler(handler) {
|
|
663
|
+
const index = cleanupHandlers.indexOf(handler);
|
|
664
|
+
if (index > -1) {
|
|
665
|
+
cleanupHandlers.splice(index, 1);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async function cleanup() {
|
|
669
|
+
console.log("Cleaning up audio resources...");
|
|
670
|
+
await Promise.all(cleanupHandlers.map((handler) => Promise.resolve(handler()).catch((err) => console.error("Cleanup handler error:", err))));
|
|
671
|
+
await closeAllAudioContexts();
|
|
672
|
+
console.log("Audio cleanup complete");
|
|
673
|
+
}
|
|
674
|
+
var cleanupHandlers;
|
|
675
|
+
var init_cleanup = __esm(() => {
|
|
676
|
+
init_utils();
|
|
677
|
+
cleanupHandlers = [];
|
|
678
|
+
if (typeof process !== "undefined") {
|
|
679
|
+
process.on("exit", () => {
|
|
680
|
+
cleanup().catch(console.error);
|
|
681
|
+
});
|
|
682
|
+
process.on("SIGINT", async () => {
|
|
683
|
+
await cleanup();
|
|
684
|
+
process.exit(0);
|
|
685
|
+
});
|
|
686
|
+
process.on("SIGTERM", async () => {
|
|
687
|
+
await cleanup();
|
|
688
|
+
process.exit(0);
|
|
689
|
+
});
|
|
690
|
+
process.on("uncaughtException", async (error) => {
|
|
691
|
+
console.error("Uncaught Exception:", error);
|
|
692
|
+
await cleanup();
|
|
693
|
+
process.exit(1);
|
|
694
|
+
});
|
|
695
|
+
process.on("unhandledRejection", async (reason, promise) => {
|
|
696
|
+
console.error("Unhandled Rejection at:", promise, "reason:", reason);
|
|
697
|
+
await cleanup();
|
|
698
|
+
process.exit(1);
|
|
699
|
+
});
|
|
700
|
+
} else if (typeof window !== "undefined") {
|
|
701
|
+
window.addEventListener("beforeunload", () => {
|
|
702
|
+
cleanup().catch(console.error);
|
|
703
|
+
});
|
|
704
|
+
window.addEventListener("unload", () => {
|
|
705
|
+
cleanup().catch(console.error);
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
|
|
625
710
|
// ../liveapi/src/live-api-client.ts
|
|
626
711
|
import {
|
|
627
712
|
GoogleGenAI,
|
|
@@ -697,6 +782,9 @@ class LiveAPIClient {
|
|
|
697
782
|
}
|
|
698
783
|
this.audioRecorder = new AudioRecorder(recordingSampleRate);
|
|
699
784
|
this.setupAudioRecorder();
|
|
785
|
+
const cleanupHandler = () => this.destroy();
|
|
786
|
+
registerCleanupHandler(cleanupHandler);
|
|
787
|
+
this._cleanupHandler = cleanupHandler;
|
|
700
788
|
}
|
|
701
789
|
updateState(updates) {
|
|
702
790
|
this.state = { ...this.state, ...updates };
|
|
@@ -1033,16 +1121,27 @@ class LiveAPIClient {
|
|
|
1033
1121
|
getConfig() {
|
|
1034
1122
|
return { ...this.state.config };
|
|
1035
1123
|
}
|
|
1036
|
-
destroy() {
|
|
1124
|
+
async destroy() {
|
|
1037
1125
|
console.log(`calling destroy()`);
|
|
1126
|
+
const cleanupHandler = this._cleanupHandler;
|
|
1127
|
+
if (cleanupHandler) {
|
|
1128
|
+
unregisterCleanupHandler(cleanupHandler);
|
|
1129
|
+
delete this._cleanupHandler;
|
|
1130
|
+
}
|
|
1038
1131
|
this.isExplicitDisconnect = true;
|
|
1039
1132
|
if (this.reconnectTimeout) {
|
|
1040
1133
|
clearTimeout(this.reconnectTimeout);
|
|
1041
1134
|
this.reconnectTimeout = null;
|
|
1042
1135
|
}
|
|
1043
1136
|
this.disconnect();
|
|
1044
|
-
this.audioRecorder?.
|
|
1045
|
-
this.
|
|
1137
|
+
await this.audioRecorder?.destroy();
|
|
1138
|
+
this.audioRecorder = null;
|
|
1139
|
+
await this.audioStreamer?.destroy();
|
|
1140
|
+
this.audioStreamer = null;
|
|
1141
|
+
const audioCtx = await audioContext({ id: "audio-out" });
|
|
1142
|
+
if (audioCtx?.state !== "closed") {
|
|
1143
|
+
await audioCtx.close();
|
|
1144
|
+
}
|
|
1046
1145
|
this.onAssistantStopSpeaking();
|
|
1047
1146
|
this.tools = [];
|
|
1048
1147
|
this.sessionHandle = null;
|
|
@@ -1066,6 +1165,7 @@ var init_live_api_client = __esm(() => {
|
|
|
1066
1165
|
init_audio_streamer();
|
|
1067
1166
|
init_utils();
|
|
1068
1167
|
init_vol_meter();
|
|
1168
|
+
init_cleanup();
|
|
1069
1169
|
});
|
|
1070
1170
|
|
|
1071
1171
|
// ../liveapi/src/ai-tool-to-genai.ts
|
|
@@ -1825,12 +1925,15 @@ var init_api = __esm(() => {
|
|
|
1825
1925
|
// ../liveapi/src/index.ts
|
|
1826
1926
|
var exports_src = {};
|
|
1827
1927
|
__export(exports_src, {
|
|
1928
|
+
unregisterCleanupHandler: () => unregisterCleanupHandler,
|
|
1828
1929
|
uiMessageToClientMessage: () => uiMessageToClientMessage,
|
|
1930
|
+
registerCleanupHandler: () => registerCleanupHandler,
|
|
1829
1931
|
mergeConsecutiveTextParts: () => mergeConsecutiveTextParts,
|
|
1830
1932
|
extractSchemaFromTool: () => extractSchemaFromTool,
|
|
1831
1933
|
downSampleInt16Buffer: () => downSampleInt16Buffer,
|
|
1832
1934
|
downSampleAudioBuffer: () => downSampleAudioBuffer,
|
|
1833
1935
|
createAudioChatAPI: () => createAudioChatAPI,
|
|
1936
|
+
closeAllAudioContexts: () => closeAllAudioContexts,
|
|
1834
1937
|
callableToolsFromObject: () => callableToolsFromObject,
|
|
1835
1938
|
aiToolToGenAIFunction: () => aiToolToGenAIFunction,
|
|
1836
1939
|
aiToolToCallableTool: () => aiToolToCallableTool,
|
|
@@ -1842,6 +1945,8 @@ var init_src = __esm(() => {
|
|
|
1842
1945
|
init_ai_tool_to_genai();
|
|
1843
1946
|
init_genai_to_ui_message();
|
|
1844
1947
|
init_api();
|
|
1948
|
+
init_utils();
|
|
1949
|
+
init_cleanup();
|
|
1845
1950
|
});
|
|
1846
1951
|
|
|
1847
1952
|
// src/cli.tsx
|
package/package.json
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimaki",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "AI voice assistant for controlling coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": "https://github.com/remorses/kimaki",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "bun build ./src/bin.ts --outfile ./dist/bundle.js --target node --format esm --external @google/genai --external @opencode-ai/plugin --external @opencode-ai/sdk --external ai --external cac --external chalk --external chokidar --external cli-table3 --external date-fns --external globby --external ink --external js-yaml --external luxon --external mime-types --external node-web-audio-api --external picocolors --external prompts --external react --external sema4 --external sharp --external strip-ansi --external wavefile --external ws --external xdg-basedir --external zod --external eventemitter3 --external @ai-sdk/openai",
|
|
12
|
-
"typecheck": "tsc",
|
|
13
|
-
"prepublishOnly": "pnpm build",
|
|
14
|
-
"save-plugin": "bun build ./src/plugin.ts --outfile ~/.config/opencode/plugin/kimaki.js --target bun --format esm",
|
|
15
|
-
"kimaki": "tsx bin.js",
|
|
16
|
-
"play": "tsx src/bin.ts",
|
|
17
|
-
"play:prod": "node bin.js",
|
|
18
|
-
"watch": "tsc -w",
|
|
19
|
-
"test": "vitest",
|
|
20
|
-
"changeset": "changeset",
|
|
21
|
-
"version": "changeset version",
|
|
22
|
-
"release": "changeset publish"
|
|
23
|
-
},
|
|
24
10
|
"bin": "./bin.js",
|
|
25
11
|
"files": [
|
|
26
12
|
"dist",
|
|
@@ -64,12 +50,26 @@
|
|
|
64
50
|
"wavefile": "^11.0.0",
|
|
65
51
|
"ws": "^8.18.3",
|
|
66
52
|
"xdg-basedir": "^5.1.0",
|
|
67
|
-
"zod": "^4.1.5"
|
|
53
|
+
"zod": "^4.1.5",
|
|
54
|
+
"@xmorse/liveapi": "0.0.3"
|
|
68
55
|
},
|
|
69
56
|
"devDependencies": {
|
|
70
57
|
"@types/mime-types": "^3.0.1",
|
|
71
58
|
"@types/node": "^24.3.0",
|
|
72
59
|
"@types/prompts": "^2.4.9",
|
|
73
60
|
"@types/ws": "^8.18.1"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "bun build ./src/bin.ts --outfile ./dist/bundle.js --target node --format esm --external @google/genai --external @opencode-ai/plugin --external @opencode-ai/sdk --external ai --external cac --external chalk --external chokidar --external cli-table3 --external date-fns --external globby --external ink --external js-yaml --external luxon --external mime-types --external node-web-audio-api --external picocolors --external prompts --external react --external sema4 --external sharp --external strip-ansi --external wavefile --external ws --external xdg-basedir --external zod --external eventemitter3 --external @ai-sdk/openai",
|
|
64
|
+
"typecheck": "tsc",
|
|
65
|
+
"save-plugin": "bun build ./src/plugin.ts --outfile ~/.config/opencode/plugin/kimaki.js --target bun --format esm",
|
|
66
|
+
"kimaki": "tsx bin.js",
|
|
67
|
+
"play": "tsx src/bin.ts",
|
|
68
|
+
"play:prod": "node bin.js",
|
|
69
|
+
"watch": "tsc -w",
|
|
70
|
+
"test": "vitest",
|
|
71
|
+
"changeset": "changeset",
|
|
72
|
+
"version": "changeset version",
|
|
73
|
+
"release": "changeset publish"
|
|
74
74
|
}
|
|
75
|
-
}
|
|
75
|
+
}
|