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.
Files changed (3) hide show
  1. package/dist/bin.js +0 -0
  2. package/dist/bundle.js +110 -5
  3. 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?.stop();
1045
- this.audioStreamer?.stop();
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.2",
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
+ }