speaksy 0.1.0__py3-none-any.whl

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.
speaksy/service.py ADDED
@@ -0,0 +1,205 @@
1
+ """Systemd service management for Speaksy."""
2
+
3
+ import os
4
+ import shutil
5
+ import subprocess
6
+ import sys
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ from speaksy.config import CONFIG_DIR, ENV_FILE
11
+
12
+ SYSTEMD_USER_DIR = Path.home() / ".config" / "systemd" / "user"
13
+ SERVICE_FILE = SYSTEMD_USER_DIR / "speaksy.service"
14
+
15
+ SERVICE_TEMPLATE = """[Unit]
16
+ Description=Speaksy - Voice Typing for Linux
17
+ After=graphical-session.target
18
+ PartOf=graphical-session.target
19
+
20
+ [Service]
21
+ Type=simple
22
+ ExecStart={python_path} -m speaksy.runner
23
+ Restart=on-failure
24
+ RestartSec=5
25
+ EnvironmentFile={env_file}
26
+
27
+ [Install]
28
+ WantedBy=graphical-session.target
29
+ """
30
+
31
+
32
+ def get_python_path() -> str:
33
+ """Get the path to the current Python interpreter."""
34
+ return sys.executable
35
+
36
+
37
+ def install_service() -> bool:
38
+ """Install the systemd user service."""
39
+ try:
40
+ SYSTEMD_USER_DIR.mkdir(parents=True, exist_ok=True)
41
+
42
+ service_content = SERVICE_TEMPLATE.format(
43
+ python_path=get_python_path(),
44
+ env_file=ENV_FILE,
45
+ )
46
+
47
+ with open(SERVICE_FILE, "w") as f:
48
+ f.write(service_content)
49
+
50
+ # Reload systemd
51
+ subprocess.run(
52
+ ["systemctl", "--user", "daemon-reload"],
53
+ check=True,
54
+ capture_output=True,
55
+ )
56
+
57
+ # Enable the service
58
+ subprocess.run(
59
+ ["systemctl", "--user", "enable", "speaksy.service"],
60
+ check=True,
61
+ capture_output=True,
62
+ )
63
+
64
+ return True
65
+ except Exception as e:
66
+ return False
67
+
68
+
69
+ def uninstall_service() -> bool:
70
+ """Uninstall the systemd user service."""
71
+ try:
72
+ stop_service()
73
+
74
+ subprocess.run(
75
+ ["systemctl", "--user", "disable", "speaksy.service"],
76
+ capture_output=True,
77
+ )
78
+
79
+ if SERVICE_FILE.exists():
80
+ SERVICE_FILE.unlink()
81
+
82
+ subprocess.run(
83
+ ["systemctl", "--user", "daemon-reload"],
84
+ capture_output=True,
85
+ )
86
+
87
+ return True
88
+ except Exception:
89
+ return False
90
+
91
+
92
+ def start_service() -> bool:
93
+ """Start the speaksy service."""
94
+ try:
95
+ result = subprocess.run(
96
+ ["systemctl", "--user", "start", "speaksy.service"],
97
+ capture_output=True,
98
+ text=True,
99
+ )
100
+ return result.returncode == 0
101
+ except Exception:
102
+ return False
103
+
104
+
105
+ def stop_service() -> bool:
106
+ """Stop the speaksy service."""
107
+ try:
108
+ result = subprocess.run(
109
+ ["systemctl", "--user", "stop", "speaksy.service"],
110
+ capture_output=True,
111
+ text=True,
112
+ )
113
+ return result.returncode == 0
114
+ except Exception:
115
+ return False
116
+
117
+
118
+ def restart_service() -> bool:
119
+ """Restart the speaksy service."""
120
+ try:
121
+ result = subprocess.run(
122
+ ["systemctl", "--user", "restart", "speaksy.service"],
123
+ capture_output=True,
124
+ text=True,
125
+ )
126
+ return result.returncode == 0
127
+ except Exception:
128
+ return False
129
+
130
+
131
+ def is_running() -> bool:
132
+ """Check if the speaksy service is running."""
133
+ try:
134
+ result = subprocess.run(
135
+ ["systemctl", "--user", "is-active", "speaksy.service"],
136
+ capture_output=True,
137
+ text=True,
138
+ )
139
+ return result.stdout.strip() == "active"
140
+ except Exception:
141
+ return False
142
+
143
+
144
+ def is_installed() -> bool:
145
+ """Check if the service is installed."""
146
+ return SERVICE_FILE.exists()
147
+
148
+
149
+ def get_uptime() -> str:
150
+ """Get how long the service has been running."""
151
+ try:
152
+ result = subprocess.run(
153
+ [
154
+ "systemctl", "--user", "show", "speaksy.service",
155
+ "--property=ActiveEnterTimestamp",
156
+ ],
157
+ capture_output=True,
158
+ text=True,
159
+ )
160
+ if result.returncode == 0:
161
+ line = result.stdout.strip()
162
+ if "=" in line:
163
+ timestamp_str = line.split("=", 1)[1].strip()
164
+ if timestamp_str:
165
+ # Parse timestamp and calculate uptime
166
+ try:
167
+ # Format: "Thu 2026-02-05 16:23:51 CST"
168
+ from dateutil import parser
169
+ start_time = parser.parse(timestamp_str)
170
+ delta = datetime.now(start_time.tzinfo) - start_time
171
+ hours, remainder = divmod(int(delta.total_seconds()), 3600)
172
+ minutes, _ = divmod(remainder, 60)
173
+ if hours > 0:
174
+ return f"{hours}h {minutes}m"
175
+ return f"{minutes}m"
176
+ except Exception:
177
+ pass
178
+ return "unknown"
179
+ except Exception:
180
+ return "unknown"
181
+
182
+
183
+ def get_logs(lines: int = 20) -> str:
184
+ """Get recent service logs."""
185
+ try:
186
+ result = subprocess.run(
187
+ [
188
+ "journalctl", "--user", "-u", "speaksy.service",
189
+ "-n", str(lines), "--no-pager",
190
+ ],
191
+ capture_output=True,
192
+ text=True,
193
+ )
194
+ return result.stdout if result.returncode == 0 else "No logs available"
195
+ except Exception:
196
+ return "Unable to fetch logs"
197
+
198
+
199
+ def get_status() -> dict:
200
+ """Get comprehensive service status."""
201
+ return {
202
+ "installed": is_installed(),
203
+ "running": is_running(),
204
+ "uptime": get_uptime() if is_running() else None,
205
+ }
@@ -0,0 +1,216 @@
1
+ """Interactive setup wizard for Speaksy."""
2
+
3
+ import shutil
4
+ import subprocess
5
+
6
+ import httpx
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ from rich.prompt import Confirm, Prompt
10
+
11
+ from speaksy import config
12
+ from speaksy import service
13
+
14
+ console = Console()
15
+
16
+
17
+ def check_system_deps() -> dict:
18
+ """Check if required system dependencies are installed."""
19
+ deps = {
20
+ "xclip": shutil.which("xclip") is not None,
21
+ "xdotool": shutil.which("xdotool") is not None,
22
+ }
23
+
24
+ # Check audio
25
+ try:
26
+ import sounddevice as sd
27
+ devices = sd.query_devices()
28
+ deps["audio"] = any(d.get("max_input_channels", 0) > 0 for d in devices)
29
+ except Exception:
30
+ deps["audio"] = False
31
+
32
+ return deps
33
+
34
+
35
+ def install_missing_deps(missing: list) -> bool:
36
+ """Attempt to install missing dependencies."""
37
+ console.print("\n[yellow]trying to install missing deps...[/yellow]")
38
+
39
+ apt_packages = []
40
+ if "xclip" in missing:
41
+ apt_packages.append("xclip")
42
+ if "xdotool" in missing:
43
+ apt_packages.append("xdotool")
44
+
45
+ if apt_packages:
46
+ try:
47
+ cmd = ["sudo", "apt", "install", "-y"] + apt_packages
48
+ result = subprocess.run(cmd, capture_output=True, text=True)
49
+ if result.returncode != 0:
50
+ console.print("[red]failed to install. try manually:[/red]")
51
+ console.print(f"[dim]sudo apt install {' '.join(apt_packages)}[/dim]")
52
+ return False
53
+ except Exception:
54
+ console.print("[red]couldn't run apt. install manually:[/red]")
55
+ console.print(f"[dim]sudo apt install {' '.join(apt_packages)}[/dim]")
56
+ return False
57
+
58
+ return True
59
+
60
+
61
+ def validate_api_key(api_key: str) -> tuple:
62
+ """Validate the Groq API key by making a test request."""
63
+ try:
64
+ resp = httpx.get(
65
+ "https://api.groq.com/openai/v1/models",
66
+ headers={"Authorization": f"Bearer {api_key}"},
67
+ timeout=10.0,
68
+ )
69
+ if resp.status_code == 200:
70
+ return True, None
71
+ elif resp.status_code == 401:
72
+ return False, "invalid_api_key"
73
+ else:
74
+ return False, f"api_error_{resp.status_code}"
75
+ except httpx.TimeoutException:
76
+ return False, "timeout"
77
+ except Exception as e:
78
+ return False, str(e)
79
+
80
+
81
+ def run_setup():
82
+ """Run the interactive setup wizard."""
83
+ console.print()
84
+ console.print("[bold cyan]aight let's get you set up real quick[/bold cyan]")
85
+ console.print()
86
+ console.print("[dim]" + "━" * 40 + "[/dim]")
87
+ console.print()
88
+
89
+ # Check system deps
90
+ console.print("[bold]checking system deps...[/bold]")
91
+ deps = check_system_deps()
92
+
93
+ all_good = True
94
+ for dep, found in deps.items():
95
+ if found:
96
+ console.print(f" [green]├─ {dep}: found ✓[/green]")
97
+ else:
98
+ console.print(f" [red]├─ {dep}: missing ✗[/red]")
99
+ all_good = False
100
+
101
+ if not all_good:
102
+ missing = [d for d, found in deps.items() if not found]
103
+ if "audio" in missing:
104
+ console.print("\n[red]no audio input detected. check your mic![/red]")
105
+ missing.remove("audio")
106
+
107
+ if missing:
108
+ if Confirm.ask("\n[yellow]want me to try installing missing deps?[/yellow]"):
109
+ if not install_missing_deps(missing):
110
+ return False
111
+ # Recheck
112
+ deps = check_system_deps()
113
+ if not all(deps.values()):
114
+ console.print("\n[red]still missing deps. fix and try again[/red]")
115
+ return False
116
+ else:
117
+ console.print("\n[yellow]install them manually and run /setup again[/yellow]")
118
+ return False
119
+
120
+ console.print()
121
+
122
+ # Get API key
123
+ console.print("[bold]drop your Groq API key[/bold]")
124
+ console.print("[dim](get one free at console.groq.com/keys)[/dim]")
125
+ console.print()
126
+
127
+ while True:
128
+ api_key = Prompt.ask(" [cyan]key[/cyan]", password=True)
129
+
130
+ if not api_key:
131
+ console.print(" [red]need a key to continue[/red]")
132
+ continue
133
+
134
+ if not api_key.startswith("gsk_"):
135
+ console.print(" [yellow]hmm that doesn't look like a groq key[/yellow]")
136
+ console.print(" [dim]should start with gsk_[/dim]")
137
+ continue
138
+
139
+ console.print(" [dim]validating...[/dim]", end=" ")
140
+ valid, error = validate_api_key(api_key)
141
+
142
+ if valid:
143
+ console.print("[green]we're in ✓[/green]")
144
+ break
145
+ else:
146
+ console.print(f"[red]nah that ain't it[/red]")
147
+ if error == "invalid_api_key":
148
+ console.print(" [dim]double check your key and try again[/dim]")
149
+ else:
150
+ console.print(f" [dim]error: {error}[/dim]")
151
+
152
+ # Save API key
153
+ config.save_api_key(api_key)
154
+ console.print()
155
+
156
+ # Hotkey customization
157
+ if Confirm.ask("[bold]wanna customize hotkeys?[/bold]", default=False):
158
+ console.print()
159
+ console.print("[dim]examples: Key.ctrl_r, Key.f8, Key.alt_l[/dim]")
160
+
161
+ current_ptt, current_toggle = config.get_hotkeys()
162
+
163
+ ptt = Prompt.ask(
164
+ f" [cyan]push-to-talk[/cyan] [dim](default: {current_ptt})[/dim]",
165
+ default=current_ptt,
166
+ )
167
+ toggle = Prompt.ask(
168
+ f" [cyan]toggle mode[/cyan] [dim](default: {current_toggle})[/dim]",
169
+ default=current_toggle,
170
+ )
171
+
172
+ config.set_hotkeys(ptt, toggle)
173
+ console.print(" [green]locked in ✓[/green]")
174
+ else:
175
+ # Save default config
176
+ cfg = config.load_config()
177
+ config.save_config(cfg)
178
+
179
+ console.print()
180
+
181
+ # Install service
182
+ console.print("[bold]installing service...[/bold]")
183
+ if service.install_service():
184
+ console.print(" [green]└─ auto-start on login: enabled ✓[/green]")
185
+ else:
186
+ console.print(" [red]└─ failed to install service[/red]")
187
+ return False
188
+
189
+ # Start service
190
+ console.print()
191
+ console.print("[dim]starting speaksy...[/dim]")
192
+ if service.start_service():
193
+ console.print("[green]service started ✓[/green]")
194
+ else:
195
+ console.print("[red]failed to start service[/red]")
196
+ return False
197
+
198
+ console.print()
199
+ console.print("[dim]" + "━" * 40 + "[/dim]")
200
+ console.print()
201
+
202
+ # Success message
203
+ ptt, toggle = config.get_hotkeys()
204
+ ptt_display = ptt.replace("Key.", "").replace("_", " ").title()
205
+ toggle_display = toggle.replace("Key.", "").upper()
206
+
207
+ console.print("[bold green]you're all set fam![/bold green]")
208
+ console.print()
209
+ console.print(f" [cyan]hold {ptt_display}[/cyan] = push-to-talk")
210
+ console.print(f" [cyan]tap {toggle_display}[/cyan] = toggle on/off")
211
+ console.print()
212
+ console.print("[dim]speaksy is now running in the background[/dim]")
213
+ console.print("[dim]just start talking wherever you type ✨[/dim]")
214
+ console.print()
215
+
216
+ return True
@@ -0,0 +1,246 @@
1
+ Metadata-Version: 2.4
2
+ Name: speaksy
3
+ Version: 0.1.0
4
+ Summary: Voice typing for Linux. Talk it. Type it. Ship it.
5
+ Project-URL: Homepage, https://github.com/oneKn8/speaksy
6
+ Project-URL: Repository, https://github.com/oneKn8/speaksy
7
+ Project-URL: Issues, https://github.com/oneKn8/speaksy/issues
8
+ Author: oneknight
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: dictation,linux,speech-to-text,typing,voice,whisper
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
23
+ Classifier: Topic :: Text Processing
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: faster-whisper>=1.1.0
26
+ Requires-Dist: httpx>=0.27.0
27
+ Requires-Dist: numpy>=1.24.0
28
+ Requires-Dist: pillow>=10.0.0
29
+ Requires-Dist: pynput>=1.7.0
30
+ Requires-Dist: pystray>=0.19.0
31
+ Requires-Dist: python-dotenv>=1.0.0
32
+ Requires-Dist: pyyaml>=6.0
33
+ Requires-Dist: rich>=13.0.0
34
+ Requires-Dist: sounddevice>=0.5.0
35
+ Description-Content-Type: text/markdown
36
+
37
+ <p align="center">
38
+ <img src="https://img.shields.io/badge/speaksy-voice%20typing-blueviolet?style=for-the-badge&logo=microphone" alt="speaksy">
39
+ </p>
40
+
41
+ <h1 align="center">speaksy</h1>
42
+
43
+ <p align="center">
44
+ <strong>talk it. type it. ship it.</strong>
45
+ </p>
46
+
47
+ <p align="center">
48
+ <a href="https://github.com/oneKn8/speaksy/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
49
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="Python"></a>
50
+ <a href="https://github.com/oneKn8/speaksy"><img src="https://img.shields.io/badge/platform-Linux-orange.svg" alt="Platform"></a>
51
+ <a href="https://console.groq.com"><img src="https://img.shields.io/badge/powered%20by-Groq-ff6600.svg" alt="Groq"></a>
52
+ </p>
53
+
54
+ <p align="center">
55
+ <em>Voice typing for Linux that actually works.<br>Hold a key, speak, release — your words appear wherever you're typing.</em>
56
+ </p>
57
+
58
+ ---
59
+
60
+ ## Demo
61
+
62
+ ```
63
+ $ speaksy
64
+
65
+ ╭────────────────────────────────────────╮
66
+ │ SPEAKSY │
67
+ │ talk it. type it. ship it. │
68
+ ╰────────────────────────────────────────╯
69
+
70
+ Status: vibing
71
+ Hotkeys: Right Ctrl (hold) | F8 (toggle)
72
+
73
+ speaksy> _
74
+ ```
75
+
76
+ <!-- TODO: Add demo GIF here -->
77
+ <!-- ![Demo](assets/demo.gif) -->
78
+
79
+ ---
80
+
81
+ ## Quick Start
82
+
83
+ ```bash
84
+ # Install
85
+ pipx install speaksy
86
+
87
+ # Run (interactive setup on first launch)
88
+ speaksy
89
+ ```
90
+
91
+ That's it. 30 seconds to voice typing.
92
+
93
+ ---
94
+
95
+ ## Features
96
+
97
+ | | Feature | Description |
98
+ |---|---------|-------------|
99
+ | **Speed** | < 1 second latency | Groq's Whisper API is blazing fast |
100
+ | **Smart** | AI text cleanup | Fixes grammar, removes "um", "uh", "like" |
101
+ | **Free** | No credit card | Groq's free tier is generous |
102
+ | **Offline** | Local fallback | Works without internet via faster-whisper |
103
+ | **Private** | Privacy mode | Keep voice 100% on your machine |
104
+ | **Auto** | Runs on login | Always ready when you are |
105
+
106
+ ---
107
+
108
+ ## How It Works
109
+
110
+ ```
111
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
112
+ │ Hold Key │ -> │ Speak │ -> │ Release │ -> │ Text Appears│
113
+ │ (Right Ctrl) │ naturally │ │ key │ │ at cursor │
114
+ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
115
+ |
116
+ v
117
+ ┌─────────────────┐
118
+ │ Groq Whisper │
119
+ │ + LLM cleanup │
120
+ └─────────────────┘
121
+ ```
122
+
123
+ 1. Press hotkey (Right Ctrl = hold, F8 = toggle)
124
+ 2. Speak naturally
125
+ 3. Release — text appears in < 1 second
126
+
127
+ Works everywhere: browser, terminal, IDE, Slack, Discord, anywhere you type.
128
+
129
+ ---
130
+
131
+ ## Commands
132
+
133
+ Run `speaksy` to open the interactive CLI:
134
+
135
+ | Command | Description |
136
+ |---------|-------------|
137
+ | `/setup` | Configure API key & hotkeys |
138
+ | `/start` | Start voice typing |
139
+ | `/stop` | Take a break |
140
+ | `/status` | Check the vibe |
141
+ | `/logs` | View receipts |
142
+ | `/config` | Tweak settings |
143
+ | `/help` | Get backup |
144
+ | `/quit` | Peace out |
145
+
146
+ ---
147
+
148
+ ## Requirements
149
+
150
+ - **OS:** Linux (X11 or XWayland)
151
+ - **Python:** 3.10+
152
+ - **API Key:** Free from [console.groq.com](https://console.groq.com)
153
+
154
+ System dependencies (auto-installed during setup):
155
+ ```bash
156
+ sudo apt install xclip xdotool
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Privacy Mode
162
+
163
+ By default, audio goes to Groq for fast transcription. Want to keep it local?
164
+
165
+ ```
166
+ speaksy> /config
167
+ # Select "Privacy mode" -> "local"
168
+ ```
169
+
170
+ Local mode uses [faster-whisper](https://github.com/SYSTRAN/faster-whisper) on your CPU. Slower (~3-5s) but your voice never leaves your machine.
171
+
172
+ ---
173
+
174
+ ## Troubleshooting
175
+
176
+ <details>
177
+ <summary><strong>No audio input detected</strong></summary>
178
+
179
+ - Check your mic is connected
180
+ - Run `arecord -l` to list audio devices
181
+ </details>
182
+
183
+ <details>
184
+ <summary><strong>Text not appearing</strong></summary>
185
+
186
+ - Install dependencies: `sudo apt install xclip xdotool`
187
+ - Some pure Wayland apps may not work with xdotool
188
+ </details>
189
+
190
+ <details>
191
+ <summary><strong>Service won't start</strong></summary>
192
+
193
+ - Check logs: run `speaksy` then `/logs`
194
+ - Verify API key at console.groq.com
195
+ </details>
196
+
197
+ ---
198
+
199
+ ## Uninstall
200
+
201
+ ```bash
202
+ # Stop service
203
+ speaksy
204
+ # > /stop
205
+ # > /quit
206
+
207
+ # Remove package
208
+ pipx uninstall speaksy
209
+
210
+ # Remove config (optional)
211
+ rm -rf ~/.config/speaksy
212
+ rm ~/.config/systemd/user/speaksy.service
213
+ systemctl --user daemon-reload
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Tech Stack
219
+
220
+ - **STT:** [Groq Whisper API](https://groq.com) / [faster-whisper](https://github.com/SYSTRAN/faster-whisper)
221
+ - **LLM:** Llama 3.1 8B (via Groq) for text cleanup
222
+ - **Audio:** [sounddevice](https://python-sounddevice.readthedocs.io/)
223
+ - **Hotkeys:** [pynput](https://pynput.readthedocs.io/)
224
+ - **CLI:** [Rich](https://rich.readthedocs.io/)
225
+
226
+ ---
227
+
228
+ ## Contributing
229
+
230
+ PRs and issues welcome!
231
+
232
+ <a href="https://github.com/oneKn8/speaksy/issues">Report Bug</a>
233
+ ·
234
+ <a href="https://github.com/oneKn8/speaksy/issues">Request Feature</a>
235
+
236
+ ---
237
+
238
+ ## License
239
+
240
+ MIT - do whatever you want with it.
241
+
242
+ ---
243
+
244
+ <p align="center">
245
+ <sub>Built with caffeine and voice commands</sub>
246
+ </p>
@@ -0,0 +1,13 @@
1
+ speaksy/__init__.py,sha256=eG5kdi4U1tayyWwhBUByiHrdFcZpCJ6bninHJclA4VU,63
2
+ speaksy/__main__.py,sha256=Y5c4dtERDNv0x1C-HcP1nauSAbssfyuP7MlyN8l1M28,110
3
+ speaksy/cli.py,sha256=Fd-g5g11RMFyKgpdYGtvQAuYIVY6BcL7nyOwFa5GMHM,10225
4
+ speaksy/config.py,sha256=bGiATdV79E27pu7TQTm2VxDDdAKYdzjQrrIbu5GCz2o,4163
5
+ speaksy/core.py,sha256=xkE0KtS3paGzZSnc481R47bECnns3_wSXkysSW66Uzs,16987
6
+ speaksy/runner.py,sha256=l5aiczErHVLBc3Ir3XIfqxZUAiq4tN6T8OCVqTWwnEU,643
7
+ speaksy/service.py,sha256=EwQs3yB3Ys-WwvP0UI2K_U5_zJqJCJLkK29_MTTcVTE,5449
8
+ speaksy/setup_wizard.py,sha256=-Glu74R-ZKL097Ji8vOmEDC7salaSk9kueONxWs0t4c,7033
9
+ speaksy-0.1.0.dist-info/METADATA,sha256=JwjfdFK_CSimqUGGZC0VwJxW1lvWmrCENw9B56cFCBw,6926
10
+ speaksy-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
11
+ speaksy-0.1.0.dist-info/entry_points.txt,sha256=LFch1KPmgD4hHNVXY_DSJ24fPnB8GaVKF4aKUrqDjzs,45
12
+ speaksy-0.1.0.dist-info/licenses/LICENSE,sha256=KgmDIQPh17s8aGNha9ebeUXZHi533ew6VyCLcY7IJE4,1066
13
+ speaksy-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ speaksy = speaksy.cli:main