grok-assistant 1.0__tar.gz

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 (108) hide show
  1. grok_assistant-1.0/LICENSE.md +39 -0
  2. grok_assistant-1.0/PKG-INFO +58 -0
  3. grok_assistant-1.0/README.md +39 -0
  4. grok_assistant-1.0/pyproject.toml +51 -0
  5. grok_assistant-1.0/setup.cfg +4 -0
  6. grok_assistant-1.0/setup.py +54 -0
  7. grok_assistant-1.0/src/grok_assistant/__init__.py +4 -0
  8. grok_assistant-1.0/src/grok_assistant/__main__.py +110 -0
  9. grok_assistant-1.0/src/grok_assistant/_bundle/LICENSE.md +39 -0
  10. grok_assistant-1.0/src/grok_assistant/_bundle/docs/img/grok-mark.png +0 -0
  11. grok_assistant-1.0/src/grok_assistant/_bundle/docs/img/grok.ico +0 -0
  12. grok_assistant-1.0/src/grok_assistant/_bundle/listeners/dictation.ps1 +80 -0
  13. grok_assistant-1.0/src/grok_assistant/_bundle/listeners/install_speech.ps1 +19 -0
  14. grok_assistant-1.0/src/grok_assistant/_bundle/scripts/build_exe.ps1 +21 -0
  15. grok_assistant-1.0/src/grok_assistant/_bundle/scripts/speak.ps1 +14 -0
  16. grok_assistant-1.0/src/grok_assistant/_bundle/scripts/voices.ps1 +6 -0
  17. grok_assistant-1.0/src/grok_assistant/build_stamp.txt +2 -0
  18. grok_assistant-1.0/src/grok_assistant/buildinfo.py +69 -0
  19. grok_assistant-1.0/src/grok_assistant/cloud/__init__.py +1 -0
  20. grok_assistant-1.0/src/grok_assistant/cloud/account_agents.py +92 -0
  21. grok_assistant-1.0/src/grok_assistant/cloud/account_usage.py +49 -0
  22. grok_assistant-1.0/src/grok_assistant/cloud/grok_cli.py +166 -0
  23. grok_assistant-1.0/src/grok_assistant/cloud/prompts.py +61 -0
  24. grok_assistant-1.0/src/grok_assistant/cloud/setup_grok.py +167 -0
  25. grok_assistant-1.0/src/grok_assistant/console.py +26 -0
  26. grok_assistant-1.0/src/grok_assistant/house/__init__.py +1 -0
  27. grok_assistant-1.0/src/grok_assistant/house/helptext.py +155 -0
  28. grok_assistant-1.0/src/grok_assistant/house/marketplace.py +531 -0
  29. grok_assistant-1.0/src/grok_assistant/house/music.py +166 -0
  30. grok_assistant-1.0/src/grok_assistant/house/personality.py +430 -0
  31. grok_assistant-1.0/src/grok_assistant/house/updates.py +165 -0
  32. grok_assistant-1.0/src/grok_assistant/i18n.py +172 -0
  33. grok_assistant-1.0/src/grok_assistant/lang/de.json +393 -0
  34. grok_assistant-1.0/src/grok_assistant/lang/en.json +393 -0
  35. grok_assistant-1.0/src/grok_assistant/lang/es.json +366 -0
  36. grok_assistant-1.0/src/grok_assistant/lang/fr.json +393 -0
  37. grok_assistant-1.0/src/grok_assistant/lines/banter.json +638 -0
  38. grok_assistant-1.0/src/grok_assistant/lines/hellos-es.txt +188 -0
  39. grok_assistant-1.0/src/grok_assistant/lines/waits-es.txt +170 -0
  40. grok_assistant-1.0/src/grok_assistant/listening/__init__.py +1 -0
  41. grok_assistant-1.0/src/grok_assistant/listening/ear_score.py +87 -0
  42. grok_assistant-1.0/src/grok_assistant/listening/enroll_audio.py +259 -0
  43. grok_assistant-1.0/src/grok_assistant/listening/kroko_ear.py +324 -0
  44. grok_assistant-1.0/src/grok_assistant/listening/listen.py +217 -0
  45. grok_assistant-1.0/src/grok_assistant/listening/offline_ear.py +243 -0
  46. grok_assistant-1.0/src/grok_assistant/listening/refine.py +131 -0
  47. grok_assistant-1.0/src/grok_assistant/listening/voiceprint.py +75 -0
  48. grok_assistant-1.0/src/grok_assistant/mind/__init__.py +1 -0
  49. grok_assistant-1.0/src/grok_assistant/mind/local_llm.py +152 -0
  50. grok_assistant-1.0/src/grok_assistant/notebook/__init__.py +1 -0
  51. grok_assistant-1.0/src/grok_assistant/notebook/auth.py +48 -0
  52. grok_assistant-1.0/src/grok_assistant/notebook/settings.py +100 -0
  53. grok_assistant-1.0/src/grok_assistant/notebook/store.py +669 -0
  54. grok_assistant-1.0/src/grok_assistant/paths.py +81 -0
  55. grok_assistant-1.0/src/grok_assistant/quiet.py +15 -0
  56. grok_assistant-1.0/src/grok_assistant/rules/__init__.py +1 -0
  57. grok_assistant-1.0/src/grok_assistant/rules/brain.py +72 -0
  58. grok_assistant-1.0/src/grok_assistant/rules/common.py +100 -0
  59. grok_assistant-1.0/src/grok_assistant/rules/hub.py +338 -0
  60. grok_assistant-1.0/src/grok_assistant/rules/match.py +523 -0
  61. grok_assistant-1.0/src/grok_assistant/rules/orders.py +235 -0
  62. grok_assistant-1.0/src/grok_assistant/rules/prints.py +173 -0
  63. grok_assistant-1.0/src/grok_assistant/rules/status.py +159 -0
  64. grok_assistant-1.0/src/grok_assistant/rules/talk.py +451 -0
  65. grok_assistant-1.0/src/grok_assistant/rules/trace.py +78 -0
  66. grok_assistant-1.0/src/grok_assistant/speaking/__init__.py +1 -0
  67. grok_assistant-1.0/src/grok_assistant/speaking/banter.py +81 -0
  68. grok_assistant-1.0/src/grok_assistant/speaking/speech.py +222 -0
  69. grok_assistant-1.0/src/grok_assistant/startup.py +50 -0
  70. grok_assistant-1.0/src/grok_assistant/textutil.py +55 -0
  71. grok_assistant-1.0/src/grok_assistant/ui/__init__.py +1 -0
  72. grok_assistant-1.0/src/grok_assistant/ui/actions.py +624 -0
  73. grok_assistant-1.0/src/grok_assistant/ui/app.py +36 -0
  74. grok_assistant-1.0/src/grok_assistant/ui/chrome.py +59 -0
  75. grok_assistant-1.0/src/grok_assistant/ui/deps.py +39 -0
  76. grok_assistant-1.0/src/grok_assistant/ui/ears.py +156 -0
  77. grok_assistant-1.0/src/grok_assistant/ui/flow.py +187 -0
  78. grok_assistant-1.0/src/grok_assistant/ui/market.py +413 -0
  79. grok_assistant-1.0/src/grok_assistant/ui/menus.py +649 -0
  80. grok_assistant-1.0/src/grok_assistant/ui/prints_ui.py +379 -0
  81. grok_assistant-1.0/src/grok_assistant/ui/round.py +244 -0
  82. grok_assistant-1.0/src/grok_assistant/ui/theme.py +164 -0
  83. grok_assistant-1.0/src/grok_assistant/ui/themes/aurora.json +30 -0
  84. grok_assistant-1.0/src/grok_assistant/ui/themes/cobre.json +30 -0
  85. grok_assistant-1.0/src/grok_assistant/ui/themes/dia.json +30 -0
  86. grok_assistant-1.0/src/grok_assistant/ui/themes/lino.json +30 -0
  87. grok_assistant-1.0/src/grok_assistant/ui/themes/mar.json +30 -0
  88. grok_assistant-1.0/src/grok_assistant/ui/themes/noche.json +30 -0
  89. grok_assistant-1.0/src/grok_assistant/ui/themes/oliva.json +30 -0
  90. grok_assistant-1.0/src/grok_assistant/ui/win_tray.py +649 -0
  91. grok_assistant-1.0/src/grok_assistant/ui/window.py +538 -0
  92. grok_assistant-1.0/src/grok_assistant/ui/worker.py +253 -0
  93. grok_assistant-1.0/src/grok_assistant.egg-info/PKG-INFO +58 -0
  94. grok_assistant-1.0/src/grok_assistant.egg-info/SOURCES.txt +106 -0
  95. grok_assistant-1.0/src/grok_assistant.egg-info/dependency_links.txt +1 -0
  96. grok_assistant-1.0/src/grok_assistant.egg-info/entry_points.txt +2 -0
  97. grok_assistant-1.0/src/grok_assistant.egg-info/requires.txt +8 -0
  98. grok_assistant-1.0/src/grok_assistant.egg-info/top_level.txt +1 -0
  99. grok_assistant-1.0/tests/test_banter.py +86 -0
  100. grok_assistant-1.0/tests/test_cli.py +117 -0
  101. grok_assistant-1.0/tests/test_enroll.py +234 -0
  102. grok_assistant-1.0/tests/test_grok_setup.py +61 -0
  103. grok_assistant-1.0/tests/test_license.py +38 -0
  104. grok_assistant-1.0/tests/test_market.py +440 -0
  105. grok_assistant-1.0/tests/test_personality.py +177 -0
  106. grok_assistant-1.0/tests/test_rules.py +1109 -0
  107. grok_assistant-1.0/tests/test_theme.py +234 -0
  108. grok_assistant-1.0/tests/test_updates.py +119 -0
@@ -0,0 +1,39 @@
1
+ # Licencia y límites — Grok Assistant
2
+
3
+ Versión candidata 1.0. Aplicación experimental.
4
+
5
+ Autor: Antonio Castellon, Castellon.CH.
6
+
7
+ ## Qué es
8
+
9
+ Grok Assistant es un programa experimental para hablar con un asistente en este equipo. El audio se queda aquí. Cuando hace falta, el programa envía texto a Grok. Grok y sus herramientas son un servicio de un tercero, no de este programa.
10
+
11
+ ## Responsabilidad
12
+
13
+ Antonio Castellon no se hace responsable de cualquier fallo de seguridad o brecha que pueda darse por el uso de Grok y de sus herramientas. Eso incluye el acceso a la cuenta, lo que Grok busque o escriba, un archivo que cambie cuando esa opción esté permitida, y cualquier daño o pérdida que venga de ese uso.
14
+
15
+ El programa se ofrece tal cual, sin garantía. Quien lo ejecuta lo hace por su cuenta.
16
+
17
+ Si un administrador permite que Grok cambie archivos, esa elección se hace en este equipo. La shell sigue apagada. Aun así, el autor no responde de lo que Grok haga con las herramientas que se hayan dejado activas.
18
+
19
+ ## Texto que manda
20
+
21
+ Este archivo está en español. La traducción al inglés es una ayuda para leerlo. Si los dos textos no dicen lo mismo, manda el español.
22
+
23
+ ---
24
+
25
+ # License and limits — Grok Assistant
26
+
27
+ Release candidate 1.0. Experimental application.
28
+
29
+ Author: Antonio Castellon, Castellon.CH.
30
+
31
+ Grok Assistant is an experimental program for talking with an assistant on this computer. The audio stays here. When needed, the program sends text to Grok. Grok and its tools are a third-party service, not part of this program.
32
+
33
+ Antonio Castellon is not responsible for any security failure or breach that may come from using Grok and its tools. That includes access to the account, what Grok looks up or writes, a file it changes when that option is allowed, and any damage or loss that comes from that use.
34
+
35
+ The program is offered as it is, without warranty. Anyone who runs it does so on their own account.
36
+
37
+ If an administrator allows Grok to change files, that choice is made on this computer. The shell stays off. The author is still not answerable for what Grok does with the tools that were left on.
38
+
39
+ If the Spanish text and this English text differ, the Spanish text prevails.
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.4
2
+ Name: grok_assistant
3
+ Version: 1.0
4
+ Summary: A PC voice assistant that listens at home and only calls Grok on purpose.
5
+ Author: Antonio Castellon
6
+ Project-URL: Homepage, https://github.com/antonio-castellon/Grok_Assistant
7
+ Project-URL: Repository, https://github.com/antonio-castellon/Grok_Assistant
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE.md
11
+ Requires-Dist: pystray>=0.19
12
+ Requires-Dist: Pillow>=10
13
+ Requires-Dist: sherpa-onnx==1.13.8
14
+ Requires-Dist: sounddevice==0.5.6
15
+ Requires-Dist: numpy<2.5,>=2.2
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=8; extra == "dev"
18
+ Dynamic: license-file
19
+
20
+ [Leer en español](README.ES.md) · [Lire en français](README.FR.md) · [Auf Deutsch lesen](README.DE.md)
21
+
22
+ ![Grok mark, with Assistant underneath](docs/img/banner.jpg)
23
+
24
+ # Grok Assistant
25
+
26
+ Release candidate 1.0.
27
+
28
+ This is a voice companion for the moments when reading is difficult and the house is quiet. The laptop version is for an older person who already has a small computer nearby. The Raspberry Pi 4 version, with 4 GB of RAM, is the same assistant as a dedicated device, for a family that would rather not leave a laptop open. I plan to publish that code soon. The microphone can stay ready. The audio stays on the computer. Only text that was meant for the assistant is sent out.
29
+
30
+ I spent years waiting for the Amazon Echo to become a better listener. It never really became more than a speaker with a light ring, so I decided to build my own.
31
+
32
+ In my case, that person is my father. His eyesight is limited, and he spends many hours on his own. I wanted him to have a voice he could simply talk to — one that could answer questions and explain things without making him find a screen or read small text. That is now possible. Grok on this machine is also the foundation for the features and integrations I plan to add next.
33
+
34
+ While the program is running, it can keep listening. Speech is turned into text on this computer. Grok only receives text when the assistant has actually been addressed: after a greeting, for a question, for a command beginning with `comando`, or when someone asks for a song. Ordinary conversation stays in the local session. If a command is unclear or misheard, Grok can help interpret it, but the assistant still asks for a `sí` before doing anything.
35
+
36
+ That is the basic idea. Below are the listening window and the icon menu, with Escucha open. The pictures are the same in every language.
37
+
38
+ ![The window waiting, on the Simple tab](docs/img/app-window.png)
39
+
40
+ ![The icon menu, with Escucha open](docs/img/tray-menu.png)
41
+
42
+ The diagram below shows what happens to a spoken phrase from the moment it is heard.
43
+
44
+ ![The STT engine turns speech into text. Before a conversation, the local model checks for a complete command. During a conversation, it notes the line and the text goes to Grok.](docs/img/flow.svg)
45
+
46
+ In Settings, Grok only searches the internet. An administrator can allow Grok to change files on this machine. The shell stays off, and a relative path lands in the assistant data folder.
47
+
48
+ ## Read on
49
+
50
+ - [How to use it](docs/en/guide.md)
51
+ - [Look](docs/en/look.md)
52
+ - [Voice prints and listeners](docs/en/prints.md)
53
+ - [Sessions and agents](docs/en/sessions.md)
54
+ - [Why Grok, and not another chat window](docs/en/why-grok.md)
55
+ - [What is actually running](docs/en/pieces.md)
56
+ - [What you can say](docs/en/saying.md)
57
+ - [Run the executable](docs/en/run.md)
58
+ - [Run it from the source tree](docs/en/source.md)
@@ -0,0 +1,39 @@
1
+ [Leer en español](README.ES.md) · [Lire en français](README.FR.md) · [Auf Deutsch lesen](README.DE.md)
2
+
3
+ ![Grok mark, with Assistant underneath](docs/img/banner.jpg)
4
+
5
+ # Grok Assistant
6
+
7
+ Release candidate 1.0.
8
+
9
+ This is a voice companion for the moments when reading is difficult and the house is quiet. The laptop version is for an older person who already has a small computer nearby. The Raspberry Pi 4 version, with 4 GB of RAM, is the same assistant as a dedicated device, for a family that would rather not leave a laptop open. I plan to publish that code soon. The microphone can stay ready. The audio stays on the computer. Only text that was meant for the assistant is sent out.
10
+
11
+ I spent years waiting for the Amazon Echo to become a better listener. It never really became more than a speaker with a light ring, so I decided to build my own.
12
+
13
+ In my case, that person is my father. His eyesight is limited, and he spends many hours on his own. I wanted him to have a voice he could simply talk to — one that could answer questions and explain things without making him find a screen or read small text. That is now possible. Grok on this machine is also the foundation for the features and integrations I plan to add next.
14
+
15
+ While the program is running, it can keep listening. Speech is turned into text on this computer. Grok only receives text when the assistant has actually been addressed: after a greeting, for a question, for a command beginning with `comando`, or when someone asks for a song. Ordinary conversation stays in the local session. If a command is unclear or misheard, Grok can help interpret it, but the assistant still asks for a `sí` before doing anything.
16
+
17
+ That is the basic idea. Below are the listening window and the icon menu, with Escucha open. The pictures are the same in every language.
18
+
19
+ ![The window waiting, on the Simple tab](docs/img/app-window.png)
20
+
21
+ ![The icon menu, with Escucha open](docs/img/tray-menu.png)
22
+
23
+ The diagram below shows what happens to a spoken phrase from the moment it is heard.
24
+
25
+ ![The STT engine turns speech into text. Before a conversation, the local model checks for a complete command. During a conversation, it notes the line and the text goes to Grok.](docs/img/flow.svg)
26
+
27
+ In Settings, Grok only searches the internet. An administrator can allow Grok to change files on this machine. The shell stays off, and a relative path lands in the assistant data folder.
28
+
29
+ ## Read on
30
+
31
+ - [How to use it](docs/en/guide.md)
32
+ - [Look](docs/en/look.md)
33
+ - [Voice prints and listeners](docs/en/prints.md)
34
+ - [Sessions and agents](docs/en/sessions.md)
35
+ - [Why Grok, and not another chat window](docs/en/why-grok.md)
36
+ - [What is actually running](docs/en/pieces.md)
37
+ - [What you can say](docs/en/saying.md)
38
+ - [Run the executable](docs/en/run.md)
39
+ - [Run it from the source tree](docs/en/source.md)
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "grok_assistant"
3
+ version = "1.0"
4
+ description = "A PC voice assistant that listens at home and only calls Grok on purpose."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license-files = ["LICENSE.md"]
8
+ authors = [{name = "Antonio Castellon"}]
9
+ dependencies = [
10
+ "pystray>=0.19",
11
+ "Pillow>=10",
12
+ "sherpa-onnx==1.13.8",
13
+ "sounddevice==0.5.6",
14
+ "numpy>=2.2,<2.5",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["pytest>=8"]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/antonio-castellon/Grok_Assistant"
22
+ Repository = "https://github.com/antonio-castellon/Grok_Assistant"
23
+
24
+ [project.scripts]
25
+ grok-assistant = "grok_assistant.__main__:main"
26
+
27
+ [build-system]
28
+ requires = ["setuptools>=68"]
29
+ build-backend = "setuptools.build_meta"
30
+
31
+ [tool.setuptools]
32
+ include-package-data = true
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+
37
+ [tool.setuptools.package-data]
38
+ grok_assistant = [
39
+ "lines/*",
40
+ "lang/*",
41
+ "ui/themes/*",
42
+ "build_stamp.txt",
43
+ "_bundle/scripts/*",
44
+ "_bundle/listeners/*",
45
+ "_bundle/docs/img/*",
46
+ "_bundle/LICENSE.md",
47
+ ]
48
+
49
+ [tool.pytest.ini_options]
50
+ testpaths = ["tests"]
51
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,54 @@
1
+ """Stage the files a pip install needs, then let pyproject.toml describe the package."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+ import shutil
6
+
7
+ from setuptools import setup
8
+
9
+ ROOT = Path(__file__).resolve().parent
10
+ sys.path.insert(0, str(ROOT / "src"))
11
+ PACKAGE = ROOT / "src" / "grok_assistant"
12
+ BUNDLE = PACKAGE / "_bundle"
13
+
14
+
15
+ def stage() -> None:
16
+ """Copy repo files into the package. A wheel built from the sdist already has them."""
17
+ from grok_assistant.buildinfo import write_stamp
18
+
19
+ speak = ROOT / "scripts" / "speak.ps1"
20
+ if not speak.is_file():
21
+ if not (BUNDLE / "scripts" / "speak.ps1").is_file():
22
+ raise SystemExit("scripts/speak.ps1 is missing")
23
+ return
24
+ if BUNDLE.exists():
25
+ shutil.rmtree(BUNDLE)
26
+ shutil.copytree(ROOT / "scripts", BUNDLE / "scripts")
27
+ shutil.copytree(ROOT / "listeners", BUNDLE / "listeners")
28
+ images = BUNDLE / "docs" / "img"
29
+ images.mkdir(parents=True)
30
+ for name in ("grok.ico", "grok-mark.png"):
31
+ source = ROOT / "docs" / "img" / name
32
+ if source.is_file():
33
+ shutil.copy2(source, images / name)
34
+ shutil.copy2(ROOT / "LICENSE.md", BUNDLE / "LICENSE.md")
35
+ write_stamp(PACKAGE / "build_stamp.txt")
36
+
37
+
38
+ def packaged_files() -> list[str]:
39
+ names: list[str] = []
40
+ for folder in ("lines", "lang", "ui/themes", "_bundle"):
41
+ base = PACKAGE / folder
42
+ if not base.exists():
43
+ continue
44
+ for path in base.rglob("*"):
45
+ if path.is_file():
46
+ names.append(path.relative_to(PACKAGE).as_posix())
47
+ stamp = PACKAGE / "build_stamp.txt"
48
+ if stamp.is_file():
49
+ names.append("build_stamp.txt")
50
+ return names
51
+
52
+
53
+ stage()
54
+ setup(package_data={"grok_assistant": packaged_files()})
@@ -0,0 +1,4 @@
1
+ """A voice assistant that listens at home and bothers the cloud on purpose."""
2
+
3
+ __version__ = "1.0"
4
+ __release__ = "v1.0-rc.2"
@@ -0,0 +1,110 @@
1
+ """Grok Assistant, the PC body of the experiment."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import faulthandler
6
+ import sys
7
+ import tempfile
8
+ import traceback
9
+ from pathlib import Path
10
+
11
+ from grok_assistant.paths import bundle_root, default_data_dir, load_lines
12
+
13
+ _FAULT_LOG = None
14
+
15
+
16
+ def main(argv: list[str] | None = None) -> None:
17
+ try:
18
+ folder = default_data_dir()
19
+ folder.mkdir(parents=True, exist_ok=True)
20
+ crash = folder / "crash.log"
21
+ # The file has to stay referenced or a later native fault has nowhere to land.
22
+ global _FAULT_LOG
23
+ _FAULT_LOG = crash.open("a", encoding="utf-8")
24
+ faulthandler.enable(_FAULT_LOG, all_threads=True)
25
+ except OSError:
26
+ pass
27
+ try:
28
+ _main(argv)
29
+ except Exception:
30
+ path = _crash_log()
31
+ _tell_crash(path)
32
+ raise SystemExit(1)
33
+
34
+
35
+ def _main(argv: list[str] | None) -> None:
36
+ args = list(sys.argv[1:] if argv is None else argv)
37
+ if "--check" in args:
38
+ _write_check()
39
+ return
40
+ if "--console" in args:
41
+ from grok_assistant.console import run
42
+ run()
43
+ return
44
+ from grok_assistant.cloud.setup_grok import ensure_grok
45
+ from grok_assistant.ui.app import run
46
+
47
+ ensure_grok()
48
+ run()
49
+
50
+
51
+ def _crash_log() -> Path:
52
+ folder = default_data_dir()
53
+ folder.mkdir(parents=True, exist_ok=True)
54
+ path = folder / "crash.log"
55
+ with path.open("a", encoding="utf-8") as handle:
56
+ handle.write(traceback.format_exc())
57
+ handle.write("\n")
58
+ return path
59
+
60
+
61
+ def _tell_crash(path: Path) -> None:
62
+ try:
63
+ import tkinter as tk
64
+ from tkinter import messagebox
65
+
66
+ root = tk.Tk()
67
+ root.withdraw()
68
+ messagebox.showerror("Grok Assistant", f"Se cerró por un error.\n{path}")
69
+ root.destroy()
70
+ except Exception:
71
+ return
72
+
73
+
74
+ def _importable(name: str) -> bool:
75
+ try:
76
+ __import__(name)
77
+ except Exception:
78
+ return False
79
+ return True
80
+
81
+
82
+ def _kroko_ready() -> bool:
83
+ from grok_assistant.listening.kroko_ear import kroko_dir
84
+
85
+ return kroko_dir() is not None
86
+
87
+
88
+ def _write_check() -> None:
89
+ from grok_assistant import __version__
90
+ from grok_assistant.cloud.grok_cli import GrokCLI
91
+ from grok_assistant.cloud.setup_grok import probe
92
+
93
+ status = probe()
94
+ lines = [
95
+ f"version={__version__}",
96
+ "channel=release-candidate",
97
+ f"hellos={len(load_lines('hellos-es.txt'))}",
98
+ f"scripts={(bundle_root() / 'scripts' / 'speak.ps1').exists()}",
99
+ f"listeners={(bundle_root() / 'listeners' / 'dictation.ps1').exists()}",
100
+ f"grok={GrokCLI.find() or ''}",
101
+ f"state={status.state}",
102
+ f"kroko_model={_kroko_ready()}",
103
+ f"sherpa={_importable('sherpa_onnx')}",
104
+ f"mic={_importable('sounddevice')}",
105
+ ]
106
+ Path(tempfile.gettempdir(), "GrokAssistant-check.txt").write_text("\n".join(lines) + "\n", encoding="utf-8")
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()
@@ -0,0 +1,39 @@
1
+ # Licencia y límites — Grok Assistant
2
+
3
+ Versión candidata 1.0. Aplicación experimental.
4
+
5
+ Autor: Antonio Castellon, Castellon.CH.
6
+
7
+ ## Qué es
8
+
9
+ Grok Assistant es un programa experimental para hablar con un asistente en este equipo. El audio se queda aquí. Cuando hace falta, el programa envía texto a Grok. Grok y sus herramientas son un servicio de un tercero, no de este programa.
10
+
11
+ ## Responsabilidad
12
+
13
+ Antonio Castellon no se hace responsable de cualquier fallo de seguridad o brecha que pueda darse por el uso de Grok y de sus herramientas. Eso incluye el acceso a la cuenta, lo que Grok busque o escriba, un archivo que cambie cuando esa opción esté permitida, y cualquier daño o pérdida que venga de ese uso.
14
+
15
+ El programa se ofrece tal cual, sin garantía. Quien lo ejecuta lo hace por su cuenta.
16
+
17
+ Si un administrador permite que Grok cambie archivos, esa elección se hace en este equipo. La shell sigue apagada. Aun así, el autor no responde de lo que Grok haga con las herramientas que se hayan dejado activas.
18
+
19
+ ## Texto que manda
20
+
21
+ Este archivo está en español. La traducción al inglés es una ayuda para leerlo. Si los dos textos no dicen lo mismo, manda el español.
22
+
23
+ ---
24
+
25
+ # License and limits — Grok Assistant
26
+
27
+ Release candidate 1.0. Experimental application.
28
+
29
+ Author: Antonio Castellon, Castellon.CH.
30
+
31
+ Grok Assistant is an experimental program for talking with an assistant on this computer. The audio stays here. When needed, the program sends text to Grok. Grok and its tools are a third-party service, not part of this program.
32
+
33
+ Antonio Castellon is not responsible for any security failure or breach that may come from using Grok and its tools. That includes access to the account, what Grok looks up or writes, a file it changes when that option is allowed, and any damage or loss that comes from that use.
34
+
35
+ The program is offered as it is, without warranty. Anyone who runs it does so on their own account.
36
+
37
+ If an administrator allows Grok to change files, that choice is made on this computer. The shell stays off. The author is still not answerable for what Grok does with the tools that were left on.
38
+
39
+ If the Spanish text and this English text differ, the Spanish text prevails.
@@ -0,0 +1,80 @@
1
+ param(
2
+ [string]$Culture = "es-ES",
3
+ [string]$PauseFile = "",
4
+ [string]$ScoreDir = "",
5
+ [switch]$Probe
6
+ )
7
+ $ErrorActionPreference = "Stop"
8
+ Add-Type -AssemblyName System.Speech
9
+ # The string constructor wants a recognizer id such as MS-3082-80-DESK, not a culture name.
10
+ try {
11
+ $cultureInfo = [System.Globalization.CultureInfo]::new($Culture)
12
+ $engine = New-Object System.Speech.Recognition.SpeechRecognitionEngine($cultureInfo)
13
+ } catch {
14
+ $info = [System.Speech.Recognition.SpeechRecognitionEngine]::InstalledRecognizers() |
15
+ Where-Object { $_.Culture.Name -eq $Culture } |
16
+ Select-Object -First 1
17
+ if (-not $info) {
18
+ [Console]::Out.WriteLine("ERR:no-recognizer")
19
+ [Console]::Out.Flush()
20
+ exit 2
21
+ }
22
+ try {
23
+ $engine = New-Object System.Speech.Recognition.SpeechRecognitionEngine($info)
24
+ } catch {
25
+ [Console]::Out.WriteLine("ERR:no-recognizer")
26
+ [Console]::Out.Flush()
27
+ exit 2
28
+ }
29
+ }
30
+ if ($Probe) {
31
+ [Console]::Out.WriteLine("READY")
32
+ [Console]::Out.Flush()
33
+ exit 0
34
+ }
35
+ if ($ScoreDir) {
36
+ try {
37
+ $engine.LoadGrammar((New-Object System.Speech.Recognition.DictationGrammar))
38
+ } catch {
39
+ [Console]::Out.WriteLine("ERR:no-recognizer")
40
+ [Console]::Out.Flush()
41
+ exit 2
42
+ }
43
+ Get-ChildItem -LiteralPath $ScoreDir -Filter *.wav | Sort-Object Name | ForEach-Object {
44
+ $text = ""
45
+ try {
46
+ $engine.SetInputToWaveFile($_.FullName)
47
+ $result = $engine.Recognize()
48
+ if ($result -and $result.Text) { $text = $result.Text }
49
+ } catch {
50
+ $text = ""
51
+ }
52
+ [Console]::Out.WriteLine("LINE:" + $_.Name + "|" + $text)
53
+ [Console]::Out.Flush()
54
+ }
55
+ exit 0
56
+ }
57
+ try {
58
+ $engine.LoadGrammar((New-Object System.Speech.Recognition.DictationGrammar))
59
+ $engine.SetInputToDefaultAudioDevice()
60
+ } catch {
61
+ [Console]::Out.WriteLine("ERR:no-microphone")
62
+ [Console]::Out.Flush()
63
+ exit 3
64
+ }
65
+ $engine.EndSilenceTimeout = [TimeSpan]::FromMilliseconds(700)
66
+ $engine.EndSilenceTimeoutAmbiguous = [TimeSpan]::FromMilliseconds(700)
67
+ $engine.InitialSilenceTimeout = [TimeSpan]::FromSeconds(2)
68
+ [Console]::Out.WriteLine("READY")
69
+ [Console]::Out.Flush()
70
+ while ($true) {
71
+ if ($PauseFile -and (Test-Path -LiteralPath $PauseFile)) {
72
+ Start-Sleep -Milliseconds 200
73
+ continue
74
+ }
75
+ $result = $engine.Recognize()
76
+ if ($result -and $result.Text) {
77
+ [Console]::Out.WriteLine("LINE:" + $result.Text)
78
+ [Console]::Out.Flush()
79
+ }
80
+ }
@@ -0,0 +1,19 @@
1
+ param(
2
+ [Parameter(Mandatory = $true)]
3
+ [string]$Log
4
+ )
5
+ $ErrorActionPreference = "Stop"
6
+ try {
7
+ $name = "Language.Speech~~~es-ES~0.0.1.0"
8
+ $cap = Get-WindowsCapability -Online | Where-Object { $_.Name -eq $name } | Select-Object -First 1
9
+ if (-not $cap) {
10
+ throw "Windows no ofrece el idioma de voz es-ES en este equipo."
11
+ }
12
+ if ($cap.State -ne "Installed") {
13
+ Add-WindowsCapability -Online -Name $name | Out-Null
14
+ }
15
+ Set-Content -LiteralPath $Log -Value "OK" -Encoding utf8
16
+ } catch {
17
+ Set-Content -LiteralPath $Log -Value ("ERR:" + $_.Exception.Message) -Encoding utf8
18
+ exit 1
19
+ }
@@ -0,0 +1,21 @@
1
+ # Build the single GrokAssistant.exe. This is for making the program, not for installing it.
2
+ $ErrorActionPreference = "Stop"
3
+ $root = Resolve-Path (Join-Path $PSScriptRoot "..")
4
+ Set-Location $root
5
+ $python = $null
6
+ foreach ($candidate in @("python", "py")) {
7
+ if (Get-Command $candidate -ErrorAction SilentlyContinue) {
8
+ if ($candidate -eq "py") {
9
+ $python = & py -3 -c "import sys; print(sys.executable)"
10
+ } else {
11
+ $python = & python -c "import sys; print(sys.executable)"
12
+ }
13
+ break
14
+ }
15
+ }
16
+ if (-not $python) { throw "Python 3.11 or newer is required to build the executable." }
17
+ & $python -m pip install --upgrade pip pyinstaller pystray Pillow "sherpa-onnx==1.13.8" "sounddevice==0.5.6" "numpy>=2.2,<2.5"
18
+ & $python -m PyInstaller --noconfirm --clean (Join-Path $root "GrokAssistant.spec")
19
+ $exe = Join-Path $root "dist\GrokAssistant.exe"
20
+ if (-not (Test-Path $exe)) { throw "The executable was not created." }
21
+ Write-Host "Built $exe"
@@ -0,0 +1,14 @@
1
+ param(
2
+ [Parameter(Mandatory = $true)][string]$Path,
3
+ [int]$Volume = 70,
4
+ [string]$Voice = ""
5
+ )
6
+ $ErrorActionPreference = "Stop"
7
+ Add-Type -AssemblyName System.Speech
8
+ $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
9
+ $synth.Volume = [Math]::Max(0, [Math]::Min(100, $Volume))
10
+ if ($Voice) {
11
+ try { $synth.SelectVoice($Voice) } catch { }
12
+ }
13
+ $text = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
14
+ if ($text) { $synth.Speak($text) }
@@ -0,0 +1,6 @@
1
+ $ErrorActionPreference = "Stop"
2
+ Add-Type -AssemblyName System.Speech
3
+ $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
4
+ $synth.GetInstalledVoices() | ForEach-Object {
5
+ $_.VoiceInfo.Name + "|" + $_.VoiceInfo.Culture.Name
6
+ }
@@ -0,0 +1,2 @@
1
+ 73c54b29e221
2
+ 2026-10-02T07:32:53Z
@@ -0,0 +1,69 @@
1
+ """The build baked into this copy. About shows it. The updater compares it."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import subprocess
6
+ import sys
7
+ from datetime import datetime, timezone
8
+ from pathlib import Path
9
+
10
+
11
+ def _git(cwd: Path, *args: str) -> str:
12
+ try:
13
+ from grok_assistant.quiet import no_window
14
+
15
+ out = subprocess.check_output(
16
+ ["git", *args],
17
+ cwd=cwd,
18
+ text=True,
19
+ encoding="utf-8",
20
+ errors="replace",
21
+ stderr=subprocess.DEVNULL,
22
+ timeout=5,
23
+ **no_window(),
24
+ )
25
+ except (OSError, subprocess.SubprocessError):
26
+ return ""
27
+ return out.strip()
28
+
29
+
30
+ def _read_file(path: Path) -> tuple[str, str]:
31
+ try:
32
+ lines = [line.strip() for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]
33
+ except OSError:
34
+ return "", ""
35
+ ident = lines[0] if lines else ""
36
+ when = lines[1] if len(lines) > 1 else ""
37
+ if not ident or ident == "unknown":
38
+ return "", when
39
+ return ident, when
40
+
41
+
42
+ def read_stamp() -> tuple[str, str]:
43
+ """Return the build id and the UTC time it was made. Empty strings when unknown."""
44
+ if getattr(sys, "frozen", False):
45
+ return _read_file(Path(getattr(sys, "_MEIPASS")) / "grok_assistant" / "build_stamp.txt")
46
+ here = Path(__file__).resolve()
47
+ checkout = here.parents[2]
48
+ if (checkout / "scripts" / "speak.ps1").is_file():
49
+ ident = _git(checkout, "rev-parse", "--short=12", "HEAD")
50
+ if ident:
51
+ return ident, _git(checkout, "log", "-1", "--format=%cI")
52
+ return _read_file(here.parent / "build_stamp.txt")
53
+
54
+
55
+ def build_line() -> str:
56
+ ident, _when = read_stamp()
57
+ if not ident:
58
+ return "build —"
59
+ return f"build {ident}"
60
+
61
+
62
+ def write_stamp(path: Path | None = None) -> tuple[str, str]:
63
+ """Write the source commit and the current UTC time. The executable and the wheel both call this."""
64
+ dest = Path(path) if path else Path(__file__).resolve().parent / "build_stamp.txt"
65
+ checkout = Path(__file__).resolve().parents[2]
66
+ ident = _git(checkout, "rev-parse", "--short=12", "HEAD")
67
+ when = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
68
+ dest.write_text(f"{ident}\n{when}\n", encoding="utf-8")
69
+ return ident, when
@@ -0,0 +1 @@
1
+ """The door to the Grok executable."""