@gotcos/glasses-server 6.65.0 → 6.66.0
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/.env.example +5 -0
- package/CHANGELOG.md +9 -0
- package/README.md +32 -6
- package/bin/cli.cjs +20 -29
- package/bin/whisper-runtime.cjs +17 -0
- package/bin/whisper-runtime.d.cts +1 -0
- package/package.json +4 -2
- package/server/lib/dictation-clean-models.ts +3 -3
- package/server/lib/dictation-clean.ts +1 -1
- package/server/lib/health-static-probes.ts +4 -2
- package/server/lib/whisper-local.ts +4 -11
- package/server/lib/whisper-preview.ts +2 -2
- package/server/routes/prompt-drafts.ts +1 -1
package/.env.example
CHANGED
|
@@ -340,3 +340,8 @@ BIND_HOST=0.0.0.0
|
|
|
340
340
|
# additionally requires a client that acknowledges displaying continue_note.
|
|
341
341
|
# Keep this in ~/.cos-glasses/.env; Control does not yet persist it on update.
|
|
342
342
|
COS_CONTINUE_FULL_PERMISSIONS=1
|
|
343
|
+
|
|
344
|
+
# Optional absolute executable paths for COS-managed whisper.cpp.
|
|
345
|
+
# Unset uses Homebrew then PATH. An invalid explicit path fails closed.
|
|
346
|
+
# COS_WHISPER_CLI_BIN=/absolute/path/to/whisper-cli
|
|
347
|
+
# COS_WHISPER_SERVER_BIN=/absolute/path/to/whisper-server
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 6.66.0 — 2026-10-08 — Managed voice setup and Haiku polish
|
|
2
|
+
|
|
3
|
+
- Advertise Haiku, Sonnet and GPT-5.6 Luna Fast. Default omitted-model polish to Haiku for lower cost, while honoring explicit model choices and retaining the transcript on failure.
|
|
4
|
+
- Glasses app 6.10.616 explicitly sends the chosen model and already works with server 6.65.0.
|
|
5
|
+
- Setup, live preview, committed transcription and HQ share explicit `COS_WHISPER_CLI_BIN` / `COS_WHISPER_SERVER_BIN` resolution. A broken explicit path fails closed; existing Homebrew/PATH detection remains the fallback.
|
|
6
|
+
- COS Control can prepare Turbo first for a local benchmark, then the chosen tier, without overwriting saved voice settings. Both preparation flags require `--setup-transcription --prepare-only`.
|
|
7
|
+
- Pairs with COS Control 0.5.273 and the signed/notarized whisper.cpp 1.9.1 runtime.
|
|
8
|
+
- Claude health probes resolve the installed absolute binary path, including managed-service environments without the interactive shell PATH.
|
|
9
|
+
|
|
1
10
|
## 6.65.0 — 2026-10-07 — Work search by meaning, and task dates
|
|
2
11
|
|
|
3
12
|
- `POST /api/work/search` `{ query, domain?, scope?, ids? }` finds the Work card the user means, not only the words they typed. Pairs with COS Control 0.5.259, which shows its own word matches at once and asks this after a pause.
|
package/README.md
CHANGED
|
@@ -646,18 +646,24 @@ and counts only, never a speaker name, path or error text; those stay in
|
|
|
646
646
|
|
|
647
647
|
## Final dictation polish (6.61.5)
|
|
648
648
|
|
|
649
|
-
With glasses app 6.10.
|
|
650
|
-
(the default,
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
649
|
+
With glasses app 6.10.616, **Settings → Voice → Polish model** offers **Haiku 5.5**
|
|
650
|
+
(the lower-cost default), **Sonnet 5.5** (higher-quality polish), and **GPT-5.6 Luna Fast**.
|
|
651
|
+
Haiku and Sonnet use your Claude CLI sign-in; Luna uses your Cursor CLI sign-in.
|
|
652
|
+
Luna uses `gpt-5.6-luna-none-fast` and needs server 6.61.5. Existing explicit choices
|
|
653
|
+
are preserved. Unset or unknown app preferences use Haiku. Both phone text and
|
|
654
|
+
recovered audio drafts honor the choice. Existing server 6.65.0 already accepts
|
|
655
|
+
explicit Haiku requests, so the new app does not require a server update.
|
|
656
|
+
|
|
657
|
+
The next server release also defaults omitted-model requests to Haiku and advertises
|
|
658
|
+
all three options in `capabilities.dictationCleanup.models`. Until that release,
|
|
659
|
+
6.65.0 still defaults requests without a model to Sonnet; 6.10.616 sends its choice
|
|
660
|
+
explicitly. This is a polish-model change, not a change to the speech recognition engine.
|
|
654
661
|
|
|
655
662
|
Cleanup uses subscription CLIs only, with no API-key fallback or automatic model
|
|
656
663
|
substitution. A failed or unavailable cleanup keeps the glossary-corrected
|
|
657
664
|
transcript. Turn **Final polish (AI)** off to skip the model call. Claude tools
|
|
658
665
|
and hooks are disabled; Cursor runs in read-only ask mode with a temporary profile
|
|
659
666
|
and empty workspace. Live preview and canonical meeting transcription are unchanged.
|
|
660
|
-
Older clients may still explicitly request Haiku.
|
|
661
667
|
|
|
662
668
|
## HQ dictation
|
|
663
669
|
|
|
@@ -917,3 +923,23 @@ This is a local owner instance, separate from app.gotcos.com tenants and hosted
|
|
|
917
923
|
## Provider parity in 6.62.0
|
|
918
924
|
|
|
919
925
|
See [provider capabilities and limits](docs/provider-parity.md) for Continue permissions, client compatibility, hook trust, observation limits and rollback. Glasses 6.10.600 shows the server's Continue note. Older clients retain Cursor Ask behavior; forks remain read-only.
|
|
926
|
+
|
|
927
|
+
### Managed Whisper runtime (6.66.0 candidate)
|
|
928
|
+
|
|
929
|
+
COS Control 0.5.272 prepares a signed, notarized whisper.cpp 1.9.1 runtime during
|
|
930
|
+
voice setup on Apple silicon. It supplies absolute `COS_WHISPER_CLI_BIN` and
|
|
931
|
+
`COS_WHISPER_SERVER_BIN` paths to the server; Homebrew is not needed for that flow.
|
|
932
|
+
Direct server installs still fall back to Homebrew and then PATH when overrides
|
|
933
|
+
are unset. A configured but missing/non-executable override is an error, never a
|
|
934
|
+
silent change of runtime.
|
|
935
|
+
|
|
936
|
+
`--setup-transcription --prepare-only --voice-benchmark-prepare` prepares Turbo
|
|
937
|
+
without changing saved tier keys. Control measures the device and follows with
|
|
938
|
+
`--setup-transcription --prepare-only --transcription-tier balanced|max
|
|
939
|
+
--preserve-voice-settings`. The latter prepares all required models, including
|
|
940
|
+
Large-v3 for saved-meeting HQ in both tiers. Applying a tier remains Control's
|
|
941
|
+
transactional action. Downloading or cancelling preparation does not change it.
|
|
942
|
+
Runtime/model setup is local; the existing provider sign-in requirements remain.
|
|
943
|
+
|
|
944
|
+
Candidate only: publish the versioned runtime asset and server package before
|
|
945
|
+
promoting the matching Control appcast entry.
|
package/bin/cli.cjs
CHANGED
|
@@ -25,7 +25,12 @@ const { homedir } = require('os')
|
|
|
25
25
|
const PKG_ROOT = resolve(__dirname, '..')
|
|
26
26
|
const CONFIG_DIR = join(homedir(), '.cos-glasses')
|
|
27
27
|
const PREPARE_ONLY = process.argv.includes('--prepare-only')
|
|
28
|
+
const VOICE_BENCHMARK_PREPARE = process.argv.includes('--voice-benchmark-prepare')
|
|
28
29
|
const SETUP_TRANSCRIPTION = process.argv.includes('--setup-transcription')
|
|
30
|
+
if ((VOICE_BENCHMARK_PREPARE || process.argv.includes('--preserve-voice-settings')) && !(SETUP_TRANSCRIPTION && PREPARE_ONLY)) {
|
|
31
|
+
console.error('Voice preparation flags require --setup-transcription --prepare-only.');
|
|
32
|
+
process.exit(64)
|
|
33
|
+
}
|
|
29
34
|
const SETUP_SPEAKER_MODEL = process.argv.includes('--setup-speaker-model')
|
|
30
35
|
const HOOKS_ACTION = process.argv.includes('--hooks') ? process.argv[process.argv.indexOf('--hooks') + 1] : undefined
|
|
31
36
|
function optionValue(name) {
|
|
@@ -540,12 +545,15 @@ function upsertEnvValue(file, key, value) {
|
|
|
540
545
|
chmodSync(file, 0o600)
|
|
541
546
|
}
|
|
542
547
|
|
|
543
|
-
if (SETUP_TRANSCRIPTION) {
|
|
548
|
+
if (SETUP_TRANSCRIPTION && !VOICE_BENCHMARK_PREPARE) {
|
|
549
|
+
const preserveVoiceSettings = process.argv.includes('--preserve-voice-settings')
|
|
544
550
|
const previewModel = TRANSCRIPTION_TIER === 'max' ? 'turbo' : 'small.en'
|
|
545
551
|
const commitModel = TRANSCRIPTION_TIER === 'max' ? 'large-v3' : 'turbo'
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
552
|
+
if (!preserveVoiceSettings) {
|
|
553
|
+
upsertEnvValue(ENV_FILE, 'COS_WHISPER_TRANSCRIPTION_TIER', TRANSCRIPTION_TIER)
|
|
554
|
+
upsertEnvValue(ENV_FILE, 'COS_WHISPER_PREVIEW_MODEL', previewModel)
|
|
555
|
+
upsertEnvValue(ENV_FILE, 'COS_WHISPER_COMMIT_MODEL', commitModel)
|
|
556
|
+
}
|
|
549
557
|
process.env.COS_WHISPER_TRANSCRIPTION_TIER = TRANSCRIPTION_TIER
|
|
550
558
|
process.env.COS_WHISPER_PREVIEW_MODEL = previewModel
|
|
551
559
|
process.env.COS_WHISPER_COMMIT_MODEL = commitModel
|
|
@@ -573,8 +581,7 @@ if (!process.env.COS_PROFILE_PATH) process.env.COS_PROFILE_PATH = PROFILE_FILE
|
|
|
573
581
|
|
|
574
582
|
// Step 5: local Whisper detection + model download. Voice stays local-only by
|
|
575
583
|
// default; cloud fallback requires an explicit flag plus a configured key.
|
|
576
|
-
const
|
|
577
|
-
const WHISPER_SERVER_KNOWN_PATHS = ['/opt/homebrew/bin/whisper-server', '/usr/local/bin/whisper-server']
|
|
584
|
+
const { resolveWhisperBinary } = require('./whisper-runtime.cjs')
|
|
578
585
|
const WHISPER_MODEL_DIR = join(homedir(), '.local/share/whisper-models')
|
|
579
586
|
const WHISPER_MODEL_PATH = join(WHISPER_MODEL_DIR, 'ggml-large-v3-turbo.bin')
|
|
580
587
|
const WHISPER_MODEL_PARTIAL = WHISPER_MODEL_PATH + '.partial'
|
|
@@ -591,24 +598,8 @@ const WHISPER_LARGE_MODEL_MIN_BYTES = 2_800_000_000
|
|
|
591
598
|
const WHISPER_MODEL_EXPECTED_BYTES = 1_624_555_275
|
|
592
599
|
const WHISPER_SMALL_MODEL_EXPECTED_BYTES = 487_614_201
|
|
593
600
|
const WHISPER_LARGE_MODEL_EXPECTED_BYTES = 3_095_033_483
|
|
594
|
-
function findWhisperCli() {
|
|
595
|
-
|
|
596
|
-
try {
|
|
597
|
-
const found = execSync('command -v whisper-cli 2>/dev/null', { shell: '/bin/sh', stdio: 'pipe', timeout: 2000 }).toString().trim()
|
|
598
|
-
return found || null
|
|
599
|
-
} catch {
|
|
600
|
-
return null
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
function findWhisperServer() {
|
|
604
|
-
for (const p of WHISPER_SERVER_KNOWN_PATHS) { if (existsSync(p)) return p }
|
|
605
|
-
try {
|
|
606
|
-
const found = execSync('command -v whisper-server 2>/dev/null', { shell: '/bin/sh', stdio: 'pipe', timeout: 2000 }).toString().trim()
|
|
607
|
-
return found || null
|
|
608
|
-
} catch {
|
|
609
|
-
return null
|
|
610
|
-
}
|
|
611
|
-
}
|
|
601
|
+
function findWhisperCli() { return resolveWhisperBinary('whisper-cli') }
|
|
602
|
+
function findWhisperServer() { return resolveWhisperBinary('whisper-server') }
|
|
612
603
|
function isValidWhisperModel(p, minBytes = WHISPER_MODEL_MIN_BYTES) {
|
|
613
604
|
if (!existsSync(p)) return false
|
|
614
605
|
try { return statSync(p).size >= minBytes } catch { return false }
|
|
@@ -652,10 +643,10 @@ if (SETUP_TRANSCRIPTION && process.env.SKIP_WHISPER_DOWNLOAD !== '1') {
|
|
|
652
643
|
mkdirSync(WHISPER_MODEL_DIR, { recursive: true })
|
|
653
644
|
const targets = [
|
|
654
645
|
[WHISPER_MODEL_PATH, WHISPER_MODEL_PARTIAL, WHISPER_MODEL_MIN_BYTES, WHISPER_MODEL_EXPECTED_BYTES],
|
|
655
|
-
...(TRANSCRIPTION_TIER === 'balanced'
|
|
646
|
+
...(!VOICE_BENCHMARK_PREPARE && TRANSCRIPTION_TIER === 'balanced'
|
|
656
647
|
? [[WHISPER_SMALL_MODEL_PATH, WHISPER_SMALL_MODEL_PARTIAL, WHISPER_SMALL_MODEL_MIN_BYTES, WHISPER_SMALL_MODEL_EXPECTED_BYTES]]
|
|
657
648
|
: []),
|
|
658
|
-
[WHISPER_LARGE_MODEL_PATH, WHISPER_LARGE_MODEL_PARTIAL, WHISPER_LARGE_MODEL_MIN_BYTES, WHISPER_LARGE_MODEL_EXPECTED_BYTES],
|
|
649
|
+
...(!VOICE_BENCHMARK_PREPARE ? [[WHISPER_LARGE_MODEL_PATH, WHISPER_LARGE_MODEL_PARTIAL, WHISPER_LARGE_MODEL_MIN_BYTES, WHISPER_LARGE_MODEL_EXPECTED_BYTES]] : []),
|
|
659
650
|
]
|
|
660
651
|
const missingBytes = targets.reduce((sum, [path, , minimum, expected]) =>
|
|
661
652
|
sum + (isValidWhisperModel(path, minimum) ? 0 : expected), 0)
|
|
@@ -716,7 +707,7 @@ if (process.env.COS_OPENAI_WHISPER_FALLBACK === '1') {
|
|
|
716
707
|
|
|
717
708
|
const previewChoice = (process.env.COS_WHISPER_PREVIEW_MODEL || process.env.COS_WHISPER_REALTIME_MODEL || 'auto').trim().toLowerCase()
|
|
718
709
|
const wantsSmallPreview = ['small', 'small.en', 'ggml-small.en.bin'].includes(previewChoice)
|
|
719
|
-
if (whisperCliPath && wantsSmallPreview) {
|
|
710
|
+
if (whisperCliPath && wantsSmallPreview && !VOICE_BENCHMARK_PREPARE) {
|
|
720
711
|
if (isValidWhisperModel(WHISPER_SMALL_MODEL_PATH, WHISPER_SMALL_MODEL_MIN_BYTES)) {
|
|
721
712
|
console.log(green(' ✓') + ' Small.en preview model ready ' + dim('— Turbo remains authoritative'))
|
|
722
713
|
} else {
|
|
@@ -745,7 +736,7 @@ if (whisperCliPath && wantsSmallPreview) {
|
|
|
745
736
|
}
|
|
746
737
|
}
|
|
747
738
|
|
|
748
|
-
if (whisperCliPath && SETUP_TRANSCRIPTION) {
|
|
739
|
+
if (whisperCliPath && SETUP_TRANSCRIPTION && !VOICE_BENCHMARK_PREPARE) {
|
|
749
740
|
if (isValidWhisperModel(WHISPER_LARGE_MODEL_PATH, WHISPER_LARGE_MODEL_MIN_BYTES)) {
|
|
750
741
|
const largeRole = TRANSCRIPTION_TIER === 'max'
|
|
751
742
|
? 'live preview + commit and saved-meeting polish'
|
|
@@ -789,7 +780,7 @@ if (whisperCliPath && SETUP_TRANSCRIPTION) {
|
|
|
789
780
|
// -- sixty times larger -- downloaded here with SKIP_WHISPER_DOWNLOAD as the
|
|
790
781
|
// escape hatch. Same shape, same opt-out, so nobody on a metered or restricted
|
|
791
782
|
// network is forced into it.
|
|
792
|
-
if (!SETUP_SPEAKER_MODEL && process.env.SKIP_SPEAKER_MODEL_DOWNLOAD !== '1') {
|
|
783
|
+
if (!VOICE_BENCHMARK_PREPARE && !SETUP_SPEAKER_MODEL && process.env.SKIP_SPEAKER_MODEL_DOWNLOAD !== '1') {
|
|
793
784
|
const spkDest = join(CONFIG_DIR, 'models', SPEAKER_MODEL.filename)
|
|
794
785
|
if (existsSync(spkDest) && sha256File(spkDest) === SPEAKER_MODEL.sha256) {
|
|
795
786
|
console.log(green(' ✓') + ' Voiceprint model ready ' + dim('— named speakers available'))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Shared by setup and both live Whisper workers. Explicit overrides fail closed.
|
|
2
|
+
const { accessSync, constants, statSync } = require('node:fs')
|
|
3
|
+
const { isAbsolute, join } = require('node:path')
|
|
4
|
+
function executable(path) {
|
|
5
|
+
try { accessSync(path, constants.X_OK); return statSync(path).isFile() } catch { return false }
|
|
6
|
+
}
|
|
7
|
+
function resolveWhisperBinary(name, env = process.env, available = executable) {
|
|
8
|
+
if (!['whisper-cli', 'whisper-server'].includes(name)) throw new Error('Unknown Whisper binary')
|
|
9
|
+
const key = name === 'whisper-cli' ? 'COS_WHISPER_CLI_BIN' : 'COS_WHISPER_SERVER_BIN'
|
|
10
|
+
if (env[key] !== undefined && env[key] !== '') {
|
|
11
|
+
const path = env[key].trim()
|
|
12
|
+
return isAbsolute(path) && available(path) ? path : null
|
|
13
|
+
}
|
|
14
|
+
const candidates = ['/opt/homebrew/bin', '/usr/local/bin', ...(env.PATH || '').split(':')]
|
|
15
|
+
return candidates.filter(isAbsolute).map(p => join(p, name)).find(available) || null
|
|
16
|
+
}
|
|
17
|
+
module.exports = { resolveWhisperBinary }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function resolveWhisperBinary(name: 'whisper-cli' | 'whisper-server', env?: NodeJS.ProcessEnv, available?: (path: string) => boolean): string | null
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotcos/glasses-server",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.66.0",
|
|
4
4
|
"description": "COS Glasses: self-hosted AI heads-up-display server for Even G2 smart glasses, powered by Claude Code, Codex, Cursor Agent CLI, or local Ollama",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -77,7 +77,9 @@
|
|
|
77
77
|
"docs/work-task-runtime.md",
|
|
78
78
|
"!server/scripts/build-work-task-runtime.py",
|
|
79
79
|
"bin/hooks/cos-cursor-observer.cjs",
|
|
80
|
-
"docs/provider-parity.md"
|
|
80
|
+
"docs/provider-parity.md",
|
|
81
|
+
"bin/whisper-runtime.cjs",
|
|
82
|
+
"bin/whisper-runtime.d.cts"
|
|
81
83
|
],
|
|
82
84
|
"author": "COS Contributors",
|
|
83
85
|
"license": "MIT",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/** Public choices. Haiku
|
|
2
|
-
export const DICTATION_CLEAN_MODELS = ['sonnet', 'luna-5.6-fast'] as const
|
|
3
|
-
export type DictationCleanModel = typeof DICTATION_CLEAN_MODELS[number]
|
|
1
|
+
/** Public polish choices. Haiku is the cost-first default. */
|
|
2
|
+
export const DICTATION_CLEAN_MODELS = ['haiku', 'sonnet', 'luna-5.6-fast'] as const
|
|
3
|
+
export type DictationCleanModel = typeof DICTATION_CLEAN_MODELS[number]
|
|
4
4
|
export const LUNA_DICTATION_CLI_MODEL = 'gpt-5.6-luna-none-fast'
|
|
5
5
|
export function isDictationCleanModel(value: unknown): value is DictationCleanModel {
|
|
6
6
|
return value === 'sonnet' || value === 'luna-5.6-fast' || value === 'haiku'
|
|
@@ -32,7 +32,7 @@ export async function autoCleanDictation(
|
|
|
32
32
|
opts: { model?: string; signal?: AbortSignal } = {},
|
|
33
33
|
): Promise<string> {
|
|
34
34
|
if (opts.signal?.aborted) throw new Error('Auto-clean aborted')
|
|
35
|
-
const model = (opts.model || process.env.COS_DICTATION_AUTOCLEAN_MODEL || '
|
|
35
|
+
const model = (opts.model || process.env.COS_DICTATION_AUTOCLEAN_MODEL || 'haiku').toLowerCase()
|
|
36
36
|
if (!isDictationCleanModel(model)) throw new Error('Unsupported dictation cleanup model')
|
|
37
37
|
const cursor = model === 'luna-5.6-fast'
|
|
38
38
|
const binary = resolveProviderBinary(cursor ? 'cursor' : 'claude')
|
|
@@ -123,9 +123,11 @@ async function probePython(): Promise<string> {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
async function probeClaude(): Promise<{ value: string; available: boolean }> {
|
|
126
|
+
export async function probeClaude(): Promise<{ value: string; available: boolean }> {
|
|
127
127
|
try {
|
|
128
|
-
const
|
|
128
|
+
const resolved = resolveProviderBinary('claude')
|
|
129
|
+
if (!resolved.ok) return { value: `unresolved (${resolved.detail})`, available: false }
|
|
130
|
+
const result = await execute(resolved.path, ['--version'])
|
|
129
131
|
return { value: firstNonemptyLine(result.stdout) ?? 'available', available: true }
|
|
130
132
|
} catch {
|
|
131
133
|
return { value: 'error', available: false }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveWhisperBinary } from '../../bin/whisper-runtime.cjs'
|
|
1
2
|
// Local Whisper transcription via whisper-server (persistent) or whisper-cli (fallback)
|
|
2
3
|
// Eliminates 1-3s OpenAI API round-trip by running inference on M3 Ultra locally.
|
|
3
4
|
//
|
|
@@ -130,17 +131,9 @@ interface WhisperJsonResponse {
|
|
|
130
131
|
text?: unknown
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
function resolveWhisperBin(name: string): string {
|
|
137
|
-
for (const prefix of ['/opt/homebrew/bin', '/usr/local/bin']) {
|
|
138
|
-
if (existsSync(`${prefix}/${name}`)) return `${prefix}/${name}`
|
|
139
|
-
}
|
|
140
|
-
return `/opt/homebrew/bin/${name}`
|
|
141
|
-
}
|
|
142
|
-
const WHISPER_CLI = resolveWhisperBin('whisper-cli')
|
|
143
|
-
const WHISPER_SERVER = resolveWhisperBin('whisper-server')
|
|
134
|
+
// Same resolution as setup and preview; a broken explicit override never falls through to Homebrew.
|
|
135
|
+
const WHISPER_CLI = resolveWhisperBinary('whisper-cli') ?? '/__cos_whisper_unavailable__/whisper-cli'
|
|
136
|
+
const WHISPER_SERVER = resolveWhisperBinary('whisper-server') ?? '/__cos_whisper_unavailable__/whisper-server'
|
|
144
137
|
const MODEL_DIR = join(process.env.HOME ?? homedir(), '.local/share/whisper-models')
|
|
145
138
|
export const WHISPER_TURBO_MODEL_PATH = join(MODEL_DIR, 'ggml-large-v3-turbo.bin')
|
|
146
139
|
export const WHISPER_LARGE_V3_MODEL_PATH = join(MODEL_DIR, 'ggml-large-v3.bin')
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveWhisperBinary } from '../../bin/whisper-runtime.cjs'
|
|
1
2
|
// Adaptive provisional transcription:
|
|
2
3
|
// Balanced -> isolated Small.en cosmetic preview + Turbo live commit
|
|
3
4
|
// Max -> isolated Turbo cosmetic preview + Large-v3 live commit
|
|
@@ -55,8 +56,7 @@ export const WHISPER_SMALL_EN_MODEL_PATH = join(MODEL_DIR, 'ggml-small.en.bin')
|
|
|
55
56
|
const WHISPER_TURBO_MODEL_PATH = join(MODEL_DIR, 'ggml-large-v3-turbo.bin')
|
|
56
57
|
const VAD_MODEL_PATH = join(MODEL_DIR, 'ggml-silero-v5.1.2.bin')
|
|
57
58
|
const VAD_ENABLED = process.env.COS_WHISPER_VAD !== '0'
|
|
58
|
-
const WHISPER_SERVER =
|
|
59
|
-
.find(existsSync) ?? '/opt/homebrew/bin/whisper-server'
|
|
59
|
+
const WHISPER_SERVER = resolveWhisperBinary('whisper-server') ?? '/__cos_whisper_unavailable__/whisper-server'
|
|
60
60
|
const PREVIEW_PORT = 8177
|
|
61
61
|
const PREVIEW_URL = `http://127.0.0.1:${PREVIEW_PORT}`
|
|
62
62
|
const PROCESS_PROBE_TIMEOUT_MS = 2_000
|
|
@@ -136,7 +136,7 @@ async function cleanOutboundDictation(text: string, opts: AutoCleanRequest & { s
|
|
|
136
136
|
console.warn(`[prompt-draft] Fuzzy correction failed (non-fatal): ${fuzzyErr?.message ?? fuzzyErr}`)
|
|
137
137
|
}
|
|
138
138
|
if (!(opts.enabled ?? autoCleanDefaultEnabled())) return cleaned
|
|
139
|
-
const model = opts.model ?? '
|
|
139
|
+
const model = opts.model ?? 'haiku'
|
|
140
140
|
const autoCleanBreaker = autoCleanBreakers[model]
|
|
141
141
|
if (cleaned.length > AUTOCLEAN_MAX_CHARS || autoCleanBreaker.isOpen() || autoCleanCountToday() >= autoCleanDailyCap()) return cleaned
|
|
142
142
|
const startedAt = Date.now()
|