Merge origin/main into deferred OpenCode restart branch.

Resolve ProvidersPage and lifecycle conflicts with custom providers and
AppImage ARGV0 stripping. Address review follow-ups: OAuth index helper +
tests, single auth-methods load trigger, shared Google env-alias module with
VS Code parity coverage, and deferred restart for custom provider upsert.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-08-03 13:57:05 +00:00
co-authored by Serhii Dziupin
94 changed files with 5949 additions and 372 deletions
@@ -20,6 +20,7 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
resolveProjectDirectory,
getProviderSources,
removeProviderConfig,
upsertProviderConfig,
refreshOpenCodeAfterConfigChange,
buildOpenCodeUrl,
getOpenCodeAuthHeaders,
@@ -445,6 +446,63 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
}
});
app.put('/api/provider', async (req, res) => {
try {
const providerID = typeof req.body?.providerID === 'string'
? req.body.providerID.trim()
: (typeof req.body?.providerId === 'string' ? req.body.providerId.trim() : '');
const config = req.body?.config;
const scope = typeof req.body?.scope === 'string' ? req.body.scope : 'user';
if (!providerID) {
return res.status(400).json({ error: 'Provider ID is required' });
}
if (!config || typeof config !== 'object' || Array.isArray(config)) {
return res.status(400).json({ error: 'Provider config is required' });
}
if (scope !== 'user' && scope !== 'project' && scope !== 'custom') {
return res.status(400).json({ error: 'Invalid scope' });
}
const headerDirectory = typeof req.get === 'function' ? req.get('x-opencode-directory') : null;
const queryDirectory = Array.isArray(req.query?.directory)
? req.query.directory[0]
: req.query?.directory;
const requestedDirectory = headerDirectory || queryDirectory || null;
let directory = null;
if (scope === 'project' || requestedDirectory) {
const resolved = await resolveProjectDirectory(req);
if (!resolved.directory) {
return res.status(400).json({ error: resolved.error || 'Working directory is required' });
}
directory = resolved.directory;
} else {
const resolved = await resolveProjectDirectory(req);
if (resolved.directory) {
directory = resolved.directory;
}
}
const { getProviderAuth } = await getAuthLibrary();
const hasStoredAuth = Boolean(getProviderAuth(providerID));
const upsertResult = upsertProviderConfig(providerID, config, directory, scope, { hasStoredAuth });
return res.json({
...buildDeferredRestartResponse(
`Provider ${providerID} saved. Restart OpenCode to apply.`,
),
providerId: upsertResult.providerId,
path: upsertResult.path,
config: upsertResult.config,
});
} catch (error) {
const status = typeof error?.statusCode === 'number' ? error.statusCode : 500;
console.error('Failed to upsert provider config:', error);
return res.status(status).json({ error: error.message || 'Failed to save provider config' });
}
});
app.delete('/api/provider/:providerId/auth', async (req, res) => {
try {
const { providerId } = req.params;