Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions ui/server/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@ import { getPilotDeckGateway } from '../pilotdeck-bridge.js';
async function notifyGatewayConfigReload() {
try {
const gw = await getPilotDeckGateway();
if (gw?.reloadConfig) await gw.reloadConfig();
} catch { /* gateway unreachable — self-watch will pick up the change */ }
if (!gw?.reloadConfig) {
return { skipped: true, reason: 'gateway_reload_unavailable' };
}
const result = await gw.reloadConfig();
return { reloaded: result?.reloaded !== false, ...(result?.changedPaths ? { changedPaths: result.changedPaths } : {}) };
} catch (error) {
return {
error: error instanceof Error ? error.message : String(error),
};
}
}

const router = express.Router();
Expand Down Expand Up @@ -126,8 +134,10 @@ router.put('/', async (req, res) => {
return res.status(400).json({ error: 'raw YAML or config object is required' });
}

const reloadResult = await reloadPilotDeckConfig(saved.config);
void notifyGatewayConfigReload();
const reloadResult = {
...(await reloadPilotDeckConfig(saved.config)),
gateway: await notifyGatewayConfigReload(),
};
// Re-read disk so the response's `raw` field comes from the actual
// (lossless) file rather than the lossy round-trip output, and so
// `serializeConfigResponse` has a `rawYaml` to render the full view.
Expand All @@ -150,8 +160,10 @@ router.post('/reload', async (_req, res) => {
if (!validation.valid) {
return res.status(400).json({ error: 'Invalid config', validation });
}
const reloadResult = await reloadPilotDeckConfig(record.config);
void notifyGatewayConfigReload();
const reloadResult = {
...(await reloadPilotDeckConfig(record.config)),
gateway: await notifyGatewayConfigReload(),
};
const response = serializeConfigResponse(record, reloadResult);
broadcastConfigEvent({ source: 'ui-reload', ...response, timestamp: new Date().toISOString() });
res.json(response);
Expand Down
24 changes: 22 additions & 2 deletions ui/src/hooks/usePilotDeckConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ type ReloadInfo = {
at: number;
};

function reloadErrorMessage(reload: ConfigReload | undefined, action: 'save' | 'reload'): string | null {
const gatewayError = reload?.gateway?.error;
if (gatewayError) {
return action === 'save'
? `Saved config, but gateway reload failed: ${gatewayError}`
: `Gateway reload failed: ${gatewayError}`;
}
return null;
}

export function usePilotDeckConfig() {
const [path, setPath] = useState('');
const [raw, setRaw] = useState('');
Expand Down Expand Up @@ -210,7 +220,12 @@ export function usePilotDeckConfig() {
const data = await response.json();
if (!response.ok) throw new Error(data.error || data.validation?.errors?.join(', ') || 'Failed to save config');
applyResponse(data, 'ui-save');
setMessage('Saved and reloaded');
const reloadError = reloadErrorMessage(data.reload, 'save');
if (reloadError) {
setError(reloadError);
} else {
setMessage('Saved and reloaded');
}
setExternalChangeNotice(null);
} catch (caught) {
setError(caught instanceof Error ? caught.message : 'Failed to save config');
Expand All @@ -228,7 +243,12 @@ export function usePilotDeckConfig() {
const data = await response.json();
if (!response.ok) throw new Error(data.error || 'Failed to reload config');
applyResponse(data, 'ui-reload');
setMessage('Reloaded current config');
const reloadError = reloadErrorMessage(data.reload, 'reload');
if (reloadError) {
setError(reloadError);
} else {
setMessage('Reloaded current config');
}
} catch (caught) {
setError(caught instanceof Error ? caught.message : 'Failed to reload config');
} finally {
Expand Down