voice-vibecoder 2.9.1__tar.gz → 2.9.3__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 (43) hide show
  1. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/CHANGELOG.md +16 -0
  2. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/PKG-INFO +1 -1
  3. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/pyproject.toml +1 -1
  4. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/__init__.py +1 -1
  5. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/code_providers/openclaw.py +63 -16
  6. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/.github/workflows/ci.yml +0 -0
  7. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/.github/workflows/release.yml +0 -0
  8. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/.gitignore +0 -0
  9. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/LICENSE +0 -0
  10. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/README.md +0 -0
  11. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/app.py +0 -0
  12. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/index.html +0 -0
  13. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/app_config.py +0 -0
  14. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/audio.py +0 -0
  15. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/cli_serve.py +0 -0
  16. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/code_providers/__init__.py +0 -0
  17. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/code_providers/claude.py +0 -0
  18. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/code_providers/cursor.py +0 -0
  19. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/code_providers/registry.py +0 -0
  20. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/config.py +0 -0
  21. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/instances.py +0 -0
  22. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/server.py +0 -0
  23. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/session.py +0 -0
  24. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/tools/__init__.py +0 -0
  25. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/tools/agent_task.py +0 -0
  26. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/tools/definitions.py +0 -0
  27. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/tools/dispatch.py +0 -0
  28. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/tools/file_tree.py +0 -0
  29. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/tools/handlers.py +0 -0
  30. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/__init__.py +0 -0
  31. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/callbacks.py +0 -0
  32. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/diff_view.py +0 -0
  33. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/help_modal.py +0 -0
  34. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/panels.py +0 -0
  35. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/screen.py +0 -0
  36. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/setup.py +0 -0
  37. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/state.py +0 -0
  38. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/styles.py +0 -0
  39. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/update_modal.py +0 -0
  40. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/ui/wizard.py +0 -0
  41. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/voice_providers/__init__.py +0 -0
  42. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/voice_providers/openai.py +0 -0
  43. {voice_vibecoder-2.9.1 → voice_vibecoder-2.9.3}/src/voice_vibecoder/worktrees.py +0 -0
@@ -2,6 +2,22 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v2.9.3 (2026-02-21)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Implement correct device auth payload signing
10
+ ([`419bdfb`](https://github.com/snokam/voice-vibecoder/commit/419bdfb6e21f32fc56ecc9cdce4ba4b9fb64d6bd))
11
+
12
+
13
+ ## v2.9.2 (2026-02-21)
14
+
15
+ ### Bug Fixes
16
+
17
+ - Use mode=cli for gateway connect
18
+ ([`2606052`](https://github.com/snokam/voice-vibecoder/commit/2606052c59b7c6f2bd98f60c9e531d1838e9af3c))
19
+
20
+
5
21
  ## v2.9.1 (2026-02-21)
6
22
 
7
23
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voice-vibecoder
3
- Version: 2.9.1
3
+ Version: 2.9.3
4
4
  Summary: Olaf The Vibecoder — a voice-controlled coding assistant using OpenAI Realtime API + Claude Code
5
5
  Project-URL: Homepage, https://github.com/snokam/voice-vibecoder
6
6
  Project-URL: Repository, https://github.com/snokam/voice-vibecoder
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "voice-vibecoder"
7
- version = "2.9.1"
7
+ version = "2.9.3"
8
8
  description = "Olaf The Vibecoder — a voice-controlled coding assistant using OpenAI Realtime API + Claude Code"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -3,7 +3,7 @@
3
3
  A voice-controlled coding assistant using OpenAI Realtime API + Claude Code.
4
4
  """
5
5
 
6
- __version__ = "2.9.1"
6
+ __version__ = "2.9.3"
7
7
 
8
8
  from voice_vibecoder.app_config import VoiceConfig
9
9
  from voice_vibecoder.ui.screen import VoiceCodingScreen
@@ -11,6 +11,7 @@ import base64
11
11
  import json
12
12
  import logging
13
13
  import os
14
+ import time
14
15
  import uuid
15
16
  from typing import Any, Awaitable, Callable
16
17
 
@@ -57,11 +58,42 @@ def _load_device_keys(state_dir: str) -> tuple[str, Ed25519PrivateKey | None, st
57
58
  return device_id, private_key, token
58
59
 
59
60
 
60
- def _sign_challenge(private_key: Ed25519PrivateKey, nonce: str, ts: int) -> tuple[str, int]:
61
- """Sign the gateway challenge."""
62
- message = f"{nonce}:{ts}".encode()
63
- signature = private_key.sign(message)
64
- return base64.urlsafe_b64encode(signature).decode().rstrip("="), ts
61
+ def _build_auth_payload(
62
+ device_id: str,
63
+ client_id: str,
64
+ client_mode: str,
65
+ role: str,
66
+ scopes: list[str],
67
+ signed_at_ms: int,
68
+ token: str | None,
69
+ nonce: str | None,
70
+ ) -> str:
71
+ """Build the payload to sign for device auth.
72
+
73
+ Format (v2 with nonce):
74
+ v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
75
+ """
76
+ scopes_str = ",".join(scopes)
77
+ token_str = token or ""
78
+
79
+ parts = [
80
+ "v2",
81
+ device_id,
82
+ client_id,
83
+ client_mode,
84
+ role,
85
+ scopes_str,
86
+ str(signed_at_ms),
87
+ token_str,
88
+ nonce or "",
89
+ ]
90
+ return "|".join(parts)
91
+
92
+
93
+ def _sign_payload(private_key: Ed25519PrivateKey, payload: str) -> str:
94
+ """Sign the payload and return base64url-encoded signature."""
95
+ signature = private_key.sign(payload.encode())
96
+ return base64.urlsafe_b64encode(signature).decode().rstrip("=")
65
97
 
66
98
 
67
99
  class OpenClawRunner:
@@ -114,19 +146,34 @@ class OpenClawRunner:
114
146
  return None
115
147
 
116
148
  nonce = challenge["payload"]["nonce"]
117
- ts = challenge["payload"]["ts"]
149
+ signed_at_ms = int(time.time() * 1000)
150
+
151
+ # Build auth payload and sign
152
+ client_id = "cli"
153
+ client_mode = "cli"
154
+ role = "operator"
155
+ scopes = ["operator.read", "operator.write"]
118
156
 
119
- # Sign challenge
120
- signature, signed_at = _sign_challenge(self._private_key, nonce, ts)
157
+ payload = _build_auth_payload(
158
+ device_id=self._device_id,
159
+ client_id=client_id,
160
+ client_mode=client_mode,
161
+ role=role,
162
+ scopes=scopes,
163
+ signed_at_ms=signed_at_ms,
164
+ token=self._device_token,
165
+ nonce=nonce,
166
+ )
167
+ signature = _sign_payload(self._private_key, payload)
121
168
 
122
169
  # Get public key in base64
123
170
  public_key_bytes = self._private_key.public_key().public_bytes(
124
171
  encoding=serialization.Encoding.Raw,
125
172
  format=serialization.PublicFormat.Raw
126
173
  )
127
- public_key_b64 = base64.urlsafe_b64encode(public_key_bytes).decode().rstrip("=")
174
+ pub_b64 = base64.urlsafe_b64encode(public_key_bytes).decode().rstrip("=")
128
175
 
129
- # Send connect request - match CLI format exactly
176
+ # Send connect request
130
177
  connect_req = {
131
178
  "type": "req",
132
179
  "id": str(uuid.uuid4()),
@@ -135,13 +182,13 @@ class OpenClawRunner:
135
182
  "minProtocol": 3,
136
183
  "maxProtocol": 3,
137
184
  "client": {
138
- "id": "cli", # Must be "cli" for operator role
185
+ "id": client_id,
139
186
  "version": "1.0.0",
140
187
  "platform": "linux",
141
- "mode": "operator" # Must be "operator"
188
+ "mode": client_mode
142
189
  },
143
- "role": "operator",
144
- "scopes": ["operator.read", "operator.write"],
190
+ "role": role,
191
+ "scopes": scopes,
145
192
  "caps": [],
146
193
  "commands": [],
147
194
  "permissions": {},
@@ -150,9 +197,9 @@ class OpenClawRunner:
150
197
  "userAgent": "voice-server/1.0.0",
151
198
  "device": {
152
199
  "id": self._device_id,
153
- "publicKey": public_key_b64,
200
+ "publicKey": pub_b64,
154
201
  "signature": signature,
155
- "signedAt": signed_at,
202
+ "signedAt": signed_at_ms,
156
203
  "nonce": nonce
157
204
  }
158
205
  }
File without changes
File without changes