careervivid 2.1.12 → 2.1.13
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.
- package/dist/commands/agent/repl.d.ts.map +1 -1
- package/dist/commands/agent/repl.js +55 -2
- package/dist/lib/shell.d.ts +13 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +30 -0
- package/dist/lib/tts.d.ts +26 -0
- package/dist/lib/tts.d.ts.map +1 -0
- package/dist/lib/tts.js +153 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAA4D,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAA4D,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAO7G,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,GAAE,MAAM,GAAG,IAAW,QAwBtF;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,WAAW,GAAG,sBAAsB,GAAG,IAAI,EACnD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9K,gBAAgB,EAAE,WAAW,EAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,GAAG,EAAE,GACX,OAAO,CAAC,IAAI,CAAC,CAosBf"}
|
|
@@ -4,8 +4,10 @@ import ora from "ora";
|
|
|
4
4
|
import { isSafeCommand } from "../../agent/tools/coding.js";
|
|
5
5
|
import { CareerVividProxyEngine } from "../../agent/CareerVividProxyEngine.js";
|
|
6
6
|
import { CV_MODELS } from "./configurator.js";
|
|
7
|
-
import { loadConfig, getProviderKey, setProviderKey } from "../../config.js";
|
|
7
|
+
import { loadConfig, getGeminiKey, getProviderKey, setProviderKey } from "../../config.js";
|
|
8
8
|
import { auditLog, writeSessionSummary, SESSION_ID } from "../../agent/agentAuditLog.js";
|
|
9
|
+
import { runShellEscape } from "../../lib/shell.js";
|
|
10
|
+
import { isVoiceEnabled, setVoiceEnabled, setLastResponse, getLastResponse, speakText, stopPlayback } from "../../lib/tts.js";
|
|
9
11
|
const { prompt } = pkg;
|
|
10
12
|
export function printCreditStatus(remaining, limit = null) {
|
|
11
13
|
if (remaining === null)
|
|
@@ -165,6 +167,15 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
165
167
|
chalk.dim("\n Then paste the job description, and press Enter twice to submit.\n"));
|
|
166
168
|
return ask();
|
|
167
169
|
}
|
|
170
|
+
// ── Subshell escape: input starting with ! runs as a raw shell command ──
|
|
171
|
+
if (userInput.startsWith("!")) {
|
|
172
|
+
const shellCmd = userInput.slice(1).trim();
|
|
173
|
+
if (shellCmd) {
|
|
174
|
+
process.stdout.write(chalk.dim(`\n $ ${shellCmd}\n`));
|
|
175
|
+
await runShellEscape(shellCmd);
|
|
176
|
+
}
|
|
177
|
+
return ask();
|
|
178
|
+
}
|
|
168
179
|
// ── Slash commands ──────────────────────────────────────────────
|
|
169
180
|
if (userInput.startsWith("/")) {
|
|
170
181
|
const [cmd, ...rest] = userInput.slice(1).split(" ");
|
|
@@ -173,13 +184,45 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
173
184
|
console.log(chalk.cyan("\n Slash commands:"));
|
|
174
185
|
console.log(chalk.dim(" /model <name> — Switch to a different model mid-session"));
|
|
175
186
|
console.log(chalk.dim(" /models — List all available CareerVivid models"));
|
|
187
|
+
console.log(chalk.dim(" /voice on|off — Toggle automatic TTS for agent responses"));
|
|
188
|
+
console.log(chalk.dim(" /speak — Read the last agent response aloud"));
|
|
176
189
|
console.log(chalk.dim(" /help — Show this help message"));
|
|
177
190
|
console.log(chalk.dim(" exit — End the session"));
|
|
178
|
-
console.log(chalk.cyan("\n
|
|
191
|
+
console.log(chalk.cyan("\n Shell escape (run terminal commands without leaving the agent):"));
|
|
192
|
+
console.log(chalk.dim(" !<command> — e.g. !ls -la or !git status\n"));
|
|
193
|
+
console.log(chalk.cyan(" Paste long content (job descriptions, cover letters):"));
|
|
179
194
|
console.log(chalk.dim(" <<< — Open multi-line paste mode; press Enter twice when done"));
|
|
180
195
|
console.log(chalk.dim(" <<<your text — Start with text directly after <<<\n"));
|
|
181
196
|
return ask();
|
|
182
197
|
}
|
|
198
|
+
if (cmd === "voice") {
|
|
199
|
+
if (arg === "on") {
|
|
200
|
+
setVoiceEnabled(true);
|
|
201
|
+
console.log(chalk.green("\n 🔊 Voice enabled. Agent responses will be spoken aloud.\n"));
|
|
202
|
+
}
|
|
203
|
+
else if (arg === "off") {
|
|
204
|
+
setVoiceEnabled(false);
|
|
205
|
+
stopPlayback();
|
|
206
|
+
console.log(chalk.yellow("\n 🔇 Voice disabled.\n"));
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const status = isVoiceEnabled() ? chalk.green("on") : chalk.yellow("off");
|
|
210
|
+
console.log(chalk.dim(`\n Voice is currently ${status}. Usage: /voice on or /voice off\n`));
|
|
211
|
+
}
|
|
212
|
+
return ask();
|
|
213
|
+
}
|
|
214
|
+
if (cmd === "speak") {
|
|
215
|
+
const last = getLastResponse();
|
|
216
|
+
if (!last) {
|
|
217
|
+
console.log(chalk.dim("\n Nothing to speak yet. Ask the agent something first.\n"));
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const geminiKey = getGeminiKey() || process.env.GEMINI_API_KEY;
|
|
221
|
+
speakText(last, geminiKey ?? undefined).catch(() => { });
|
|
222
|
+
console.log(chalk.dim("\n 🔊 Speaking last response...\n"));
|
|
223
|
+
}
|
|
224
|
+
return ask();
|
|
225
|
+
}
|
|
183
226
|
if (cmd === "models") {
|
|
184
227
|
console.log(chalk.cyan("\n Available CareerVivid models:"));
|
|
185
228
|
for (const m of CV_MODELS) {
|
|
@@ -418,6 +461,7 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
418
461
|
};
|
|
419
462
|
if (engine) {
|
|
420
463
|
sessionTurns++;
|
|
464
|
+
let responseAccumulator = "";
|
|
421
465
|
const sharedOnChunk = (text) => {
|
|
422
466
|
if (firstChunk) {
|
|
423
467
|
thinkingSpinner.stop();
|
|
@@ -426,6 +470,7 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
426
470
|
firstChunk = false;
|
|
427
471
|
}
|
|
428
472
|
process.stdout.write(text);
|
|
473
|
+
responseAccumulator += text; // Accumulate for TTS
|
|
429
474
|
};
|
|
430
475
|
const sharedOnError = (error) => {
|
|
431
476
|
thinkingSpinner.stop();
|
|
@@ -476,6 +521,14 @@ export async function askLoop(engine, options, selectedProvider, selectedModel,
|
|
|
476
521
|
onError: sharedOnError,
|
|
477
522
|
});
|
|
478
523
|
}
|
|
524
|
+
// ── TTS: store last response + auto-speak if voice enabled ──────
|
|
525
|
+
if (responseAccumulator) {
|
|
526
|
+
setLastResponse(responseAccumulator);
|
|
527
|
+
if (isVoiceEnabled()) {
|
|
528
|
+
const geminiKey = getGeminiKey() || process.env.GEMINI_API_KEY;
|
|
529
|
+
speakText(responseAccumulator, geminiKey ?? undefined).catch(() => { });
|
|
530
|
+
}
|
|
531
|
+
}
|
|
479
532
|
// ── Clean turn separator after every AI reply ─────────────────────────────
|
|
480
533
|
process.stdout.write("\n" + chalk.dim("─".repeat(48)) + "\n");
|
|
481
534
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shell.ts — Subshell escape for the CareerVivid REPL
|
|
3
|
+
*
|
|
4
|
+
* Intercepts user input starting with `!` and spawns it as a raw
|
|
5
|
+
* shell command. stdout/stderr are piped directly to the terminal.
|
|
6
|
+
* The agent session and conversation history are never affected.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Runs a shell command and streams its output to the terminal.
|
|
10
|
+
* Returns a promise that resolves when the command exits.
|
|
11
|
+
*/
|
|
12
|
+
export declare function runShellEscape(command: string): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/lib/shell.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB7D"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shell.ts — Subshell escape for the CareerVivid REPL
|
|
3
|
+
*
|
|
4
|
+
* Intercepts user input starting with `!` and spawns it as a raw
|
|
5
|
+
* shell command. stdout/stderr are piped directly to the terminal.
|
|
6
|
+
* The agent session and conversation history are never affected.
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from "child_process";
|
|
9
|
+
/**
|
|
10
|
+
* Runs a shell command and streams its output to the terminal.
|
|
11
|
+
* Returns a promise that resolves when the command exits.
|
|
12
|
+
*/
|
|
13
|
+
export function runShellEscape(command) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const child = spawn(command, {
|
|
16
|
+
shell: true,
|
|
17
|
+
stdio: "inherit", // pipe stdin/stdout/stderr directly to TTY
|
|
18
|
+
});
|
|
19
|
+
child.on("error", (err) => {
|
|
20
|
+
process.stderr.write(`\n⚠️ Shell error: ${err.message}\n`);
|
|
21
|
+
resolve();
|
|
22
|
+
});
|
|
23
|
+
child.on("close", (code) => {
|
|
24
|
+
if (code !== 0 && code !== null) {
|
|
25
|
+
process.stdout.write(`\n [exit ${code}]\n`);
|
|
26
|
+
}
|
|
27
|
+
resolve();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tts.ts — Text-to-Speech engine for the CareerVivid REPL
|
|
3
|
+
*
|
|
4
|
+
* Uses the Gemini API (TTS model) to synthesize speech from text,
|
|
5
|
+
* writes the result to a temp WAV file, and plays it via the OS
|
|
6
|
+
* audio player (afplay on macOS, aplay on Linux) without blocking
|
|
7
|
+
* the main thread.
|
|
8
|
+
*
|
|
9
|
+
* Toggle: /voice on | /voice off
|
|
10
|
+
* Speak: /speak (reads the last agent response)
|
|
11
|
+
*/
|
|
12
|
+
export declare function isVoiceEnabled(): boolean;
|
|
13
|
+
export declare function setVoiceEnabled(on: boolean): void;
|
|
14
|
+
export declare function setLastResponse(text: string): void;
|
|
15
|
+
export declare function getLastResponse(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Stops any currently playing audio immediately.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stopPlayback(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Synthesizes `text` via Gemini TTS and plays it asynchronously.
|
|
22
|
+
* Does nothing if voice is disabled. Silently ignores errors so the
|
|
23
|
+
* REPL is never disrupted by TTS failures.
|
|
24
|
+
*/
|
|
25
|
+
export declare function speakText(text: string, apiKey?: string): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=tts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/lib/tts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,wBAAgB,cAAc,YAA2B;AACzD,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,QAAwB;AACnE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,QAA0B;AACtE,wBAAgB,eAAe,WAA2B;AAI1D;;GAEG;AACH,wBAAgB,YAAY,SAK3B;AAoED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/lib/tts.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tts.ts — Text-to-Speech engine for the CareerVivid REPL
|
|
3
|
+
*
|
|
4
|
+
* Uses the Gemini API (TTS model) to synthesize speech from text,
|
|
5
|
+
* writes the result to a temp WAV file, and plays it via the OS
|
|
6
|
+
* audio player (afplay on macOS, aplay on Linux) without blocking
|
|
7
|
+
* the main thread.
|
|
8
|
+
*
|
|
9
|
+
* Toggle: /voice on | /voice off
|
|
10
|
+
* Speak: /speak (reads the last agent response)
|
|
11
|
+
*/
|
|
12
|
+
import { writeFileSync, unlinkSync } from "fs";
|
|
13
|
+
import { spawn } from "child_process";
|
|
14
|
+
import { tmpdir } from "os";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import { GoogleGenAI, Modality } from "@google/genai";
|
|
17
|
+
// ── State ────────────────────────────────────────────────────────────────────
|
|
18
|
+
let voiceEnabled = false;
|
|
19
|
+
let lastResponse = "";
|
|
20
|
+
let playbackProcess = null;
|
|
21
|
+
export function isVoiceEnabled() { return voiceEnabled; }
|
|
22
|
+
export function setVoiceEnabled(on) { voiceEnabled = on; }
|
|
23
|
+
export function setLastResponse(text) { lastResponse = text; }
|
|
24
|
+
export function getLastResponse() { return lastResponse; }
|
|
25
|
+
// ── Audio Playback ────────────────────────────────────────────────────────────
|
|
26
|
+
/**
|
|
27
|
+
* Stops any currently playing audio immediately.
|
|
28
|
+
*/
|
|
29
|
+
export function stopPlayback() {
|
|
30
|
+
if (playbackProcess && !playbackProcess.killed) {
|
|
31
|
+
playbackProcess.kill("SIGKILL");
|
|
32
|
+
playbackProcess = null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Plays a WAV buffer using the OS audio player (non-blocking).
|
|
37
|
+
* afplay on macOS, aplay on Linux, powershell on Windows.
|
|
38
|
+
*/
|
|
39
|
+
function playWav(wavBuffer) {
|
|
40
|
+
const tmpFile = join(tmpdir(), `cv-tts-${Date.now()}.wav`);
|
|
41
|
+
writeFileSync(tmpFile, wavBuffer);
|
|
42
|
+
const platform = process.platform;
|
|
43
|
+
let playerCmd;
|
|
44
|
+
let playerArgs;
|
|
45
|
+
if (platform === "darwin") {
|
|
46
|
+
playerCmd = "afplay";
|
|
47
|
+
playerArgs = [tmpFile];
|
|
48
|
+
}
|
|
49
|
+
else if (platform === "linux") {
|
|
50
|
+
playerCmd = "aplay";
|
|
51
|
+
playerArgs = ["-q", tmpFile];
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Windows fallback via PowerShell
|
|
55
|
+
playerCmd = "powershell";
|
|
56
|
+
playerArgs = ["-c", `(New-Object System.Media.SoundPlayer '${tmpFile}').PlaySync()`];
|
|
57
|
+
}
|
|
58
|
+
stopPlayback(); // Stop any previous playback
|
|
59
|
+
const child = spawn(playerCmd, playerArgs, { stdio: "ignore", detached: false });
|
|
60
|
+
playbackProcess = child;
|
|
61
|
+
child.on("close", () => {
|
|
62
|
+
try {
|
|
63
|
+
unlinkSync(tmpFile);
|
|
64
|
+
}
|
|
65
|
+
catch { /* ignore cleanup errors */ }
|
|
66
|
+
if (playbackProcess === child)
|
|
67
|
+
playbackProcess = null;
|
|
68
|
+
});
|
|
69
|
+
child.on("error", () => {
|
|
70
|
+
// Silently ignore — player may not be installed
|
|
71
|
+
try {
|
|
72
|
+
unlinkSync(tmpFile);
|
|
73
|
+
}
|
|
74
|
+
catch { /* ignore */ }
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// ── WAV Header Builder ────────────────────────────────────────────────────────
|
|
78
|
+
function buildWavHeader(dataLength, sampleRate = 24000, channels = 1, bitsPerSample = 16) {
|
|
79
|
+
const byteRate = sampleRate * channels * bitsPerSample / 8;
|
|
80
|
+
const blockAlign = channels * bitsPerSample / 8;
|
|
81
|
+
const header = Buffer.alloc(44);
|
|
82
|
+
header.write("RIFF", 0);
|
|
83
|
+
header.writeUInt32LE(36 + dataLength, 4);
|
|
84
|
+
header.write("WAVE", 8);
|
|
85
|
+
header.write("fmt ", 12);
|
|
86
|
+
header.writeUInt32LE(16, 16);
|
|
87
|
+
header.writeUInt16LE(1, 20); // PCM
|
|
88
|
+
header.writeUInt16LE(channels, 22);
|
|
89
|
+
header.writeUInt32LE(sampleRate, 24);
|
|
90
|
+
header.writeUInt32LE(byteRate, 28);
|
|
91
|
+
header.writeUInt16LE(blockAlign, 32);
|
|
92
|
+
header.writeUInt16LE(bitsPerSample, 34);
|
|
93
|
+
header.write("data", 36);
|
|
94
|
+
header.writeUInt32LE(dataLength, 40);
|
|
95
|
+
return header;
|
|
96
|
+
}
|
|
97
|
+
// ── TTS Synthesis ────────────────────────────────────────────────────────────
|
|
98
|
+
/**
|
|
99
|
+
* Synthesizes `text` via Gemini TTS and plays it asynchronously.
|
|
100
|
+
* Does nothing if voice is disabled. Silently ignores errors so the
|
|
101
|
+
* REPL is never disrupted by TTS failures.
|
|
102
|
+
*/
|
|
103
|
+
export async function speakText(text, apiKey) {
|
|
104
|
+
if (!text.trim())
|
|
105
|
+
return;
|
|
106
|
+
const key = apiKey || process.env.GEMINI_API_KEY;
|
|
107
|
+
if (!key)
|
|
108
|
+
return; // No key — silently skip
|
|
109
|
+
// Strip markdown so the audio sounds natural
|
|
110
|
+
const cleaned = text
|
|
111
|
+
.replace(/```[\s\S]*?```/g, "") // code blocks
|
|
112
|
+
.replace(/`[^`]+`/g, "") // inline code
|
|
113
|
+
.replace(/\*\*(.*?)\*\*/g, "$1") // bold
|
|
114
|
+
.replace(/\*(.*?)\*/g, "$1") // italic
|
|
115
|
+
.replace(/^[#>•\-*]\s*/gm, "") // headings, quotes, bullets
|
|
116
|
+
.replace(/\s+/g, " ")
|
|
117
|
+
.trim()
|
|
118
|
+
.slice(0, 1000); // Cap to ~1000 chars to keep TTS fast
|
|
119
|
+
if (!cleaned)
|
|
120
|
+
return;
|
|
121
|
+
try {
|
|
122
|
+
const ai = new GoogleGenAI({ apiKey: key });
|
|
123
|
+
const response = await ai.models.generateContent({
|
|
124
|
+
model: "gemini-2.5-flash-preview-tts",
|
|
125
|
+
contents: [{ parts: [{ text: cleaned }] }],
|
|
126
|
+
config: {
|
|
127
|
+
responseModalities: [Modality.AUDIO],
|
|
128
|
+
speechConfig: {
|
|
129
|
+
voiceConfig: {
|
|
130
|
+
prebuiltVoiceConfig: { voiceName: "Zephyr" },
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const parts = response?.candidates?.[0]?.content?.parts ?? [];
|
|
136
|
+
const audioParts = [];
|
|
137
|
+
for (const part of parts) {
|
|
138
|
+
if (part.inlineData?.data) {
|
|
139
|
+
audioParts.push(Buffer.from(part.inlineData.data, "base64"));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (audioParts.length === 0)
|
|
143
|
+
return;
|
|
144
|
+
const pcmData = Buffer.concat(audioParts);
|
|
145
|
+
const wavHeader = buildWavHeader(pcmData.length);
|
|
146
|
+
const wavBuffer = Buffer.concat([wavHeader, pcmData]);
|
|
147
|
+
// Fire and forget — does not block the REPL
|
|
148
|
+
playWav(wavBuffer);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Silently swallow TTS errors — never disrupt the agent session
|
|
152
|
+
}
|
|
153
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "careervivid",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.13",
|
|
4
4
|
"description": "Official CLI for CareerVivid — AI voice interviews, autonomous job applications, resume editing, and portfolio publishing from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"mermaid": "11.14.0",
|
|
32
32
|
"open": "^10.1.0",
|
|
33
33
|
"ora": "^8.1.0",
|
|
34
|
-
"playwright-core": "
|
|
34
|
+
"playwright-core": "1.59.1",
|
|
35
35
|
"semver": "7.7.4",
|
|
36
36
|
"update-notifier": "^7.3.1"
|
|
37
37
|
},
|