Merge main into refactor/settings: add install mode detection (#418)

Resolves merge conflict in VersionUpgradeModal.tsx — combines PR #402's
file relocation and copyTextToClipboard utility with the installMode
feature from main that detects git vs npm installs and shows the
correct upgrade command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
simosmik
2026-02-23 22:33:55 +00:00
16 changed files with 115 additions and 18 deletions

37
CHANGELOG.md Normal file
View File

@@ -0,0 +1,37 @@
# Changelog
All notable changes to CloudCLI UI will be documented in this file.
## [1.20.1](https://github.com/siteboon/claudecodeui/compare/v1.19.1...v1.20.1) (2026-02-23)
### New Features
* implement install mode detection and update commands in version upgrade process ([f986004](https://github.com/siteboon/claudecodeui/commit/f986004319207b068431f9f6adf338a8ce8decfc))
* migrate legacy database to new location and improve last login update handling ([50e097d](https://github.com/siteboon/claudecodeui/commit/50e097d4ac498aa9f1803ef3564843721833dc19))
## [1.19.1](https://github.com/siteboon/claudecodeui/compare/v1.19.0...v1.19.1) (2026-02-23)
### Bug Fixes
* add prepublishOnly script to build before publishing ([82efac4](https://github.com/siteboon/claudecodeui/commit/82efac4704cab11ed8d1a05fe84f41312140b223))
## [1.19.0](https://github.com/siteboon/claudecodeui/compare/v1.18.2...v1.19.0) (2026-02-23)
### New Features
* add HOST environment variable for configurable bind address ([#360](https://github.com/siteboon/claudecodeui/issues/360)) ([cccd915](https://github.com/siteboon/claudecodeui/commit/cccd915c336192216b6e6f68e2b5f3ece0ccf966))
* subagent tool grouping ([#398](https://github.com/siteboon/claudecodeui/issues/398)) ([0207a1f](https://github.com/siteboon/claudecodeui/commit/0207a1f3a3c87f1c6c1aee8213be999b23289386))
### Bug Fixes
* **macos:** fix node-pty posix_spawnp error with postinstall script ([#347](https://github.com/siteboon/claudecodeui/issues/347)) ([38a593c](https://github.com/siteboon/claudecodeui/commit/38a593c97fdb2bb7f051e09e8e99c16035448655)), closes [#284](https://github.com/siteboon/claudecodeui/issues/284)
* slash commands with arguments bypass command execution ([#392](https://github.com/siteboon/claudecodeui/issues/392)) ([597e9c5](https://github.com/siteboon/claudecodeui/commit/597e9c54b76e7c6cd1947299c668c78d24019cab))
### Refactoring
* **releases:** Create a contributing guide and proper release notes using a release-it plugin ([fc369d0](https://github.com/siteboon/claudecodeui/commit/fc369d047e13cba9443fe36c0b6bb2ce3beaf61c))
### Maintenance
* update @anthropic-ai/claude-agent-sdk to version 0.1.77 in package-lock.json ([#410](https://github.com/siteboon/claudecodeui/issues/410)) ([7ccbc8d](https://github.com/siteboon/claudecodeui/commit/7ccbc8d92d440e18c157b656c9ea2635044a64f6))

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@siteboon/claude-code-ui",
"version": "1.18.2",
"version": "1.20.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@siteboon/claude-code-ui",
"version": "1.18.2",
"version": "1.20.1",
"hasInstallScript": true,
"license": "GPL-3.0",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@siteboon/claude-code-ui",
"version": "1.18.2",
"version": "1.20.1",
"description": "A web-based UI for Claude Code CLI",
"type": "module",
"main": "server/index.js",
@@ -32,6 +32,7 @@
"typecheck": "tsc --noEmit -p tsconfig.json",
"start": "npm run build && npm run server",
"release": "./release.sh",
"prepublishOnly": "npm run build",
"postinstall": "node scripts/fix-node-pty.js"
},
"keywords": [

View File

@@ -40,6 +40,22 @@ if (process.env.DATABASE_PATH) {
}
}
// As part of 1.19.2 we are introducing a new location for auth.db. The below handles exisitng moving legacy database from install directory to new location
const LEGACY_DB_PATH = path.join(__dirname, 'auth.db');
if (DB_PATH !== LEGACY_DB_PATH && !fs.existsSync(DB_PATH) && fs.existsSync(LEGACY_DB_PATH)) {
try {
fs.copyFileSync(LEGACY_DB_PATH, DB_PATH);
console.log(`[MIGRATION] Copied database from ${LEGACY_DB_PATH} to ${DB_PATH}`);
for (const suffix of ['-wal', '-shm']) {
if (fs.existsSync(LEGACY_DB_PATH + suffix)) {
fs.copyFileSync(LEGACY_DB_PATH + suffix, DB_PATH + suffix);
}
}
} catch (err) {
console.warn(`[MIGRATION] Could not copy legacy database: ${err.message}`);
}
}
// Create database connection
const db = new Database(DB_PATH);
@@ -128,12 +144,12 @@ const userDb = {
}
},
// Update last login time
// Update last login time (non-fatal — logged but not thrown)
updateLastLogin: (userId) => {
try {
db.prepare('UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?').run(userId);
} catch (err) {
throw err;
console.warn('Failed to update last login:', err.message);
}
},

View File

@@ -9,6 +9,8 @@ import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const installMode = fs.existsSync(path.join(__dirname, '..', '.git')) ? 'git' : 'npm';
// ANSI color codes for terminal output
const colors = {
reset: '\x1b[0m',
@@ -333,7 +335,8 @@ app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString()
timestamp: new Date().toISOString(),
installMode
});
});
@@ -410,11 +413,13 @@ app.post('/api/system/update', authenticateToken, async (req, res) => {
console.log('Starting system update from directory:', projectRoot);
// Run the update command
const updateCommand = 'git checkout main && git pull && npm install';
// Run the update command based on install mode
const updateCommand = installMode === 'git'
? 'git checkout main && git pull && npm install'
: 'npm install -g @siteboon/claude-code-ui@latest';
const child = spawn('sh', ['-c', updateCommand], {
cwd: projectRoot,
cwd: installMode === 'git' ? projectRoot : os.homedir(),
env: process.env
});

View File

@@ -1,5 +1,6 @@
// Load environment variables from .env before other imports execute.
import fs from 'fs';
import os from 'os';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
@@ -22,3 +23,7 @@ try {
} catch (e) {
console.log('No .env file found or error reading it:', e.message);
}
if (!process.env.DATABASE_PATH) {
process.env.DATABASE_PATH = path.join(os.homedir(), '.cloudcli', 'auth.db');
}

View File

@@ -53,11 +53,11 @@ router.post('/register', async (req, res) => {
// Generate token
const token = generateToken(user);
// Update last login
db.prepare('COMMIT').run();
// Update last login (non-fatal, outside transaction)
userDb.updateLastLogin(user.id);
db.prepare('COMMIT').run();
res.json({
success: true,
user: { id: user.id, username: user.username },

View File

@@ -63,5 +63,5 @@ export const CODEX_MODELS = {
{ value: 'o4-mini', label: 'O4-mini' }
],
DEFAULT: 'gpt-5.2'
DEFAULT: 'gpt-5.3-codex'
};

View File

@@ -38,7 +38,7 @@ function Sidebar({
}: SidebarProps) {
const { t } = useTranslation(['sidebar', 'common']);
const { isPWA } = useDeviceSettings({ trackMobile: false });
const { updateAvailable, latestVersion, currentVersion, releaseInfo } = useVersionCheck(
const { updateAvailable, latestVersion, currentVersion, releaseInfo, installMode } = useVersionCheck(
'siteboon',
'claudecodeui',
);
@@ -200,6 +200,7 @@ function Sidebar({
releaseInfo={releaseInfo}
currentVersion={currentVersion}
latestVersion={latestVersion}
installMode={installMode}
t={t}
/>

View File

@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import { authenticatedFetch } from "../../../../utils/api";
import { ReleaseInfo } from "../../../../types/sharedTypes";
import { copyTextToClipboard } from "../../../../utils/clipboard";
import type { InstallMode } from "../../../../hooks/useVersionCheck";
interface VersionUpgradeModalProps {
isOpen: boolean;
@@ -10,6 +11,7 @@ interface VersionUpgradeModalProps {
releaseInfo: ReleaseInfo | null;
currentVersion: string;
latestVersion: string | null;
installMode: InstallMode;
}
export default function VersionUpgradeModal({
@@ -17,9 +19,13 @@ export default function VersionUpgradeModal({
onClose,
releaseInfo,
currentVersion,
latestVersion
latestVersion,
installMode
}: VersionUpgradeModalProps) {
const { t } = useTranslation('common');
const upgradeCommand = installMode === 'npm'
? t('versionUpdate.npmUpgradeCommand')
: 'git checkout main && git pull && npm install';
const [isUpdating, setIsUpdating] = useState(false);
const [updateOutput, setUpdateOutput] = useState('');
const [updateError, setUpdateError] = useState('');
@@ -151,7 +157,7 @@ export default function VersionUpgradeModal({
<h3 className="text-sm font-medium text-gray-900 dark:text-white">{t('versionUpdate.manualUpgrade')}</h3>
<div className="bg-gray-100 dark:bg-gray-800 rounded-lg p-3 border">
<code className="text-sm text-gray-800 dark:text-gray-200 font-mono">
git checkout main && git pull && npm install
{upgradeCommand}
</code>
</div>
<p className="text-xs text-gray-600 dark:text-gray-400">
@@ -171,7 +177,7 @@ export default function VersionUpgradeModal({
{!updateOutput && (
<>
<button
onClick={() => copyTextToClipboard('git checkout main && git pull && npm install')}
onClick={() => copyTextToClipboard(upgradeCommand)}
className="flex-1 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-md transition-colors"
>
{t('versionUpdate.buttons.copyCommand')}

View File

@@ -8,6 +8,7 @@ import Settings from '../../../settings/view/Settings';
import VersionUpgradeModal from '../modals/VersionUpgradeModal';
import type { Project } from '../../../../types/app';
import type { ReleaseInfo } from '../../../../types/sharedTypes';
import type { InstallMode } from '../../../../hooks/useVersionCheck';
import { normalizeProjectForSettings } from '../../utils/utils';
import type { DeleteProjectConfirmation, SessionDeleteConfirmation, SettingsProject } from '../../types/types';
@@ -30,6 +31,7 @@ type SidebarModalsProps = {
releaseInfo: ReleaseInfo | null;
currentVersion: string;
latestVersion: string | null;
installMode: InstallMode;
t: TFunction;
};
@@ -65,6 +67,7 @@ export default function SidebarModals({
releaseInfo,
currentVersion,
latestVersion,
installMode,
t,
}: SidebarModalsProps) {
// Settings expects project identity/path fields to be present for dropdown labels and local-scope MCP config.
@@ -199,6 +202,7 @@ export default function SidebarModals({
releaseInfo={releaseInfo}
currentVersion={currentVersion}
latestVersion={latestVersion}
installMode={installMode}
/>
</>
);

View File

@@ -21,10 +21,28 @@ const compareVersions = (v1: string, v2: string) => {
return 0;
};
export type InstallMode = 'git' | 'npm';
export const useVersionCheck = (owner: string, repo: string) => {
const [updateAvailable, setUpdateAvailable] = useState(false);
const [latestVersion, setLatestVersion] = useState<string | null>(null);
const [releaseInfo, setReleaseInfo] = useState<ReleaseInfo | null>(null);
const [installMode, setInstallMode] = useState<InstallMode>('git');
useEffect(() => {
const fetchInstallMode = async () => {
try {
const response = await fetch('/health');
const data = await response.json();
if (data.installMode === 'npm' || data.installMode === 'git') {
setInstallMode(data.installMode);
}
} catch {
// Default to git on error
}
};
fetchInstallMode();
}, []);
useEffect(() => {
const checkVersion = async () => {
@@ -66,5 +84,5 @@ export const useVersionCheck = (owner: string, repo: string) => {
return () => clearInterval(interval);
}, [owner, repo]);
return { updateAvailable, latestVersion, currentVersion: version, releaseInfo };
return { updateAvailable, latestVersion, currentVersion: version, releaseInfo, installMode };
};

View File

@@ -200,6 +200,7 @@
"viewFullRelease": "View full release",
"updateProgress": "Update Progress:",
"manualUpgrade": "Manual upgrade:",
"npmUpgradeCommand": "npm install -g @siteboon/claude-code-ui@latest",
"manualUpgradeHint": "Or click \"Update Now\" to run the update automatically.",
"updateCompleted": "Update completed successfully!",
"restartServer": "Please restart the server to apply changes.",

View File

@@ -200,6 +200,7 @@
"viewFullRelease": "リリース全文を見る",
"updateProgress": "アップデートの進捗:",
"manualUpgrade": "手動アップグレード:",
"npmUpgradeCommand": "npm install -g @siteboon/claude-code-ui@latest",
"manualUpgradeHint": "または「今すぐ更新」をクリックして自動的にアップデートを実行できます。",
"updateCompleted": "アップデートが完了しました!",
"restartServer": "変更を適用するにはサーバーを再起動してください。",

View File

@@ -200,6 +200,7 @@
"viewFullRelease": "전체 릴리스 보기",
"updateProgress": "업데이트 진행 상황:",
"manualUpgrade": "수동 업그레이드:",
"npmUpgradeCommand": "npm install -g @siteboon/claude-code-ui@latest",
"manualUpgradeHint": "또는 \"지금 업데이트\"를 클릭하여 자동으로 업데이트합니다.",
"updateCompleted": "업데이트가 완료되었습니다!",
"restartServer": "변경사항을 적용하려면 서버를 재시작하세요.",

View File

@@ -200,6 +200,7 @@
"viewFullRelease": "查看完整发布",
"updateProgress": "更新进度:",
"manualUpgrade": "手动升级:",
"npmUpgradeCommand": "npm install -g @siteboon/claude-code-ui@latest",
"manualUpgradeHint": "或点击'立即更新'以自动运行更新。",
"updateCompleted": "更新成功完成!",
"restartServer": "请重启服务器以应用更改。",