Fix whisper docker status detection for compose profile
This commit is contained in:
@@ -3,6 +3,18 @@
|
|||||||
"updated_at": "2026-02-23",
|
"updated_at": "2026-02-23",
|
||||||
"description": "Tracks the status of all Flynn plans and implementation phases",
|
"description": "Tracks the status of all Flynn plans and implementation phases",
|
||||||
"plans": {
|
"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": {
|
"minimal-tui-submitted-line-dedupe": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-23",
|
"date": "2026-02-23",
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ function createConfig(endpoint: string, enabled = true): Config {
|
|||||||
|
|
||||||
describe('listDockerDependencyStatuses', () => {
|
describe('listDockerDependencyStatuses', () => {
|
||||||
it('reports whisper as running when compose ps shows active container', async () => {
|
it('reports whisper as running when compose ps shows active container', async () => {
|
||||||
|
const seenCalls: string[][] = [];
|
||||||
const runner = async (args: string[]) => {
|
const runner = async (args: string[]) => {
|
||||||
if (args[0] === 'config') {
|
seenCalls.push(args);
|
||||||
|
if (args.includes('config')) {
|
||||||
return { stdout: 'flynn\nwhisper-server\n', stderr: '' };
|
return { stdout: 'flynn\nwhisper-server\n', stderr: '' };
|
||||||
}
|
}
|
||||||
if (args[0] === 'ps') {
|
if (args.includes('ps')) {
|
||||||
return {
|
return {
|
||||||
stdout: JSON.stringify([{
|
stdout: JSON.stringify([{
|
||||||
Name: 'flynn-whisper-server-1',
|
Name: 'flynn-whisper-server-1',
|
||||||
@@ -48,14 +50,16 @@ describe('listDockerDependencyStatuses', () => {
|
|||||||
statusText: 'Up 4 minutes (healthy)',
|
statusText: 'Up 4 minutes (healthy)',
|
||||||
containerName: 'flynn-whisper-server-1',
|
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 () => {
|
it('reports whisper as defined but not started when no container exists yet', async () => {
|
||||||
const runner = async (args: string[]) => {
|
const runner = async (args: string[]) => {
|
||||||
if (args[0] === 'config') {
|
if (args.includes('config')) {
|
||||||
return { stdout: 'flynn\nwhisper-server\n', stderr: '' };
|
return { stdout: 'flynn\nwhisper-server\n', stderr: '' };
|
||||||
}
|
}
|
||||||
if (args[0] === 'ps') {
|
if (args.includes('ps')) {
|
||||||
return { stdout: '[]', stderr: '' };
|
return { stdout: '[]', stderr: '' };
|
||||||
}
|
}
|
||||||
throw new Error(`Unexpected args: ${args.join(' ')}`);
|
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 () => {
|
it('reports whisper service as missing when compose file does not define it', async () => {
|
||||||
const runner = async (args: string[]) => {
|
const runner = async (args: string[]) => {
|
||||||
if (args[0] === 'config') {
|
if (args.includes('config')) {
|
||||||
return { stdout: 'flynn\n', stderr: '' };
|
return { stdout: 'flynn\n', stderr: '' };
|
||||||
}
|
}
|
||||||
throw new Error(`Unexpected args: ${args.join(' ')}`);
|
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 () => {
|
it('marks whisper as not configured for non-local transcription endpoints', async () => {
|
||||||
const runner = async (args: string[]) => {
|
const runner = async (args: string[]) => {
|
||||||
if (args[0] === 'config') {
|
if (args.includes('config')) {
|
||||||
return { stdout: 'whisper-server\n', stderr: '' };
|
return { stdout: 'whisper-server\n', stderr: '' };
|
||||||
}
|
}
|
||||||
if (args[0] === 'ps') {
|
if (args.includes('ps')) {
|
||||||
return { stdout: '[]', stderr: '' };
|
return { stdout: '[]', stderr: '' };
|
||||||
}
|
}
|
||||||
throw new Error(`Unexpected args: ${args.join(' ')}`);
|
throw new Error(`Unexpected args: ${args.join(' ')}`);
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ interface ComposePsEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const WHISPER_SERVICE = 'whisper-server';
|
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> {
|
function defaultRunner(args: string[]): Promise<DockerComposeResult> {
|
||||||
return execFile('docker', ['compose', '-f', 'docker-compose.yml', ...args], {
|
return execFile('docker', ['compose', '-f', 'docker-compose.yml', ...args], {
|
||||||
@@ -154,7 +159,7 @@ export async function listDockerDependencyStatuses(
|
|||||||
|
|
||||||
let services: string[];
|
let services: string[];
|
||||||
try {
|
try {
|
||||||
const response = await runner(['config', '--services']);
|
const response = await runner(withWhisperProfile(['config', '--services']));
|
||||||
services = parseServiceList(response.stdout);
|
services = parseServiceList(response.stdout);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [{
|
return [{
|
||||||
@@ -174,7 +179,7 @@ export async function listDockerDependencyStatuses(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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)
|
const rows = parseComposePsOutput(response.stdout)
|
||||||
.filter((entry) => (entry.Service ?? '') === WHISPER_SERVICE || !entry.Service);
|
.filter((entry) => (entry.Service ?? '') === WHISPER_SERVICE || !entry.Service);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user