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
15 changes: 13 additions & 2 deletions src/integrations/dotnet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,27 @@ export async function run(options: InstallerOptions): Promise<string> {
integration: config.metadata.integration,
});

const { apiKey, clientId } = await getOrAskForWorkOSCredentials(options, config.environment.requiresApiKey);
// apiKey/clientId are mutable: dashboard-config 401 recovery may swap in a
// fresh credential pair from a different environment.
const { apiKey: initialApiKey, clientId: initialClientId } = await getOrAskForWorkOSCredentials(
options,
config.environment.requiresApiKey,
);
let apiKey = initialApiKey;
let clientId = initialClientId;

// Auto-configure WorkOS environment (redirect URI, CORS, homepage)
const callerHandledConfig = Boolean(options.apiKey || options.clientId);
if (!callerHandledConfig && apiKey) {
const port = 5000; // ASP.NET Core default HTTP port
await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, port, {
const outcome = await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, port, {
homepageUrl: options.homepageUrl,
redirectUri: options.redirectUri,
});
if (outcome) {
apiKey = outcome.apiKey;
if (outcome.clientId) clientId = outcome.clientId;
}
}

// Build prompt — credentials are passed via prompt context since .NET doesn't use .env.local
Expand Down
15 changes: 13 additions & 2 deletions src/integrations/go/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,27 @@ export async function run(options: InstallerOptions): Promise<string> {
});

// Get WorkOS credentials
const { apiKey, clientId } = await getOrAskForWorkOSCredentials(options, config.environment.requiresApiKey);
// apiKey/clientId are mutable: dashboard-config 401 recovery may swap in a
// fresh credential pair from a different environment.
const { apiKey: initialApiKey, clientId: initialClientId } = await getOrAskForWorkOSCredentials(
options,
config.environment.requiresApiKey,
);
let apiKey = initialApiKey;
let clientId = initialClientId;

// Auto-configure WorkOS environment (redirect URI, CORS)
const callerHandledConfig = Boolean(options.apiKey || options.clientId);
if (!callerHandledConfig && apiKey) {
const redirectUri = options.redirectUri || `http://localhost:${GO_DEFAULT_PORT}${GO_CALLBACK_PATH}`;
await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, GO_DEFAULT_PORT, {
const outcome = await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, GO_DEFAULT_PORT, {
homepageUrl: options.homepageUrl,
redirectUri,
});
if (outcome) {
apiKey = outcome.apiKey;
if (outcome.clientId) clientId = outcome.clientId;
}
}

// Gather Go-specific context
Expand Down
14 changes: 11 additions & 3 deletions src/integrations/ruby/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,27 @@ export async function run(options: InstallerOptions): Promise<string> {
});

// Get WorkOS credentials
const { apiKey, clientId: _clientId } = await getOrAskForWorkOSCredentials(
// apiKey/clientId are mutable: dashboard-config 401 recovery may swap in a
// fresh credential pair from a different environment.
const { apiKey: initialApiKey, clientId: initialClientId } = await getOrAskForWorkOSCredentials(
options,
config.environment.requiresApiKey,
);
let apiKey = initialApiKey;
let clientId = initialClientId;

// Auto-configure WorkOS environment (redirect URI, CORS, homepage) if not already done
const callerHandledConfig = Boolean(options.apiKey || options.clientId);
if (!callerHandledConfig && apiKey) {
const port = 3000; // Rails default
await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, port, {
const outcome = await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, port, {
homepageUrl: options.homepageUrl,
redirectUri: options.redirectUri,
});
if (outcome) {
apiKey = outcome.apiKey;
if (outcome.clientId) clientId = outcome.clientId;
}
}

// Build prompt for the agent
Expand All @@ -107,7 +115,7 @@ export async function run(options: InstallerOptions): Promise<string> {

The following environment variables are needed (create a .env file if one does not exist):
- WORKOS_API_KEY
- WORKOS_CLIENT_ID
- WORKOS_CLIENT_ID=${clientId}
- WORKOS_REDIRECT_URI=${redirectUri}

## Integration Instructions
Expand Down
20 changes: 17 additions & 3 deletions src/lib/agent-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,15 @@ export async function runAgentInstaller(config: FrameworkConfig, options: Instal
integration: config.metadata.integration,
});

// Get WorkOS credentials (API key optional for client-only SDKs)
const { apiKey, clientId } = await getOrAskForWorkOSCredentials(options, config.environment.requiresApiKey);
// Get WorkOS credentials (API key optional for client-only SDKs).
// apiKey/clientId are mutable: dashboard-config 401 recovery may swap in a
// fresh credential pair from a different environment.
const { apiKey: initialApiKey, clientId: initialClientId } = await getOrAskForWorkOSCredentials(
options,
config.environment.requiresApiKey,
);
let apiKey = initialApiKey;
let clientId = initialClientId;

// Check if caller (state machine) already configured WorkOS environment
// If credentials were passed via options, the caller handled config+env writing
Expand All @@ -66,10 +73,17 @@ export async function runAgentInstaller(config: FrameworkConfig, options: Instal
// Skip if caller already handled this (prevents duplicate dashboard config output)
if (!callerHandledConfig && apiKey && config.environment.requiresApiKey) {
const port = detectPort(config.metadata.integration, options.installDir);
await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, port, {
const outcome = await autoConfigureWorkOSEnvironment(apiKey, config.metadata.integration, port, {
homepageUrl: options.homepageUrl,
redirectUri: options.redirectUri,
});
// If 401 recovery re-authenticated, continue with the credentials that
// worked. Use the recovered clientId when present — the new key may
// belong to a different environment than the original client ID.
if (outcome) {
apiKey = outcome.apiKey;
if (outcome.clientId) clientId = outcome.clientId;
}
}

// Gather framework-specific context (e.g., Next.js router, React Native platform)
Expand Down
22 changes: 18 additions & 4 deletions src/lib/run-with-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,18 +320,32 @@ export async function runWithCore(options: InstallerOptions): Promise<void> {
const redirectUri = installerOptions.redirectUri || `http://localhost:${port}${callbackPath}`;

const requiresApiKey = ['nextjs', 'tanstack-start', 'react-router'].includes(integration);
if (credentials.apiKey && requiresApiKey) {
await autoConfigureWorkOSEnvironment(credentials.apiKey, integration, port, {
// Mutable: dashboard-config 401 recovery may swap in a fresh credential pair.
let apiKey = credentials.apiKey;
let clientId = credentials.clientId;
if (apiKey && requiresApiKey) {
const outcome = await autoConfigureWorkOSEnvironment(apiKey, integration, port, {
homepageUrl: installerOptions.homepageUrl,
redirectUri: installerOptions.redirectUri,
});
// If 401 recovery re-authenticated, use the credentials that worked —
// the recovered key may belong to a different environment than the
// original client ID, so adopt the recovered clientId when present.
if (outcome) {
apiKey = outcome.apiKey;
if (outcome.clientId) clientId = outcome.clientId;
// Write back to the shared machine context so the later runAgent
// step hands the agent the working credentials, not the rejected key.
credentials.apiKey = apiKey;
credentials.clientId = clientId;
}
}

const redirectUriKey = integration === 'nextjs' ? 'NEXT_PUBLIC_WORKOS_REDIRECT_URI' : 'WORKOS_REDIRECT_URI';

writeEnvLocal(installerOptions.installDir, {
...(credentials.apiKey ? { WORKOS_API_KEY: credentials.apiKey } : {}),
WORKOS_CLIENT_ID: credentials.clientId,
...(apiKey ? { WORKOS_API_KEY: apiKey } : {}),
WORKOS_CLIENT_ID: clientId,
[redirectUriKey]: redirectUri,
});
}),
Expand Down
Loading
Loading