mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-24 08:29:16 +00:00
feat(copilot): add raw settings API and model mapping routes
- add GET/PUT /api/copilot/settings/raw for copilot.settings.json - update PUT /api/copilot/config to handle model mapping fields - sync model mappings between settings file and unified config
This commit is contained in:
@@ -1675,6 +1675,13 @@ apiRoutes.put('/copilot/config', (req: Request, res: Response): void => {
|
|||||||
config.copilot?.wait_on_limit ??
|
config.copilot?.wait_on_limit ??
|
||||||
DEFAULT_COPILOT_CONFIG.wait_on_limit,
|
DEFAULT_COPILOT_CONFIG.wait_on_limit,
|
||||||
model: updates.model ?? config.copilot?.model ?? DEFAULT_COPILOT_CONFIG.model,
|
model: updates.model ?? config.copilot?.model ?? DEFAULT_COPILOT_CONFIG.model,
|
||||||
|
// Model mapping for opus/sonnet/haiku tiers
|
||||||
|
opus_model:
|
||||||
|
updates.opus_model !== undefined ? updates.opus_model : config.copilot?.opus_model,
|
||||||
|
sonnet_model:
|
||||||
|
updates.sonnet_model !== undefined ? updates.sonnet_model : config.copilot?.sonnet_model,
|
||||||
|
haiku_model:
|
||||||
|
updates.haiku_model !== undefined ? updates.haiku_model : config.copilot?.haiku_model,
|
||||||
};
|
};
|
||||||
|
|
||||||
saveUnifiedConfig(config);
|
saveUnifiedConfig(config);
|
||||||
@@ -1756,3 +1763,97 @@ apiRoutes.post('/copilot/daemon/stop', async (_req: Request, res: Response): Pro
|
|||||||
res.status(500).json({ error: (error as Error).message });
|
res.status(500).json({ error: (error as Error).message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/copilot/settings/raw - Get raw copilot.settings.json
|
||||||
|
* Returns the raw JSON content for editing in the code editor
|
||||||
|
*/
|
||||||
|
apiRoutes.get('/copilot/settings/raw', (_req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const settingsPath = path.join(getCcsDir(), 'copilot.settings.json');
|
||||||
|
const config = loadOrCreateUnifiedConfig();
|
||||||
|
const copilotConfig = config.copilot ?? DEFAULT_COPILOT_CONFIG;
|
||||||
|
|
||||||
|
// Default model for all tiers
|
||||||
|
const defaultModel = copilotConfig.model;
|
||||||
|
|
||||||
|
// If file doesn't exist, return default structure with all model mappings
|
||||||
|
if (!fs.existsSync(settingsPath)) {
|
||||||
|
// Create settings structure matching CLIProxy pattern - always include all model mappings
|
||||||
|
const defaultSettings = {
|
||||||
|
env: {
|
||||||
|
ANTHROPIC_BASE_URL: `http://localhost:${copilotConfig.port}`,
|
||||||
|
ANTHROPIC_AUTH_TOKEN: 'copilot-managed',
|
||||||
|
ANTHROPIC_MODEL: defaultModel,
|
||||||
|
ANTHROPIC_DEFAULT_OPUS_MODEL: copilotConfig.opus_model || defaultModel,
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL: copilotConfig.sonnet_model || defaultModel,
|
||||||
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: copilotConfig.haiku_model || defaultModel,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: defaultSettings,
|
||||||
|
mtime: Date.now(),
|
||||||
|
path: `~/.ccs/copilot.settings.json`,
|
||||||
|
exists: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = fs.readFileSync(settingsPath, 'utf-8');
|
||||||
|
const settings = JSON.parse(content);
|
||||||
|
const stat = fs.statSync(settingsPath);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings,
|
||||||
|
mtime: stat.mtimeMs,
|
||||||
|
path: `~/.ccs/copilot.settings.json`,
|
||||||
|
exists: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT /api/copilot/settings/raw - Save raw copilot.settings.json
|
||||||
|
* Saves the raw JSON content from the code editor
|
||||||
|
*/
|
||||||
|
apiRoutes.put('/copilot/settings/raw', (req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const { settings, expectedMtime } = req.body;
|
||||||
|
const settingsPath = path.join(getCcsDir(), 'copilot.settings.json');
|
||||||
|
|
||||||
|
// Check for conflict if file exists and expectedMtime provided
|
||||||
|
if (fs.existsSync(settingsPath) && expectedMtime) {
|
||||||
|
const stat = fs.statSync(settingsPath);
|
||||||
|
if (Math.abs(stat.mtimeMs - expectedMtime) > 1000) {
|
||||||
|
res.status(409).json({ error: 'File modified externally', mtime: stat.mtimeMs });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write settings file atomically
|
||||||
|
const tempPath = settingsPath + '.tmp';
|
||||||
|
fs.writeFileSync(tempPath, JSON.stringify(settings, null, 2) + '\n');
|
||||||
|
fs.renameSync(tempPath, settingsPath);
|
||||||
|
|
||||||
|
// Also sync model mappings back to unified config
|
||||||
|
const config = loadOrCreateUnifiedConfig();
|
||||||
|
const env = settings.env || {};
|
||||||
|
|
||||||
|
config.copilot = {
|
||||||
|
...(config.copilot ?? DEFAULT_COPILOT_CONFIG),
|
||||||
|
model: env.ANTHROPIC_MODEL || config.copilot?.model || DEFAULT_COPILOT_CONFIG.model,
|
||||||
|
opus_model: env.ANTHROPIC_DEFAULT_OPUS_MODEL || undefined,
|
||||||
|
sonnet_model: env.ANTHROPIC_DEFAULT_SONNET_MODEL || undefined,
|
||||||
|
haiku_model: env.ANTHROPIC_DEFAULT_HAIKU_MODEL || undefined,
|
||||||
|
};
|
||||||
|
saveUnifiedConfig(config);
|
||||||
|
|
||||||
|
const stat = fs.statSync(settingsPath);
|
||||||
|
res.json({ success: true, mtime: stat.mtimeMs });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user