Compare commits

...

10 Commits

Author SHA1 Message Date
simosmik
ea19bd9a00 Release 1.13.5 2025-12-31 07:52:43 +00:00
viper151
6d4e5017d0 Merge pull request #257 from panta82/main
Fix issue: Broken pasted image upload
2025-12-31 08:49:03 +01:00
viper151
9b217ada0d Merge branch 'main' into main 2025-12-31 08:48:30 +01:00
simosmik
04efaa41f6 feat: add custom port and database path options to CLI commands 2025-12-31 07:42:01 +00:00
simosmik
5aef9c683a Release 1.13.3 2025-12-31 07:20:41 +00:00
simosmik
724cb5bb5c fix: adding shared folder to npm build 2025-12-31 07:17:39 +00:00
simosmik
4e163c8c10 Release 1.13.2 2025-12-30 18:10:21 +00:00
simosmik
b315360f8a fix: replace HOME env variable with os.homedir() to support windows 2025-12-30 18:07:04 +00:00
viper151
04821b8ad5 Merge pull request #273 from siteboon/fix/npmignore
Fix/npmignore
2025-12-30 18:53:22 +01:00
Ivan Pantic
19bb741af0 Fix issue: Broken pasted image upload 2025-12-12 00:27:09 +01:00
6 changed files with 92 additions and 56 deletions

View File

@@ -1,6 +1,6 @@
<div align="center"> <div align="center">
<img src="public/logo.svg" alt="Claude Code UI" width="64" height="64"> <img src="public/logo.svg" alt="Claude Code UI" width="64" height="64">
<h1>Claude Code UI</h1> <h1>Cloud CLI (aka Claude Code UI)</h1>
</div> </div>
@@ -88,32 +88,25 @@ claude-code-ui
**To restart**: Stop with Ctrl+C and run `claude-code-ui` again. **To restart**: Stop with Ctrl+C and run `claude-code-ui` again.
### CLI Commands ### CLI Usage
After global installation, you have access to both `claude-code-ui` and `cloudcli` commands: After global installation, you have access to both `claude-code-ui` and `cloudcli` commands:
| Command / Option | Short | Description |
|------------------|-------|-------------|
| `cloudcli` or `claude-code-ui` | | Start the server (default) |
| `cloudcli start` | | Start the server explicitly |
| `cloudcli status` | | Show configuration and data locations |
| `cloudcli help` | | Show help information |
| `cloudcli version` | | Show version information |
| `--port <port>` | `-p` | Set server port (default: 3001) |
| `--database-path <path>` | | Set custom database location |
**Examples:**
```bash ```bash
# Start the server (default command) cloudcli # Start with defaults
claude-code-ui cloudcli -p 8080 # Start on custom port
cloudcli start cloudcli status # Show current configuration
# Show configuration and data locations
cloudcli status
# Show help information
cloudcli help
# Show version
cloudcli version
```
**The `cloudcli status` command shows you:**
- Installation directory location
- Database location (where credentials are stored)
- Current configuration (PORT, DATABASE_PATH, etc.)
- Claude projects folder location
- Configuration file location
``` ```
### Run as Background Service (Recommended for Production) ### Run as Background Service (Recommended for Production)
@@ -134,6 +127,9 @@ pm2 start claude-code-ui --name "claude-code-ui"
# Or using the shorter alias # Or using the shorter alias
pm2 start cloudcli --name "claude-code-ui" pm2 start cloudcli --name "claude-code-ui"
# Start on a custom port
pm2 start cloudcli --name "claude-code-ui" -- --port 8080
``` ```

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@siteboon/claude-code-ui", "name": "@siteboon/claude-code-ui",
"version": "1.13.1", "version": "1.13.5",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@siteboon/claude-code-ui", "name": "@siteboon/claude-code-ui",
"version": "1.13.1", "version": "1.13.5",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.1.29", "@anthropic-ai/claude-agent-sdk": "^0.1.29",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@siteboon/claude-code-ui", "name": "@siteboon/claude-code-ui",
"version": "1.13.1", "version": "1.13.5",
"description": "A web-based UI for Claude Code CLI", "description": "A web-based UI for Claude Code CLI",
"type": "module", "type": "module",
"main": "server/index.js", "main": "server/index.js",
@@ -10,6 +10,7 @@
}, },
"files": [ "files": [
"server/", "server/",
"shared/",
"dist/", "dist/",
"README.md" "README.md"
], ],

View File

@@ -14,6 +14,7 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import os from 'os';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { dirname } from 'path'; import { dirname } from 'path';
@@ -115,7 +116,7 @@ function showStatus() {
console.log(` CONTEXT_WINDOW: ${c.dim(process.env.CONTEXT_WINDOW || '160000 (default)')}`); console.log(` CONTEXT_WINDOW: ${c.dim(process.env.CONTEXT_WINDOW || '160000 (default)')}`);
// Claude projects folder // Claude projects folder
const claudeProjectsPath = path.join(process.env.HOME, '.claude', 'projects'); const claudeProjectsPath = path.join(os.homedir(), '.claude', 'projects');
const projectsExists = fs.existsSync(claudeProjectsPath); const projectsExists = fs.existsSync(claudeProjectsPath);
console.log(`\n${c.info('[INFO]')} Claude Projects Folder:`); console.log(`\n${c.info('[INFO]')} Claude Projects Folder:`);
console.log(` ${c.dim(claudeProjectsPath)}`); console.log(` ${c.dim(claudeProjectsPath)}`);
@@ -130,10 +131,10 @@ function showStatus() {
console.log('\n' + c.dim('═'.repeat(60))); console.log('\n' + c.dim('═'.repeat(60)));
console.log(`\n${c.tip('[TIP]')} Hints:`); console.log(`\n${c.tip('[TIP]')} Hints:`);
console.log(` ${c.dim('>')} Set DATABASE_PATH env variable to use a custom database location`); console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --port 8080')} to run on a custom port`);
console.log(` ${c.dim('>')} Create .env file in installation directory for persistent config`); console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --database-path /path/to/db')} for custom database`);
console.log(` ${c.dim('>')} Run "claude-code-ui" or "cloudcli start" to start the server`); console.log(` ${c.dim('>')} Run ${c.bright('cloudcli help')} for all options`);
console.log(` ${c.dim('>')} Access the UI at http://localhost:3001 (or custom PORT)\n`); console.log(` ${c.dim('>')} Access the UI at http://localhost:${process.env.PORT || '3001'}\n`);
} }
// Show help // Show help
@@ -144,8 +145,8 @@ function showHelp() {
╚═══════════════════════════════════════════════════════════════╝ ╚═══════════════════════════════════════════════════════════════╝
Usage: Usage:
claude-code-ui [command] claude-code-ui [command] [options]
cloudcli [command] cloudcli [command] [options]
Commands: Commands:
start Start the Claude Code UI server (default) start Start the Claude Code UI server (default)
@@ -153,10 +154,18 @@ Commands:
help Show this help information help Show this help information
version Show version information version Show version information
Options:
-p, --port <port> Set server port (default: 3001)
--database-path <path> Set custom database location
-h, --help Show this help information
-v, --version Show version information
Examples: Examples:
$ claude-code-ui # Start the server $ cloudcli # Start with defaults
$ cloudcli status # Show configuration $ cloudcli --port 8080 # Start on port 8080
$ cloudcli help # Show help $ cloudcli -p 3000 # Short form for port
$ cloudcli start --port 4000 # Explicit start command
$ cloudcli status # Show configuration
Environment Variables: Environment Variables:
PORT Set server port (default: 3001) PORT Set server port (default: 3001)
@@ -164,11 +173,6 @@ Environment Variables:
CLAUDE_CLI_PATH Set custom Claude CLI path CLAUDE_CLI_PATH Set custom Claude CLI path
CONTEXT_WINDOW Set context window size (default: 160000) CONTEXT_WINDOW Set context window size (default: 160000)
Configuration:
Create a .env file in the installation directory to set
persistent environment variables. Use 'cloudcli status' to
see the installation directory path.
Documentation: Documentation:
${packageJson.homepage || 'https://github.com/siteboon/claudecodeui'} ${packageJson.homepage || 'https://github.com/siteboon/claudecodeui'}
@@ -188,10 +192,45 @@ async function startServer() {
await import('./index.js'); await import('./index.js');
} }
// Parse CLI arguments
function parseArgs(args) {
const parsed = { command: 'start', options: {} };
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--port' || arg === '-p') {
parsed.options.port = args[++i];
} else if (arg.startsWith('--port=')) {
parsed.options.port = arg.split('=')[1];
} else if (arg === '--database-path') {
parsed.options.databasePath = args[++i];
} else if (arg.startsWith('--database-path=')) {
parsed.options.databasePath = arg.split('=')[1];
} else if (arg === '--help' || arg === '-h') {
parsed.command = 'help';
} else if (arg === '--version' || arg === '-v') {
parsed.command = 'version';
} else if (!arg.startsWith('-')) {
parsed.command = arg;
}
}
return parsed;
}
// Main CLI handler // Main CLI handler
async function main() { async function main() {
const args = process.argv.slice(2); const args = process.argv.slice(2);
const command = args[0] || 'start'; const { command, options } = parseArgs(args);
// Apply CLI options to environment variables
if (options.port) {
process.env.PORT = options.port;
}
if (options.databasePath) {
process.env.DATABASE_PATH = options.databasePath;
}
switch (command) { switch (command) {
case 'start': case 'start':

View File

@@ -84,7 +84,7 @@ const connectedClients = new Set();
// Setup file system watcher for Claude projects folder using chokidar // Setup file system watcher for Claude projects folder using chokidar
async function setupProjectsWatcher() { async function setupProjectsWatcher() {
const chokidar = (await import('chokidar')).default; const chokidar = (await import('chokidar')).default;
const claudeProjectsPath = path.join(process.env.HOME, '.claude', 'projects'); const claudeProjectsPath = path.join(os.homedir(), '.claude', 'projects');
if (projectsWatcher) { if (projectsWatcher) {
projectsWatcher.close(); projectsWatcher.close();
@@ -462,7 +462,7 @@ app.post('/api/projects/create', authenticateToken, async (req, res) => {
}); });
// Browse filesystem endpoint for project suggestions - uses existing getFileTree // Browse filesystem endpoint for project suggestions - uses existing getFileTree
app.get('/api/browse-filesystem', authenticateToken, async (req, res) => { app.get('/api/browse-filesystem', authenticateToken, async (req, res) => {
try { try {
const { path: dirPath } = req.query; const { path: dirPath } = req.query;
@@ -510,9 +510,9 @@ app.get('/api/browse-filesystem', authenticateToken, async (req, res) => {
suggestions.push(...directories); suggestions.push(...directories);
} }
res.json({ res.json({
path: targetPath, path: targetPath,
suggestions: suggestions suggestions: suggestions
}); });
} catch (error) { } catch (error) {
@@ -1015,7 +1015,7 @@ function handleShellConnection(ws) {
name: 'xterm-256color', name: 'xterm-256color',
cols: termCols, cols: termCols,
rows: termRows, rows: termRows,
cwd: process.env.HOME || (os.platform() === 'win32' ? process.env.USERPROFILE : '/'), cwd: os.homedir(),
env: { env: {
...process.env, ...process.env,
TERM: 'xterm-256color', TERM: 'xterm-256color',

View File

@@ -204,7 +204,7 @@ function clearProjectDirectoryCache() {
// Load project configuration file // Load project configuration file
async function loadProjectConfig() { async function loadProjectConfig() {
const configPath = path.join(process.env.HOME, '.claude', 'project-config.json'); const configPath = path.join(os.homedir(), '.claude', 'project-config.json');
try { try {
const configData = await fs.readFile(configPath, 'utf8'); const configData = await fs.readFile(configPath, 'utf8');
return JSON.parse(configData); return JSON.parse(configData);
@@ -216,7 +216,7 @@ async function loadProjectConfig() {
// Save project configuration file // Save project configuration file
async function saveProjectConfig(config) { async function saveProjectConfig(config) {
const claudeDir = path.join(process.env.HOME, '.claude'); const claudeDir = path.join(os.homedir(), '.claude');
const configPath = path.join(claudeDir, 'project-config.json'); const configPath = path.join(claudeDir, 'project-config.json');
// Ensure the .claude directory exists // Ensure the .claude directory exists
@@ -276,7 +276,7 @@ async function extractProjectDirectory(projectName) {
return originalPath; return originalPath;
} }
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName); const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
const cwdCounts = new Map(); const cwdCounts = new Map();
let latestTimestamp = 0; let latestTimestamp = 0;
let latestCwd = null; let latestCwd = null;
@@ -380,7 +380,7 @@ async function extractProjectDirectory(projectName) {
} }
async function getProjects() { async function getProjects() {
const claudeDir = path.join(process.env.HOME, '.claude', 'projects'); const claudeDir = path.join(os.homedir(), '.claude', 'projects');
const config = await loadProjectConfig(); const config = await loadProjectConfig();
const projects = []; const projects = [];
const existingProjects = new Set(); const existingProjects = new Set();
@@ -546,7 +546,7 @@ async function getProjects() {
} }
async function getSessions(projectName, limit = 5, offset = 0) { async function getSessions(projectName, limit = 5, offset = 0) {
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName); const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
try { try {
const files = await fs.readdir(projectDir); const files = await fs.readdir(projectDir);
@@ -828,7 +828,7 @@ async function parseJsonlSessions(filePath) {
// Get messages for a specific session with pagination support // Get messages for a specific session with pagination support
async function getSessionMessages(projectName, sessionId, limit = null, offset = 0) { async function getSessionMessages(projectName, sessionId, limit = null, offset = 0) {
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName); const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
try { try {
const files = await fs.readdir(projectDir); const files = await fs.readdir(projectDir);
@@ -917,7 +917,7 @@ async function renameProject(projectName, newDisplayName) {
// Delete a session from a project // Delete a session from a project
async function deleteSession(projectName, sessionId) { async function deleteSession(projectName, sessionId) {
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName); const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
try { try {
const files = await fs.readdir(projectDir); const files = await fs.readdir(projectDir);
@@ -980,7 +980,7 @@ async function isProjectEmpty(projectName) {
// Delete an empty project // Delete an empty project
async function deleteProject(projectName) { async function deleteProject(projectName) {
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName); const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
try { try {
// First check if the project is empty // First check if the project is empty
@@ -1020,7 +1020,7 @@ async function addProjectManually(projectPath, displayName = null) {
// Check if project already exists in config // Check if project already exists in config
const config = await loadProjectConfig(); const config = await loadProjectConfig();
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName); const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
if (config[projectName]) { if (config[projectName]) {
throw new Error(`Project already configured for path: ${absolutePath}`); throw new Error(`Project already configured for path: ${absolutePath}`);