feat: add Cursor CLI installation attempts documentation and enhance Docker setup

- Introduced a new markdown file summarizing various attempts to install the Cursor CLI in Docker, detailing approaches, results, and key learnings.
- Updated Dockerfile to ensure proper installation of Cursor CLI for the non-root user, including necessary PATH adjustments for interactive shells.
- Enhanced entrypoint script to manage OAuth tokens for both Claude and Cursor CLIs, ensuring correct permissions and directory setups.
- Added scripts for extracting OAuth tokens from macOS Keychain and Linux JSON files for seamless integration with Docker.
- Updated docker-compose files to support persistent storage for CLI configurations and authentication tokens.

These changes improve the development workflow and provide clear guidance on CLI installation and authentication processes.
This commit is contained in:
webdevcody
2026-01-05 18:13:14 -05:00
parent 5d675561ba
commit af394183e6
11 changed files with 536 additions and 49 deletions

104
dev.mjs
View File

@@ -12,6 +12,8 @@
import path from 'path';
import { fileURLToPath } from 'url';
import { createRequire } from 'module';
import { statSync } from 'fs';
import { execSync } from 'child_process';
import {
createRestrictedFs,
@@ -45,6 +47,89 @@ const processes = {
docker: null,
};
/**
* Check if Docker images need to be rebuilt based on Dockerfile or package.json changes
*/
function shouldRebuildDockerImages() {
try {
const dockerfilePath = path.join(__dirname, 'Dockerfile');
const packageJsonPath = path.join(__dirname, 'package.json');
// Get modification times of source files
const dockerfileMtime = statSync(dockerfilePath).mtimeMs;
const packageJsonMtime = statSync(packageJsonPath).mtimeMs;
const latestSourceMtime = Math.max(dockerfileMtime, packageJsonMtime);
// Get image names from docker-compose config
let serverImageName, uiImageName;
try {
const composeConfig = execSync('docker compose config --format json', {
encoding: 'utf-8',
cwd: __dirname,
});
const config = JSON.parse(composeConfig);
// Docker Compose generates image names as <project>_<service>
// Get project name from config or default to directory name
const projectName =
config.name ||
path
.basename(__dirname)
.toLowerCase()
.replace(/[^a-z0-9]/g, '');
serverImageName = `${projectName}_server`;
uiImageName = `${projectName}_ui`;
} catch (error) {
// Fallback to default naming convention
const projectName = path
.basename(__dirname)
.toLowerCase()
.replace(/[^a-z0-9]/g, '');
serverImageName = `${projectName}_server`;
uiImageName = `${projectName}_ui`;
}
// Check if images exist and get their creation times
let needsRebuild = false;
try {
// Check server image
const serverImageInfo = execSync(
`docker image inspect ${serverImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
{ encoding: 'utf-8', cwd: __dirname }
).trim();
// Check UI image
const uiImageInfo = execSync(
`docker image inspect ${uiImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
{ encoding: 'utf-8', cwd: __dirname }
).trim();
// If either image doesn't exist, we need to rebuild
if (!serverImageInfo || !uiImageInfo) {
return true;
}
// Parse image creation times (ISO 8601 format)
const serverCreated = new Date(serverImageInfo).getTime();
const uiCreated = new Date(uiImageInfo).getTime();
const oldestImageTime = Math.min(serverCreated, uiCreated);
// If source files are newer than images, rebuild
needsRebuild = latestSourceMtime > oldestImageTime;
} catch (error) {
// If images don't exist or inspect fails, rebuild
needsRebuild = true;
}
return needsRebuild;
} catch (error) {
// If we can't check, err on the side of rebuilding
log('Could not check Docker image status, will rebuild to be safe', 'yellow');
return true;
}
}
/**
* Install Playwright browsers (dev-only dependency)
*/
@@ -172,9 +257,16 @@ async function main() {
} else if (choice === '3') {
console.log('');
log('Launching Docker Container (Isolated Mode)...', 'blue');
log('Starting Docker containers...', 'yellow');
log('Note: Containers will only rebuild if images are missing.', 'yellow');
log('To force a rebuild, run: docker compose up --build', 'yellow');
// Check if Dockerfile or package.json changed and rebuild if needed
const needsRebuild = shouldRebuildDockerImages();
const buildFlag = needsRebuild ? ['--build'] : [];
if (needsRebuild) {
log('Dockerfile or package.json changed - rebuilding images...', 'yellow');
} else {
log('Starting Docker containers...', 'yellow');
}
console.log('');
// Check if ANTHROPIC_API_KEY is set
@@ -185,9 +277,9 @@ async function main() {
console.log('');
}
// Start containers with docker-compose (without --build to preserve volumes)
// Images will only be built if they don't exist
processes.docker = crossSpawn('docker', ['compose', 'up'], {
// Start containers with docker-compose
// Will rebuild if Dockerfile or package.json changed
processes.docker = crossSpawn('docker', ['compose', 'up', ...buildFlag], {
stdio: 'inherit',
cwd: __dirname,
env: {