zerg-ztc 0.1.11 → 0.1.12

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 (122) hide show
  1. package/bin/ztc-audio-darwin-arm64 +0 -0
  2. package/dist/utils/dictation_native.d.ts.map +1 -1
  3. package/dist/utils/dictation_native.js +43 -23
  4. package/dist/utils/dictation_native.js.map +1 -1
  5. package/package.json +5 -4
  6. package/packages/ztc-dictation/Cargo.toml +0 -43
  7. package/packages/ztc-dictation/README.md +0 -65
  8. package/packages/ztc-dictation/index.d.ts +0 -16
  9. package/packages/ztc-dictation/index.js +0 -74
  10. package/packages/ztc-dictation/package.json +0 -41
  11. package/packages/ztc-dictation/src/main.rs +0 -430
  12. package/src/App.tsx +0 -910
  13. package/src/agent/agent.ts +0 -534
  14. package/src/agent/backends/anthropic.ts +0 -86
  15. package/src/agent/backends/gemini.ts +0 -119
  16. package/src/agent/backends/inception.ts +0 -23
  17. package/src/agent/backends/index.ts +0 -17
  18. package/src/agent/backends/openai.ts +0 -23
  19. package/src/agent/backends/openai_compatible.ts +0 -143
  20. package/src/agent/backends/types.ts +0 -83
  21. package/src/agent/commands/clipboard.ts +0 -77
  22. package/src/agent/commands/config.ts +0 -204
  23. package/src/agent/commands/debug.ts +0 -23
  24. package/src/agent/commands/dictation.ts +0 -11
  25. package/src/agent/commands/emulation.ts +0 -80
  26. package/src/agent/commands/execution.ts +0 -9
  27. package/src/agent/commands/help.ts +0 -20
  28. package/src/agent/commands/history.ts +0 -13
  29. package/src/agent/commands/index.ts +0 -48
  30. package/src/agent/commands/input_mode.ts +0 -22
  31. package/src/agent/commands/keybindings.ts +0 -40
  32. package/src/agent/commands/model.ts +0 -11
  33. package/src/agent/commands/models.ts +0 -116
  34. package/src/agent/commands/permissions.ts +0 -64
  35. package/src/agent/commands/retry.ts +0 -9
  36. package/src/agent/commands/shell.ts +0 -68
  37. package/src/agent/commands/skills.ts +0 -54
  38. package/src/agent/commands/status.ts +0 -19
  39. package/src/agent/commands/types.ts +0 -88
  40. package/src/agent/commands/update.ts +0 -32
  41. package/src/agent/factory.ts +0 -60
  42. package/src/agent/index.ts +0 -20
  43. package/src/agent/runtime/capabilities.ts +0 -7
  44. package/src/agent/runtime/memory.ts +0 -23
  45. package/src/agent/runtime/policy.ts +0 -48
  46. package/src/agent/runtime/session.ts +0 -18
  47. package/src/agent/runtime/tracing.ts +0 -23
  48. package/src/agent/tools/file.ts +0 -178
  49. package/src/agent/tools/index.ts +0 -52
  50. package/src/agent/tools/screenshot.ts +0 -821
  51. package/src/agent/tools/search.ts +0 -138
  52. package/src/agent/tools/shell.ts +0 -69
  53. package/src/agent/tools/skills.ts +0 -28
  54. package/src/agent/tools/types.ts +0 -14
  55. package/src/agent/tools/zerg.ts +0 -50
  56. package/src/cli.tsx +0 -163
  57. package/src/components/ActivityLine.tsx +0 -23
  58. package/src/components/FullScreen.tsx +0 -79
  59. package/src/components/Header.tsx +0 -27
  60. package/src/components/InputArea.tsx +0 -1660
  61. package/src/components/MessageList.tsx +0 -71
  62. package/src/components/SingleMessage.tsx +0 -298
  63. package/src/components/StatusBar.tsx +0 -55
  64. package/src/components/index.tsx +0 -8
  65. package/src/config/types.ts +0 -19
  66. package/src/config.ts +0 -186
  67. package/src/debug/logger.ts +0 -14
  68. package/src/emulation/README.md +0 -24
  69. package/src/emulation/catalog.ts +0 -82
  70. package/src/emulation/trace_style.ts +0 -8
  71. package/src/emulation/types.ts +0 -7
  72. package/src/skills/index.ts +0 -36
  73. package/src/skills/loader.ts +0 -135
  74. package/src/skills/registry.ts +0 -6
  75. package/src/skills/types.ts +0 -10
  76. package/src/types.ts +0 -84
  77. package/src/ui/README.md +0 -44
  78. package/src/ui/core/factory.ts +0 -9
  79. package/src/ui/core/index.ts +0 -4
  80. package/src/ui/core/input.ts +0 -38
  81. package/src/ui/core/input_segments.ts +0 -410
  82. package/src/ui/core/input_state.ts +0 -17
  83. package/src/ui/core/layout_yoga.ts +0 -122
  84. package/src/ui/core/style.ts +0 -38
  85. package/src/ui/core/types.ts +0 -54
  86. package/src/ui/ink/index.tsx +0 -1
  87. package/src/ui/ink/render.tsx +0 -60
  88. package/src/ui/views/activity_line.ts +0 -33
  89. package/src/ui/views/app.ts +0 -111
  90. package/src/ui/views/header.ts +0 -44
  91. package/src/ui/views/input_area.ts +0 -255
  92. package/src/ui/views/message_list.ts +0 -443
  93. package/src/ui/views/status_bar.ts +0 -114
  94. package/src/ui/vue/index.ts +0 -53
  95. package/src/ui/web/frame_render.tsx +0 -148
  96. package/src/ui/web/index.tsx +0 -1
  97. package/src/ui/web/render.tsx +0 -41
  98. package/src/utils/clipboard.ts +0 -39
  99. package/src/utils/clipboard_image.ts +0 -40
  100. package/src/utils/dictation.ts +0 -467
  101. package/src/utils/dictation_native.ts +0 -258
  102. package/src/utils/diff.ts +0 -52
  103. package/src/utils/image_preview.ts +0 -36
  104. package/src/utils/models.ts +0 -98
  105. package/src/utils/path_complete.ts +0 -173
  106. package/src/utils/path_format.ts +0 -99
  107. package/src/utils/shell.ts +0 -72
  108. package/src/utils/spinner_frames.ts +0 -1
  109. package/src/utils/spinner_verbs.ts +0 -23
  110. package/src/utils/table.ts +0 -171
  111. package/src/utils/tool_summary.ts +0 -56
  112. package/src/utils/tool_trace.ts +0 -346
  113. package/src/utils/update.ts +0 -44
  114. package/src/utils/version.ts +0 -15
  115. package/src/web/index.html +0 -352
  116. package/src/web/mirror-favicon.svg +0 -4
  117. package/src/web/mirror.html +0 -641
  118. package/src/web/mirror_hook.ts +0 -25
  119. package/src/web/mirror_server.ts +0 -204
  120. package/tsconfig.json +0 -22
  121. package/vite.config.ts +0 -363
  122. /package/{packages/ztc-dictation/bin → bin}/.gitkeep +0 -0
@@ -1,430 +0,0 @@
1
- use clap::Parser;
2
- use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
3
- use ringbuf::{HeapRb, traits::{Consumer, Producer, Split}};
4
- use serde::Serialize;
5
- use std::io::{self, Write};
6
- use std::path::PathBuf;
7
- use std::sync::atomic::{AtomicBool, Ordering};
8
- use std::sync::{Arc, Mutex};
9
- use std::time::{Duration, Instant};
10
- use whisper_rs::{FullParams, SamplingStrategy, WhisperContext, WhisperContextParameters};
11
-
12
- const SAMPLE_RATE: u32 = 16000;
13
- const CHANNELS: u16 = 1;
14
-
15
- #[derive(Parser, Debug)]
16
- #[command(name = "ztc-audio")]
17
- #[command(about = "Real-time audio capture and transcription for ZTC")]
18
- struct Args {
19
- /// Whisper model to use: tiny, base, small, medium, large
20
- #[arg(short, long, default_value = "base")]
21
- model: String,
22
-
23
- /// Just list audio devices and exit
24
- #[arg(long)]
25
- list_devices: bool,
26
-
27
- /// Download model if not present and exit
28
- #[arg(long)]
29
- download_model: bool,
30
-
31
- /// Audio input device name (uses default if not specified)
32
- #[arg(short, long)]
33
- device: Option<String>,
34
- }
35
-
36
- #[derive(Serialize)]
37
- #[serde(tag = "type")]
38
- enum OutputMessage {
39
- #[serde(rename = "ready")]
40
- Ready { device: String, model: String },
41
-
42
- #[serde(rename = "level")]
43
- Level { db: f32, rms: f32 },
44
-
45
- #[serde(rename = "text")]
46
- Text { content: String, partial: bool },
47
-
48
- #[serde(rename = "error")]
49
- Error { message: String },
50
-
51
- #[serde(rename = "device")]
52
- Device { name: String, is_default: bool },
53
- }
54
-
55
- fn emit(msg: &OutputMessage) {
56
- if let Ok(json) = serde_json::to_string(msg) {
57
- println!("{}", json);
58
- let _ = io::stdout().flush();
59
- }
60
- }
61
-
62
- fn get_model_path(model_name: &str) -> PathBuf {
63
- let models_dir = dirs::home_dir()
64
- .unwrap_or_else(|| PathBuf::from("."))
65
- .join(".ztc")
66
- .join("models");
67
-
68
- std::fs::create_dir_all(&models_dir).ok();
69
- models_dir.join(format!("ggml-{}.bin", model_name))
70
- }
71
-
72
- fn download_model(model_name: &str) -> Result<PathBuf, String> {
73
- let path = get_model_path(model_name);
74
-
75
- if path.exists() {
76
- return Ok(path);
77
- }
78
-
79
- let url = format!(
80
- "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-{}.bin",
81
- model_name
82
- );
83
-
84
- eprintln!("Downloading whisper model '{}' from {}", model_name, url);
85
-
86
- let response = ureq::get(&url)
87
- .call()
88
- .map_err(|e| format!("Failed to download model: {}", e))?;
89
-
90
- let total_size = response
91
- .header("Content-Length")
92
- .and_then(|s| s.parse::<u64>().ok())
93
- .unwrap_or(0);
94
-
95
- let pb = indicatif::ProgressBar::new(total_size);
96
- pb.set_style(
97
- indicatif::ProgressStyle::default_bar()
98
- .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
99
- .unwrap()
100
- .progress_chars("#>-"),
101
- );
102
-
103
- let mut reader = response.into_reader();
104
- let mut file = std::fs::File::create(&path)
105
- .map_err(|e| format!("Failed to create model file: {}", e))?;
106
-
107
- let mut buffer = [0u8; 8192];
108
- let mut downloaded = 0u64;
109
-
110
- loop {
111
- let bytes_read = reader.read(&mut buffer)
112
- .map_err(|e| format!("Failed to read from network: {}", e))?;
113
-
114
- if bytes_read == 0 {
115
- break;
116
- }
117
-
118
- file.write_all(&buffer[..bytes_read])
119
- .map_err(|e| format!("Failed to write to file: {}", e))?;
120
-
121
- downloaded += bytes_read as u64;
122
- pb.set_position(downloaded);
123
- }
124
-
125
- pb.finish_with_message("Download complete");
126
- eprintln!("Model saved to {:?}", path);
127
-
128
- Ok(path)
129
- }
130
-
131
- fn list_devices() {
132
- let host = cpal::default_host();
133
-
134
- if let Some(device) = host.default_input_device() {
135
- if let Ok(name) = device.name() {
136
- emit(&OutputMessage::Device {
137
- name,
138
- is_default: true,
139
- });
140
- }
141
- }
142
-
143
- if let Ok(devices) = host.input_devices() {
144
- for device in devices {
145
- if let Ok(name) = device.name() {
146
- emit(&OutputMessage::Device {
147
- name,
148
- is_default: false,
149
- });
150
- }
151
- }
152
- }
153
- }
154
-
155
- fn calculate_db(samples: &[f32]) -> (f32, f32) {
156
- if samples.is_empty() {
157
- return (-60.0, 0.0);
158
- }
159
-
160
- let sum_squares: f32 = samples.iter().map(|s| s * s).sum();
161
- let rms = (sum_squares / samples.len() as f32).sqrt();
162
-
163
- // Convert to dB (with floor at -60dB)
164
- let db = if rms > 0.0 {
165
- 20.0 * rms.log10()
166
- } else {
167
- -60.0
168
- };
169
-
170
- (db.max(-60.0), rms)
171
- }
172
-
173
- fn run_audio_capture(
174
- device_name: Option<&str>,
175
- model_path: PathBuf,
176
- ) -> Result<(), String> {
177
- let host = cpal::default_host();
178
-
179
- // Find the audio device
180
- let device = if let Some(name) = device_name {
181
- host.input_devices()
182
- .map_err(|e| format!("Failed to enumerate devices: {}", e))?
183
- .find(|d| d.name().map(|n| n.contains(name)).unwrap_or(false))
184
- .ok_or_else(|| format!("Device '{}' not found", name))?
185
- } else {
186
- host.default_input_device()
187
- .ok_or_else(|| "No default input device".to_string())?
188
- };
189
-
190
- let device_name_str = device.name().unwrap_or_else(|_| "Unknown".to_string());
191
-
192
- // Initialize Whisper
193
- let ctx = WhisperContext::new_with_params(
194
- model_path.to_str().unwrap(),
195
- WhisperContextParameters::default(),
196
- )
197
- .map_err(|e| format!("Failed to load whisper model: {}", e))?;
198
-
199
- // Get the device's default config and use its sample rate
200
- let default_config = device.default_input_config()
201
- .map_err(|e| format!("Failed to get default input config: {}", e))?;
202
-
203
- let device_sample_rate = default_config.sample_rate().0;
204
- let device_channels = default_config.channels();
205
-
206
- emit(&OutputMessage::Ready {
207
- device: device_name_str.clone(),
208
- model: model_path.file_stem()
209
- .and_then(|s| s.to_str())
210
- .unwrap_or("unknown")
211
- .to_string(),
212
- });
213
-
214
- // Use device's native sample rate and resample to 16kHz for Whisper
215
- let config = cpal::StreamConfig {
216
- channels: device_channels,
217
- sample_rate: cpal::SampleRate(device_sample_rate),
218
- buffer_size: cpal::BufferSize::Default,
219
- };
220
-
221
- let resample_ratio = SAMPLE_RATE as f32 / device_sample_rate as f32;
222
-
223
- // Shared state
224
- let running = Arc::new(AtomicBool::new(true));
225
- let running_clone = running.clone();
226
-
227
- // Ring buffer for audio samples (5 seconds of audio at 16kHz)
228
- let ring_buffer = HeapRb::<f32>::new(SAMPLE_RATE as usize * 5);
229
- let (mut producer, mut consumer) = ring_buffer.split();
230
-
231
- // Buffer for level calculation (100ms chunks)
232
- let level_buffer = Arc::new(Mutex::new(Vec::with_capacity(SAMPLE_RATE as usize / 10)));
233
- let level_buffer_clone = level_buffer.clone();
234
-
235
- // Resampling state - accumulate fractional samples
236
- let resample_accum = Arc::new(Mutex::new(0.0f32));
237
- let resample_accum_clone = resample_accum.clone();
238
-
239
- // Handle Ctrl+C
240
- ctrlc::set_handler(move || {
241
- running_clone.store(false, Ordering::SeqCst);
242
- })
243
- .map_err(|e| format!("Failed to set signal handler: {}", e))?;
244
-
245
- // Start audio capture
246
- let stream = device
247
- .build_input_stream(
248
- &config,
249
- move |data: &[f32], _: &cpal::InputCallbackInfo| {
250
- // Convert to mono and resample to 16kHz
251
- let num_channels = device_channels as usize;
252
-
253
- // First convert to mono by averaging channels
254
- let mono_samples: Vec<f32> = data
255
- .chunks(num_channels)
256
- .map(|frame| frame.iter().sum::<f32>() / num_channels as f32)
257
- .collect();
258
-
259
- // Simple linear resampling to 16kHz
260
- // For better quality, could use a proper resampler crate
261
- let mut accum = resample_accum_clone.lock().unwrap();
262
- let mut resampled = Vec::new();
263
-
264
- for &sample in &mono_samples {
265
- *accum += resample_ratio;
266
- while *accum >= 1.0 {
267
- resampled.push(sample);
268
- *accum -= 1.0;
269
- }
270
- }
271
-
272
- // Push resampled samples to ring buffer for transcription
273
- for sample in &resampled {
274
- let _ = producer.try_push(*sample);
275
- }
276
-
277
- // Also collect for level metering (use original mono samples for responsiveness)
278
- if let Ok(mut buf) = level_buffer_clone.lock() {
279
- buf.extend_from_slice(&mono_samples);
280
- }
281
- },
282
- |err| {
283
- emit(&OutputMessage::Error {
284
- message: format!("Audio stream error: {}", err),
285
- });
286
- },
287
- None,
288
- )
289
- .map_err(|e| format!("Failed to build input stream: {}", e))?;
290
-
291
- stream.play().map_err(|e| format!("Failed to start stream: {}", e))?;
292
-
293
- // Main processing loop
294
- let mut accumulated_audio: Vec<f32> = Vec::new();
295
- let mut last_level_time = Instant::now();
296
- let mut last_transcribe_time = Instant::now();
297
- let level_interval = Duration::from_millis(50); // 20 Hz level updates
298
- let transcribe_interval = Duration::from_millis(500); // Transcribe every 500ms
299
-
300
- while running.load(Ordering::SeqCst) {
301
- // Read stdin for commands (non-blocking would be better but this works)
302
- // For now, just rely on Ctrl+C / signal handling
303
-
304
- // Emit audio levels
305
- if last_level_time.elapsed() >= level_interval {
306
- if let Ok(mut buf) = level_buffer.lock() {
307
- if !buf.is_empty() {
308
- let (db, rms) = calculate_db(&buf);
309
- emit(&OutputMessage::Level { db, rms });
310
- buf.clear();
311
- }
312
- }
313
- last_level_time = Instant::now();
314
- }
315
-
316
- // Collect audio for transcription
317
- while let Some(sample) = consumer.try_pop() {
318
- accumulated_audio.push(sample);
319
- }
320
-
321
- // Transcribe periodically
322
- if last_transcribe_time.elapsed() >= transcribe_interval && !accumulated_audio.is_empty() {
323
- // Run whisper on accumulated audio
324
- let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 });
325
- params.set_language(Some("en"));
326
- params.set_print_special(false);
327
- params.set_print_progress(false);
328
- params.set_print_realtime(false);
329
- params.set_print_timestamps(false);
330
- params.set_single_segment(true);
331
- params.set_no_context(true);
332
-
333
- let mut state = ctx.create_state()
334
- .map_err(|e| format!("Failed to create whisper state: {}", e))?;
335
-
336
- if state.full(params, &accumulated_audio).is_ok() {
337
- let num_segments = state.full_n_segments()
338
- .unwrap_or(0);
339
-
340
- let mut text = String::new();
341
- for i in 0..num_segments {
342
- if let Ok(segment) = state.full_get_segment_text(i) {
343
- text.push_str(&segment);
344
- }
345
- }
346
-
347
- let text = text.trim().to_string();
348
- if !text.is_empty() {
349
- emit(&OutputMessage::Text {
350
- content: text,
351
- partial: true,
352
- });
353
- }
354
- }
355
-
356
- last_transcribe_time = Instant::now();
357
- }
358
-
359
- std::thread::sleep(Duration::from_millis(10));
360
- }
361
-
362
- // Final transcription
363
- if !accumulated_audio.is_empty() {
364
- let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 });
365
- params.set_language(Some("en"));
366
- params.set_print_special(false);
367
- params.set_print_progress(false);
368
- params.set_print_realtime(false);
369
- params.set_print_timestamps(false);
370
-
371
- let mut state = ctx.create_state()
372
- .map_err(|e| format!("Failed to create whisper state: {}", e))?;
373
-
374
- if state.full(params, &accumulated_audio).is_ok() {
375
- let num_segments = state.full_n_segments().unwrap_or(0);
376
-
377
- let mut text = String::new();
378
- for i in 0..num_segments {
379
- if let Ok(segment) = state.full_get_segment_text(i) {
380
- text.push_str(&segment);
381
- }
382
- }
383
-
384
- let text = text.trim().to_string();
385
- emit(&OutputMessage::Text {
386
- content: text,
387
- partial: false,
388
- });
389
- }
390
- }
391
-
392
- Ok(())
393
- }
394
-
395
- fn main() {
396
- let args = Args::parse();
397
-
398
- if args.list_devices {
399
- list_devices();
400
- return;
401
- }
402
-
403
- if args.download_model {
404
- match download_model(&args.model) {
405
- Ok(path) => {
406
- eprintln!("Model ready at {:?}", path);
407
- }
408
- Err(e) => {
409
- emit(&OutputMessage::Error { message: e });
410
- std::process::exit(1);
411
- }
412
- }
413
- return;
414
- }
415
-
416
- // Ensure model is downloaded
417
- let model_path = match download_model(&args.model) {
418
- Ok(path) => path,
419
- Err(e) => {
420
- emit(&OutputMessage::Error { message: e });
421
- std::process::exit(1);
422
- }
423
- };
424
-
425
- // Run audio capture and transcription
426
- if let Err(e) = run_audio_capture(args.device.as_deref(), model_path) {
427
- emit(&OutputMessage::Error { message: e });
428
- std::process::exit(1);
429
- }
430
- }