Fix whisper docker status detection for compose profile

This commit is contained in:
William Valentin
2026-02-22 19:37:49 -08:00
parent 8fcbb5f521
commit 453eb264df
3 changed files with 30 additions and 9 deletions
+12
View File
@@ -3,6 +3,18 @@
"updated_at": "2026-02-23",
"description": "Tracks the status of all Flynn plans and implementation phases",
"plans": {
"dashboard-docker-dependencies-profile-aware-detection": {
"status": "completed",
"date": "2026-02-23",
"updated": "2026-02-23",
"summary": "Fixed false `not-found` Whisper status in dashboard docker dependency cards by querying docker compose with the `voice` profile enabled (`--profile voice`) for both `config --services` and `ps` probes.",
"files_modified": [
"src/gateway/handlers/dockerDependencies.ts",
"src/gateway/handlers/dockerDependencies.test.ts",
"docs/plans/state.json"
],
"test_status": "pnpm test:run src/gateway/handlers/dockerDependencies.test.ts + pnpm typecheck passing"
},
"minimal-tui-submitted-line-dedupe": {
"status": "completed",
"date": "2026-02-23",
@@ -16,11 +16,13 @@ function createConfig(endpoint: string, enabled = true): Config {
describe('listDockerDependencyStatuses', () => {
it('reports whisper as running when compose ps shows active container', async () => {
const seenCalls: string[][] = [];
const runner = async (args: string[]) => {
if (args[0] === 'config') {
seenCalls.push(args);
if (args.includes('config')) {
return { stdout: 'flynn\nwhisper-server\n', stderr: '' };
}
if (args[0] === 'ps') {
if (args.includes('ps')) {
return {
stdout: JSON.stringify([{
Name: 'flynn-whisper-server-1',
@@ -48,14 +50,16 @@ describe('listDockerDependencyStatuses', () => {
statusText: 'Up 4 minutes (healthy)',
containerName: 'flynn-whisper-server-1',
});
expect(seenCalls[0]).toEqual(['--profile', 'voice', 'config', '--services']);
expect(seenCalls[1]).toEqual(['--profile', 'voice', 'ps', 'whisper-server', '--format', 'json']);
});
it('reports whisper as defined but not started when no container exists yet', async () => {
const runner = async (args: string[]) => {
if (args[0] === 'config') {
if (args.includes('config')) {
return { stdout: 'flynn\nwhisper-server\n', stderr: '' };
}
if (args[0] === 'ps') {
if (args.includes('ps')) {
return { stdout: '[]', stderr: '' };
}
throw new Error(`Unexpected args: ${args.join(' ')}`);
@@ -76,7 +80,7 @@ describe('listDockerDependencyStatuses', () => {
it('reports whisper service as missing when compose file does not define it', async () => {
const runner = async (args: string[]) => {
if (args[0] === 'config') {
if (args.includes('config')) {
return { stdout: 'flynn\n', stderr: '' };
}
throw new Error(`Unexpected args: ${args.join(' ')}`);
@@ -109,10 +113,10 @@ describe('listDockerDependencyStatuses', () => {
it('marks whisper as not configured for non-local transcription endpoints', async () => {
const runner = async (args: string[]) => {
if (args[0] === 'config') {
if (args.includes('config')) {
return { stdout: 'whisper-server\n', stderr: '' };
}
if (args[0] === 'ps') {
if (args.includes('ps')) {
return { stdout: '[]', stderr: '' };
}
throw new Error(`Unexpected args: ${args.join(' ')}`);
+7 -2
View File
@@ -30,6 +30,11 @@ interface ComposePsEntry {
}
const WHISPER_SERVICE = 'whisper-server';
const WHISPER_PROFILE = 'voice';
function withWhisperProfile(args: string[]): string[] {
return ['--profile', WHISPER_PROFILE, ...args];
}
function defaultRunner(args: string[]): Promise<DockerComposeResult> {
return execFile('docker', ['compose', '-f', 'docker-compose.yml', ...args], {
@@ -154,7 +159,7 @@ export async function listDockerDependencyStatuses(
let services: string[];
try {
const response = await runner(['config', '--services']);
const response = await runner(withWhisperProfile(['config', '--services']));
services = parseServiceList(response.stdout);
} catch (error) {
return [{
@@ -174,7 +179,7 @@ export async function listDockerDependencyStatuses(
}
try {
const response = await runner(['ps', WHISPER_SERVICE, '--format', 'json']);
const response = await runner(withWhisperProfile(['ps', WHISPER_SERVICE, '--format', 'json']));
const rows = parseComposePsOutput(response.stdout)
.filter((entry) => (entry.Service ?? '') === WHISPER_SERVICE || !entry.Service);