@codexstar/pi-listen 1.0.9 → 1.0.11

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.
@@ -18,7 +18,7 @@ export function buildProvisioningPlan(config: VoiceConfig, diagnostics: Environm
18
18
 
19
19
  if (config.mode === "api") {
20
20
  if (config.backend === "deepgram" && !diagnostics.hasDeepgramKey) {
21
- manualSteps.push("Set DEEPGRAM_API_KEY before using Deepgram API mode");
21
+ manualSteps.push("Set DEEPGRAM_API_KEY get a free key at https://dpgr.am/pi-voice ($200 free credits)");
22
22
  }
23
23
  } else {
24
24
  if (!diagnostics.hasPython) {
@@ -140,7 +140,7 @@ export async function runVoiceOnboarding(
140
140
  ): Promise<OnboardingResult | undefined> {
141
141
  const modeChoice = await ctx.ui.select("How do you want to use speech-to-text?", [
142
142
  "Recommended for me",
143
- "Cloud API (fastest setup)",
143
+ "Cloud API (fastest setup — free $200 credits from Deepgram)",
144
144
  "Local download (offline / private)",
145
145
  ]);
146
146
  if (!modeChoice) return undefined;
@@ -168,6 +168,77 @@ export async function runVoiceOnboarding(
168
168
  if (!cloudSelection) return undefined;
169
169
  backend = cloudSelection.backend;
170
170
  model = cloudSelection.model;
171
+
172
+ // ─── Deepgram API key setup ──────────────────────────────
173
+ if (backend === "deepgram" && !diagnostics.hasDeepgramKey) {
174
+ const keyAction = await ctx.ui.select(
175
+ "Deepgram API key not found. What would you like to do?",
176
+ [
177
+ "Paste API key now",
178
+ "I'll set it up later (ask pi to help or export DEEPGRAM_API_KEY=...)",
179
+ ],
180
+ );
181
+ if (!keyAction) return undefined;
182
+
183
+ if (keyAction.startsWith("Paste")) {
184
+ ctx.ui.notify(
185
+ [
186
+ "Get your free Deepgram API key:",
187
+ " → https://dpgr.am/pi-voice",
188
+ " (Sign up → $200 free credits, no card needed)",
189
+ "",
190
+ "Paste your key below:",
191
+ ].join("\n"),
192
+ "info",
193
+ );
194
+ const apiKey = await ctx.ui.input("DEEPGRAM_API_KEY");
195
+ if (apiKey && apiKey.trim().length > 10) {
196
+ const trimmedKey = apiKey.trim();
197
+ // Write to ~/.env.secrets if it exists, else ~/.zshrc
198
+ const fs = await import("node:fs");
199
+ const os = await import("node:os");
200
+ const home = os.homedir();
201
+ const envSecretsPath = `${home}/.env.secrets`;
202
+ const zshrcPath = `${home}/.zshrc`;
203
+ const exportLine = `export DEEPGRAM_API_KEY="${trimmedKey}"`;
204
+
205
+ const targetFile = fs.existsSync(envSecretsPath) ? envSecretsPath : zshrcPath;
206
+ const existing = fs.existsSync(targetFile) ? fs.readFileSync(targetFile, "utf-8") : "";
207
+
208
+ if (existing.includes("DEEPGRAM_API_KEY")) {
209
+ // Replace existing line
210
+ const updated = existing.replace(/^export DEEPGRAM_API_KEY=.*$/m, exportLine);
211
+ fs.writeFileSync(targetFile, updated);
212
+ } else {
213
+ fs.appendFileSync(targetFile, `\n${exportLine}\n`);
214
+ }
215
+
216
+ // Also set for current process so daemon can use it immediately
217
+ process.env.DEEPGRAM_API_KEY = trimmedKey;
218
+ diagnostics.hasDeepgramKey = true;
219
+
220
+ ctx.ui.notify(
221
+ `API key saved to ${targetFile}\nActive in this session. New terminals will pick it up automatically.`,
222
+ "info",
223
+ );
224
+ } else if (apiKey !== undefined && apiKey !== null) {
225
+ ctx.ui.notify(
226
+ "Key looks too short — skipped. You can set it later:\n export DEEPGRAM_API_KEY=\"your-key\"",
227
+ "warning",
228
+ );
229
+ }
230
+ } else {
231
+ ctx.ui.notify(
232
+ [
233
+ "No problem! When you're ready:",
234
+ " 1. Get a key → https://dpgr.am/pi-voice ($200 free credits)",
235
+ " 2. Run: export DEEPGRAM_API_KEY=\"your-key\"",
236
+ " 3. Or ask pi: \"help me set up my Deepgram API key\"",
237
+ ].join("\n"),
238
+ "info",
239
+ );
240
+ }
241
+ }
171
242
  } else {
172
243
  const localSelection = await chooseBackendAndModel(
173
244
  ctx,
@@ -990,6 +990,22 @@ export default function (pi: ExtensionAPI) {
990
990
  },
991
991
  });
992
992
 
993
+ // ─── Dedicated setup command (discoverable in /command list) ──────────────
994
+
995
+ pi.registerCommand("voice-setup", {
996
+ description: "Configure voice input — select backend, model, and language",
997
+ handler: async (_args, cmdCtx) => {
998
+ ctx = cmdCtx;
999
+ const diagnostics = scanEnvironment(TRANSCRIBE_SCRIPT);
1000
+ const result = await runVoiceOnboarding(cmdCtx, config, diagnostics);
1001
+ if (!result) {
1002
+ cmdCtx.ui.notify("Voice setup cancelled.", "warning");
1003
+ return;
1004
+ }
1005
+ await finalizeAndSaveSetup(cmdCtx, result.config, result.selectedScope, result.summaryLines, "setup-command");
1006
+ },
1007
+ });
1008
+
993
1009
  pi.registerCommand("btw:new", {
994
1010
  description: "Start a fresh BTW thread",
995
1011
  handler: async (args, cmdCtx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexstar/pi-listen",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Voice input, first-run onboarding, and side-channel BTW conversations for Pi",
5
5
  "type": "module",
6
6
  "keywords": [