Skip to content

Commit 6c20844

Browse files
committed
fixing the dashboard url resetting
1 parent 2e29b80 commit 6c20844

9 files changed

Lines changed: 184 additions & 7 deletions

File tree

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [0.5.4] - 27/08/2026
4+
- Stop silently falling back to app.codeant.ai for login on a custom/self-hosted base URL
5+
- Added `set-dashboard-url`, `get-dashboard-url`, and `remove-dashboard-url` commands to explicitly configure the login dashboard URL
6+
37
## [0.5.3] - 24/08/2026
48
- Improved consistency between interactive and headless local reviews
59
- Added shared multi-file planning and verification before returning findings

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codeant-cli",
3-
"version": "0.5.3",
3+
"version": "0.5.4",
44
"description": "Code review CLI tool",
55
"type": "module",
66
"bin": {

src/commands/getDashboardUrl.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { Text, Box, useApp } from 'ink';
3+
import { getConfigValue } from '../utils/config.js';
4+
import { getBaseUrl, getDashboardUrl as resolveDashboardUrl } from '../utils/baseUrl.js';
5+
6+
export default function GetDashboardUrl() {
7+
const { exit } = useApp();
8+
const [state, setState] = useState({ status: 'loading' });
9+
10+
const envValue = process.env.CODEANT_DASHBOARD_URL;
11+
const configValue = getConfigValue('dashboardUrl');
12+
const source = envValue ? 'env' : configValue ? 'config' : 'upstream (queried from API base URL)';
13+
14+
useEffect(() => {
15+
(async () => {
16+
try {
17+
const url = await resolveDashboardUrl();
18+
setState({ status: 'resolved', url });
19+
} catch (err) {
20+
setState({ status: 'error', message: err.message });
21+
}
22+
exit();
23+
})();
24+
}, []);
25+
26+
if (state.status === 'loading') {
27+
return React.createElement(
28+
Box,
29+
{ flexDirection: 'column', padding: 1 },
30+
React.createElement(Text, null, 'Querying dashboard URL...')
31+
);
32+
}
33+
34+
if (state.status === 'error') {
35+
return React.createElement(
36+
Box,
37+
{ flexDirection: 'column', padding: 1 },
38+
React.createElement(Text, { color: 'red' }, '✗ ', state.message),
39+
React.createElement(Text, { color: 'gray' }, 'Base URL: ', getBaseUrl())
40+
);
41+
}
42+
43+
return React.createElement(
44+
Box,
45+
{ flexDirection: 'column', padding: 1 },
46+
React.createElement(Text, { bold: true }, 'Dashboard URL: ', state.url),
47+
React.createElement(Text, { color: 'gray' }, 'Source: ', source)
48+
);
49+
}

src/commands/login.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,15 @@ export default function Login() {
3030
let timeoutId;
3131

3232
(async () => {
33-
const dashboardUrl = await getDashboardUrl();
33+
let dashboardUrl;
34+
try {
35+
dashboardUrl = await getDashboardUrl();
36+
} catch (err) {
37+
setError(err.message);
38+
setStatus('error');
39+
setTimeout(() => exit(err), 100);
40+
return;
41+
}
3442
const loginUrl = `${dashboardUrl}?ideLoginToken=${token}`;
3543
setLoginUrl(loginUrl);
3644

src/commands/removeDashboardUrl.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { useEffect } from 'react';
2+
import { Text, Box, useApp } from 'ink';
3+
import { getConfigValue, setConfigValue } from '../utils/config.js';
4+
5+
export default function RemoveDashboardUrl() {
6+
const { exit } = useApp();
7+
8+
const hadValue = !!getConfigValue('dashboardUrl');
9+
10+
useEffect(() => {
11+
if (hadValue) {
12+
setConfigValue('dashboardUrl', null);
13+
}
14+
exit();
15+
}, []);
16+
17+
if (!hadValue) {
18+
return React.createElement(
19+
Box,
20+
{ flexDirection: 'column', padding: 1 },
21+
React.createElement(Text, { color: 'yellow' }, 'No dashboard URL override was set.')
22+
);
23+
}
24+
25+
return React.createElement(
26+
Box,
27+
{ flexDirection: 'column', padding: 1 },
28+
React.createElement(Text, { color: 'green' }, '✓ Dashboard URL override removed.'),
29+
React.createElement(Text, { color: 'gray' }, 'It will be auto-detected from the API base URL on next login.')
30+
);
31+
}

src/commands/setDashboardUrl.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React, { useEffect } from 'react';
2+
import { Text, Box, useApp } from 'ink';
3+
import { setConfigValue, CONFIG_FILE } from '../utils/config.js';
4+
5+
export default function SetDashboardUrl({ url }) {
6+
const { exit } = useApp();
7+
8+
useEffect(() => {
9+
if (!url) {
10+
exit(new Error('URL is required'));
11+
return;
12+
}
13+
14+
try {
15+
setConfigValue('dashboardUrl', url);
16+
exit();
17+
} catch (err) {
18+
exit(err);
19+
}
20+
}, []);
21+
22+
if (!url) {
23+
return React.createElement(
24+
Box,
25+
{ flexDirection: 'column', padding: 1 },
26+
React.createElement(Text, { color: 'red' }, '✗ Error: URL is required'),
27+
React.createElement(Text, { color: 'gray' }, 'Usage: codeant set-dashboard-url <url>')
28+
);
29+
}
30+
31+
return React.createElement(
32+
Box,
33+
{ flexDirection: 'column', padding: 1 },
34+
React.createElement(Text, { color: 'green' }, '✓ Dashboard URL set to: ', url),
35+
React.createElement(Text, { color: 'gray' }, 'Saved to: ', CONFIG_FILE)
36+
);
37+
}

src/index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { createRequire } from 'module';
77
import Secrets from './commands/secrets.js';
88
import SetBaseUrl from './commands/setBaseUrl.js';
99
import GetBaseUrl from './commands/getBaseUrl.js';
10+
import SetDashboardUrl from './commands/setDashboardUrl.js';
11+
import GetDashboardUrl from './commands/getDashboardUrl.js';
12+
import RemoveDashboardUrl from './commands/removeDashboardUrl.js';
1013
import SetApiKey from './commands/setApiKey.js';
1114
import GetApiKey from './commands/getApiKey.js';
1215
import Login from './commands/login.js';
@@ -188,6 +191,27 @@ program
188191
render(React.createElement(GetBaseUrl));
189192
});
190193

194+
program
195+
.command('set-dashboard-url <url>')
196+
.description('Set the web app dashboard URL used for login')
197+
.action((url) => {
198+
render(React.createElement(SetDashboardUrl, { url }));
199+
});
200+
201+
program
202+
.command('get-dashboard-url')
203+
.description('Show the current dashboard URL override')
204+
.action(() => {
205+
render(React.createElement(GetDashboardUrl));
206+
});
207+
208+
program
209+
.command('remove-dashboard-url')
210+
.description('Remove the dashboard URL override')
211+
.action(() => {
212+
render(React.createElement(RemoveDashboardUrl));
213+
});
214+
191215
program
192216
.command('set-codeant-api-key <key>')
193217
.description('Set the CodeAnt API key')

src/utils/baseUrl.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,46 @@ import { getConfigValue } from './config.js';
22

33
//
44
const NEW_BASE_URL = 'https://service.codeant.ai';
5+
const DEFAULT_BASE_URLS = new Set([
6+
'https://service.codeant.ai',
7+
'https://api.codeant.ai',
8+
'https://dev-api.codeant.ai',
9+
]);
510

611
const getBaseUrl = () => {
712
const url = process.env.CODEANT_API_URL || getConfigValue('baseUrl') || NEW_BASE_URL;
813
if (url === 'https://api.codeant.ai') return NEW_BASE_URL;
914
return url;
1015
};
1116

17+
const isDefaultBaseUrl = () => {
18+
const configuredUrl = process.env.CODEANT_API_URL || getConfigValue('baseUrl');
19+
return !configuredUrl || DEFAULT_BASE_URLS.has(configuredUrl);
20+
};
21+
1222
const getDashboardUrl = async () => {
23+
const override = process.env.CODEANT_DASHBOARD_URL || getConfigValue('dashboardUrl');
24+
if (override) return override;
25+
26+
const usingDefaultBaseUrl = isDefaultBaseUrl();
27+
1328
try {
1429
const response = await fetch(`${getBaseUrl()}/extension/get/dashboard`);
15-
// console.log('Fetching dashboard URL from:', `${getBaseUrl()}/extension/get/dashboard`);
1630
const data = await response.json();
17-
return data.dashboard_url || 'https://app.codeant.ai';
31+
// On a custom base URL, never silently trust an auto-detected app.codeant.ai —
32+
// that's the SaaS dashboard, and a self-hosted instance's OAuth apps won't
33+
// recognize it as a valid redirect target. Require an explicit override instead.
34+
if (data.dashboard_url && (usingDefaultBaseUrl || data.dashboard_url !== 'https://app.codeant.ai')) {
35+
return data.dashboard_url;
36+
}
1837
} catch {
19-
return 'https://app.codeant.ai';
38+
// fall through to the error below
2039
}
40+
41+
throw new Error(
42+
`Could not determine the dashboard URL for base URL "${getBaseUrl()}". ` +
43+
`Set it explicitly with: codeant set-dashboard-url <your web app URL>`
44+
);
2145
};
2246

2347
export { getBaseUrl, getDashboardUrl };

0 commit comments

Comments
 (0)