fix(sdk): link dev UI to workspace application detail page (#20849)

sdk handle auth of one workspace per session -- but server could be
configured as multi or single -- hence for multi get subdomain -- and
for single the localhost fallback!
also: link includes applicationId so it opens the app detail page
directly (not the list)

## QA
multi workspace flag on -

<img width="2996" height="1712" alt="CleanShot 2026-05-22 at 18 21
31@2x"
src="https://github.com/user-attachments/assets/8499b9f3-b22e-45e2-8b97-4b27fadc3c94"
/>

multi workspace flag off - 

<img width="3012" height="1734" alt="CleanShot 2026-05-22 at 18 14
37@2x"
src="https://github.com/user-attachments/assets/3af2f492-5e2d-4a4b-8251-c3343d79ae9e"
/>
This commit is contained in:
nitin
2026-05-22 19:49:27 +05:30
committed by GitHub
parent 4554dbe3c9
commit 59d69e2f5c
8 changed files with 182 additions and 11 deletions
@@ -123,9 +123,8 @@ export class OrchestratorState {
private eventIdCounter = 0;
onChange?: () => void;
constructor(options: { appPath: string; frontendUrl?: string }) {
constructor(options: { appPath: string }) {
this.appPath = options.appPath;
this.frontendUrl = options.frontendUrl;
this.previousObjectsFieldsFingerprint = null;
@@ -2,6 +2,7 @@ import { type ApiService } from '@/cli/utilities/api/api-service';
import { ConfigService } from '@/cli/utilities/config/config-service';
import { type OrchestratorState } from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state';
import { detectLocalServer } from '@/cli/utilities/server/detect-local-server';
import { isDefined } from 'twenty-shared/utils';
export type CheckServerOrchestratorStepOutput = {
isReady: boolean;
@@ -88,6 +89,14 @@ export class CheckServerOrchestratorStep {
step.output = { isReady: true, errorLogged: false };
step.status = 'done';
if (!isDefined(this.state.frontendUrl)) {
const frontendUrl = await this.apiService.getWorkspaceFrontendUrl();
if (isDefined(frontendUrl)) {
this.state.frontendUrl = frontendUrl;
}
}
if (!wasReady) {
this.notify();
}
@@ -7,6 +7,7 @@ import {
type OrchestratorStateEntityInfo,
} from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state';
import { SyncableEntity } from 'twenty-shared/application';
import { isDefined } from 'twenty-shared/utils';
export type DevUiStatus =
| 'idle'
@@ -157,14 +158,13 @@ export const groupEntitiesByType = (
};
export const getApplicationUrl = (state: OrchestratorState): string | null => {
if (
!state.frontendUrl ||
!state.steps.resolveApplication.output.universalIdentifier
) {
const applicationId = state.steps.resolveApplication.output.applicationId;
if (!isDefined(state.frontendUrl) || !isDefined(applicationId)) {
return null;
}
return `${state.frontendUrl}/settings/applications`;
return `${state.frontendUrl}/settings/applications/${applicationId}`;
};
export const mergeStepStatuses = (