verbalcoding 0.2.4 → 0.2.5

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.
@@ -19,7 +19,8 @@ test('package exposes a short vc shell command', () => {
19
19
  assert.ok(pkg.files.includes('app-node/'));
20
20
  assert.ok(pkg.files.includes('scripts/*.mjs'));
21
21
  assert.ok(pkg.files.includes('scripts/*.sh'));
22
- assert.ok(pkg.files.includes('scripts/*.py'));
22
+ assert.ok(pkg.files.includes('integrations/openvoice/'));
23
+ assert.ok(!pkg.files.includes('scripts/*.py'));
23
24
  assert.ok(pkg.files.includes('run.sh'));
24
25
  assert.ok(pkg.files.includes('LICENSE'));
25
26
  });
@@ -39,12 +40,23 @@ test('installer shell script links the vc command during setup', () => {
39
40
 
40
41
  assert.match(script, /bootstrap_prereqs\.sh/);
41
42
  assert.match(script, /--no-wizard/);
43
+ assert.match(script, /--yes\) BOOTSTRAP_ARGS\+=\("\$arg"\); INSTALL_ARGS\+=\("\$arg"\)/);
42
44
  assert.match(script, /VERBALCODING_SKIP_BOOTSTRAP/);
43
45
  assert.match(script, /npm link/);
44
46
  assert.match(script, /Installed shell CLI: vc/);
45
47
  assert.match(script, /VERBALCODING_SKIP_CLI_LINK/);
46
48
  });
47
49
 
50
+ test('npm setup supports non-interactive --yes mode', () => {
51
+ const installer = fs.readFileSync(path.join(ROOT, 'scripts', 'install.mjs'), 'utf8');
52
+ const config = fs.readFileSync(path.join(ROOT, 'app-node', 'install_config.mjs'), 'utf8');
53
+
54
+ assert.match(installer, /args\.includes\('--yes'\)/);
55
+ assert.match(installer, /normalizeInstallAnswers\(process\.env\)/);
56
+ assert.match(config, /vc start/);
57
+ assert.doesNotMatch(config, /npm install -g \.\s+#/);
58
+ });
59
+
48
60
  test('bootstrap script installs cross-platform prerequisites and local model helpers', () => {
49
61
  const script = fs.readFileSync(path.join(ROOT, 'scripts', 'bootstrap_prereqs.sh'), 'utf8');
50
62
 
@@ -247,13 +247,14 @@ export function renderInstallSummary(values = {}) {
247
247
  `Configured Discord voice bridge for harness: ${backend}`,
248
248
  '',
249
249
  'Next commands:',
250
- ' npm install -g . # or ./scripts/install.sh to install the vc command',
251
250
  ' vc doctor',
252
- ' ./run.sh',
251
+ ' vc start',
253
252
  '',
254
253
  'Legacy project-local equivalents still work:',
255
254
  ' npm install',
255
+ ' ./scripts/install.sh',
256
256
  ' npm run doctor',
257
+ ' ./run.sh',
257
258
  '',
258
259
  `Auto-join voice channels: ${values.AUTO_JOIN_VOICE_CHANNELS || '일반,General,general'}`,
259
260
  `TTS backend: ${values.TTS_BACKEND || 'edge'}`,
@@ -135,7 +135,7 @@ export function createOpenVoiceBackend(settings, deps = {}) {
135
135
  return edge.synthesize(text, { signal, kind });
136
136
  }
137
137
  const out = uniquePath(tmpdir, 'verbalcoding-openvoice', 'wav');
138
- const script = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'scripts', 'openvoice_synth.py');
138
+ const script = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'integrations', 'openvoice', 'synth.py');
139
139
  const args = [
140
140
  script,
141
141
  '--openvoice-dir', openvoice.dir,
@@ -121,7 +121,7 @@ test('OpenVoice final synthesis calls Python wrapper with reference audio and ou
121
121
  const out = await backend.synthesize('복제 음성 테스트', { kind: 'final' });
122
122
 
123
123
  assert.equal(calls[0].cmd, path.join('/project/.venv-openvoice', 'bin', 'python'));
124
- assert.ok(calls[0].args.some(arg => String(arg).endsWith('scripts/openvoice_synth.py')));
124
+ assert.ok(calls[0].args.some(arg => String(arg).endsWith('integrations/openvoice/synth.py')));
125
125
  assert.ok(calls[0].args.includes('--ref-audio'));
126
126
  assert.ok(calls[0].args.includes('/project/voice-samples/me.wav'));
127
127
  assert.ok(calls[0].args.includes('--text'));
@@ -171,7 +171,7 @@ Edge TTS remains the default and fallback. To try local voice cloning with OpenV
171
171
  mkdir -p voice-samples
172
172
  # Put a permitted reference sample at voice-samples/user-reference.wav,
173
173
  # or capture one from Discord with !voice-clone capture.
174
- python3 scripts/openvoice_smoke.py
174
+ python3 integrations/openvoice/synth.py --openvoice-dir vendor/OpenVoice --ref-audio voice-samples/user-reference.wav --text '안녕하세요. 버벌코딩 목소리 복제 테스트입니다.' --output /tmp/verbalcoding-openvoice-smoke.wav
175
175
  ```
176
176
 
177
177
  Then set:
@@ -170,7 +170,7 @@ OpenVoice voice cloning is optional. Keep `TTS_BACKEND=edge` for a fresh public
170
170
  # Download OpenVoice V2 checkpoints into vendor/OpenVoice/checkpoints_v2/
171
171
  # Add a permitted local sample at voice-samples/user-reference.wav,
172
172
  # or run the bot, say "목소리 샘플 녹음 시작해", then speak 10-30 seconds.
173
- python3 scripts/openvoice_smoke.py
173
+ python3 integrations/openvoice/synth.py --openvoice-dir vendor/OpenVoice --ref-audio voice-samples/user-reference.wav --text '안녕하세요. 버벌코딩 목소리 복제 테스트입니다.' --output /tmp/verbalcoding-openvoice-smoke.wav
174
174
  ```
175
175
 
176
176
  Then set `TTS_BACKEND=openvoice`, run `vc doctor`, and test `!voice-test <text>` in Discord.
@@ -179,7 +179,7 @@ Edge TTS가 기본값이자 fallback입니다. OpenVoice V2로 로컬 음성 복
179
179
  mkdir -p voice-samples
180
180
  # 허가된 기준 샘플을 voice-samples/user-reference.wav에 넣거나,
181
181
  # Discord에서 !voice-clone capture로 샘플을 캡처합니다.
182
- python3 scripts/openvoice_smoke.py
182
+ python3 integrations/openvoice/synth.py --openvoice-dir vendor/OpenVoice --ref-audio voice-samples/user-reference.wav --text '안녕하세요. 버벌코딩 목소리 복제 테스트입니다.' --output /tmp/verbalcoding-openvoice-smoke.wav
183
183
  ```
184
184
 
185
185
  그 뒤 설정:
@@ -170,7 +170,7 @@ OpenVoice 음성 복제는 선택 기능입니다. 공개 설치 직후에는 `T
170
170
  # OpenVoice V2 체크포인트를 vendor/OpenVoice/checkpoints_v2/ 아래에 넣습니다.
171
171
  # 허가된 로컬 샘플을 voice-samples/user-reference.wav에 두거나,
172
172
  # 봇 실행 후 “목소리 샘플 녹음 시작해”라고 말하고 10~30초 발화합니다.
173
- python3 scripts/openvoice_smoke.py
173
+ python3 integrations/openvoice/synth.py --openvoice-dir vendor/OpenVoice --ref-audio voice-samples/user-reference.wav --text '안녕하세요. 버벌코딩 목소리 복제 테스트입니다.' --output /tmp/verbalcoding-openvoice-smoke.wav
174
174
  ```
175
175
 
176
176
  그 뒤 `TTS_BACKEND=openvoice`로 설정하고 `vc doctor`, Discord의 `!voice-test <text>`로 테스트합니다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verbalcoding",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Discord voice bridge for CLI coding agents.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "docs/",
35
35
  "scripts/*.mjs",
36
36
  "scripts/*.sh",
37
- "scripts/*.py",
37
+ "integrations/openvoice/",
38
38
  "run.sh",
39
39
  ".env.example",
40
40
  "README.md",
@@ -86,7 +86,7 @@ if (ttsBackend === 'edge') {
86
86
  ok = check('OpenVoice repo', fs.existsSync(openvoiceDir), path.relative(ROOT, openvoiceDir)) && ok;
87
87
  ok = check('OpenVoice venv', fs.existsSync(openvoiceVenv), path.relative(ROOT, openvoiceVenv)) && ok;
88
88
  ok = check('OpenVoice reference audio', fs.existsSync(refAudio), path.relative(ROOT, refAudio)) && ok;
89
- ok = check('OpenVoice synth wrapper help', spawnSync('python3', ['scripts/openvoice_synth.py', '--help'], { cwd: ROOT, encoding: 'utf8' }).status === 0, 'scripts/openvoice_synth.py') && ok;
89
+ ok = check('OpenVoice synth wrapper help', spawnSync('python3', ['integrations/openvoice/synth.py', '--help'], { cwd: ROOT, encoding: 'utf8' }).status === 0, 'integrations/openvoice/synth.py') && ok;
90
90
  note('OpenVoice progress prompts', ['1', 'true', 'yes', 'on'].includes(String(env.OPENVOICE_PROGRESS || '0').toLowerCase()) ? 'openvoice' : 'edge fallback');
91
91
  } else if (ttsBackend === 'speechswift') {
92
92
  const mode = String(env.SPEECHSWIFT_MODE || 'cli').toLowerCase() === 'server' ? 'server' : 'cli';
@@ -17,6 +17,7 @@ async function ask(question, fallback = '', options = {}) {
17
17
 
18
18
  async function main() {
19
19
  const args = process.argv.slice(2);
20
+ const yes = args.includes('--yes') || args.includes('-y');
20
21
  if (args[0] === 'instance' || args.includes('--instance')) {
21
22
  const { spawnSync } = await import('node:child_process');
22
23
  const pass = args[0] === 'instance'
@@ -26,6 +27,19 @@ async function main() {
26
27
  process.exitCode = result.status ?? 1;
27
28
  return;
28
29
  }
30
+ if (yes) {
31
+ const values = normalizeInstallAnswers(process.env);
32
+ const envPath = path.join(ROOT, '.env');
33
+ if (fs.existsSync(envPath)) {
34
+ const backup = `${envPath}.bak-${Date.now()}`;
35
+ fs.copyFileSync(envPath, backup);
36
+ console.log(`Backed up existing .env to ${backup}`);
37
+ }
38
+ fs.writeFileSync(envPath, buildEnvFile(values), { mode: 0o600 });
39
+ console.log(`Wrote ${envPath}`);
40
+ console.log(renderInstallSummary(values));
41
+ return;
42
+ }
29
43
  globalThis.__rl = readline.createInterface({ input, output });
30
44
  try {
31
45
  console.log('VerbalCoding installer');
@@ -9,7 +9,8 @@ for arg in "$@"; do
9
9
  case "$arg" in
10
10
  --no-wizard) RUN_WIZARD=0 ;;
11
11
  --skip-bootstrap) export VERBALCODING_SKIP_BOOTSTRAP=1 ;;
12
- --yes|--skip-system|--skip-model|--skip-edge-tts) BOOTSTRAP_ARGS+=("$arg") ;;
12
+ --yes) BOOTSTRAP_ARGS+=("$arg"); INSTALL_ARGS+=("$arg") ;;
13
+ --skip-system|--skip-model|--skip-edge-tts) BOOTSTRAP_ARGS+=("$arg") ;;
13
14
  *) INSTALL_ARGS+=("$arg") ;;
14
15
  esac
15
16
  done
@@ -29,6 +29,7 @@ Next manual steps:
29
29
  https://myshell-public-repo-host.s3.amazonaws.com/openvoice/checkpoints_v2_0417.zip
30
30
  2. Extract them under vendor/OpenVoice/checkpoints_v2/
31
31
  3. Put a permitted reference sample at voice-samples/user-reference.wav
32
- 4. Run: python3 scripts/openvoice_smoke.py
32
+ 4. Smoke test manually if needed:
33
+ python3 integrations/openvoice/synth.py --openvoice-dir vendor/OpenVoice --ref-audio voice-samples/user-reference.wav --text '안녕하세요. 버벌코딩 목소리 복제 테스트입니다.' --output /tmp/verbalcoding-openvoice-smoke.wav
33
34
  5. Set TTS_BACKEND=openvoice in .env and restart VerbalCoding.
34
35
  MSG
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Small OpenVoice smoke-test helper for VerbalCoding."""
3
-
4
- from __future__ import annotations
5
-
6
- import argparse
7
- from pathlib import Path
8
- import subprocess
9
- import sys
10
-
11
-
12
- def main() -> int:
13
- parser = argparse.ArgumentParser(description="Run a short Korean OpenVoice smoke test")
14
- parser.add_argument("--openvoice-dir", default="./vendor/OpenVoice")
15
- parser.add_argument("--ref-audio", default="./voice-samples/user-reference.wav")
16
- parser.add_argument("--output", default="/tmp/verbalcoding-openvoice-smoke.wav")
17
- parser.add_argument("--text", default="안녕하세요. 버벌코딩 목소리 복제 테스트입니다.")
18
- args = parser.parse_args()
19
- script = Path(__file__).with_name("openvoice_synth.py")
20
- cmd = [
21
- sys.executable,
22
- str(script),
23
- "--openvoice-dir", args.openvoice_dir,
24
- "--ref-audio", args.ref_audio,
25
- "--text", args.text,
26
- "--language", "KR",
27
- "--style", "default",
28
- "--output", args.output,
29
- ]
30
- return subprocess.call(cmd)
31
-
32
-
33
- if __name__ == "__main__":
34
- raise SystemExit(main())