codecli-hostapi
Advanced tools
+288
-235
@@ -9,4 +9,13 @@ #!/usr/bin/env node | ||
| const PROXY_PORT = 18080; | ||
| const PROXY_BASE_URL = `http://127.0.0.1:${PROXY_PORT}`; | ||
| const PROXY_HOST = '127.0.0.1'; | ||
| const PROXY_BASE_URL = `http://${PROXY_HOST}:${PROXY_PORT}`; | ||
| // XDG-compliant paths | ||
| const HOME = process.env.HOME || process.env.USERPROFILE; | ||
| const DATA_DIR = path.join(HOME, '.local', 'share', 'codecli-hostapi'); | ||
| const CONFIG_DIR = path.join(HOME, '.config', 'codecli-hostapi'); | ||
| const BACKUP_DIR = path.join(DATA_DIR, 'backups'); | ||
| const PROXY_SCRIPT_PATH = path.join(DATA_DIR, 'proxy.py'); | ||
| const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json'); | ||
| const PROXY_SCRIPT = `#!/usr/bin/env python3 | ||
@@ -16,3 +25,3 @@ import json, subprocess, time, sys, uuid | ||
| HOST = "127.0.0.1" | ||
| HOST = "${PROXY_HOST}" | ||
| PORT = ${PROXY_PORT} | ||
@@ -66,3 +75,3 @@ | ||
| self.end_headers() | ||
| self.wfile.write(json.dumps({"ok": True}).encode()) | ||
| self.wfile.write(json.dumps({"ok": True, "endpoint": f"http://{HOST}:{PORT}"}).encode()) | ||
| return | ||
@@ -176,2 +185,3 @@ self.send_response(404) | ||
| log(f"anthropic-cli-proxy listening on http://{HOST}:{PORT}") | ||
| log(f"API Endpoint: http://{HOST}:{PORT}/v1/messages") | ||
| httpd.serve_forever() | ||
@@ -185,2 +195,8 @@ | ||
| function ensureDir(dir) { | ||
| if (!fs.existsSync(dir)) { | ||
| fs.mkdirSync(dir, { recursive: true }); | ||
| } | ||
| } | ||
| function run(cmd, opts = {}) { | ||
@@ -196,78 +212,78 @@ if (!opts.silent) console.log(`> ${cmd}`); | ||
| function findClawdbotConfig() { | ||
| const home = process.env.HOME || process.env.USERPROFILE; | ||
| const candidates = [ | ||
| path.join(home, 'clawd', 'clawdbot.json'), | ||
| path.join(home, '.config', 'clawdbot', 'clawdbot.json'), | ||
| path.join(home, 'clawdbot.json'), | ||
| ]; | ||
| for (const p of candidates) { | ||
| if (fs.existsSync(p)) return p; | ||
| async function prompt(question) { | ||
| const rl = readline.createInterface({ | ||
| input: process.stdin, | ||
| output: process.stdout | ||
| }); | ||
| return new Promise(resolve => { | ||
| rl.question(question, answer => { | ||
| rl.close(); | ||
| resolve(answer.trim()); | ||
| }); | ||
| }); | ||
| } | ||
| function loadConfig() { | ||
| if (fs.existsSync(CONFIG_PATH)) { | ||
| return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); | ||
| } | ||
| return null; | ||
| return { installed: false, apps: {} }; | ||
| } | ||
| function getBackupDir(configPath) { | ||
| return path.join(path.dirname(configPath), 'backups'); | ||
| function saveConfig(config) { | ||
| ensureDir(CONFIG_DIR); | ||
| fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2)); | ||
| } | ||
| function ensureBackupDir(configPath) { | ||
| const backupDir = getBackupDir(configPath); | ||
| if (!fs.existsSync(backupDir)) { | ||
| fs.mkdirSync(backupDir, { recursive: true }); | ||
| function isProxyRunning() { | ||
| try { | ||
| const result = run(`lsof -i :${PROXY_PORT} -t`, { silent: true, ignoreError: true }); | ||
| return result.trim().length > 0; | ||
| } catch { | ||
| return false; | ||
| } | ||
| return backupDir; | ||
| } | ||
| function hasHostProxy(config) { | ||
| const baseUrl = config?.models?.providers?.anthropic?.baseUrl || ''; | ||
| return baseUrl.includes('127.0.0.1') || baseUrl.includes('localhost'); | ||
| function getProxyPid() { | ||
| try { | ||
| return run(`lsof -i :${PROXY_PORT} -t`, { silent: true }).trim().split('\n')[0]; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| function isOurProxy(config) { | ||
| const baseUrl = config?.models?.providers?.anthropic?.baseUrl || ''; | ||
| return baseUrl === PROXY_BASE_URL; | ||
| } | ||
| // ============ App Configurations ============ | ||
| function listBackups(configPath) { | ||
| const backupDir = getBackupDir(configPath); | ||
| if (!fs.existsSync(backupDir)) return []; | ||
| return fs.readdirSync(backupDir) | ||
| .filter(f => f.endsWith('.json')) | ||
| .map(f => { | ||
| const fullPath = path.join(backupDir, f); | ||
| const stat = fs.statSync(fullPath); | ||
| return { | ||
| name: f.replace('.json', ''), | ||
| path: fullPath, | ||
| time: stat.mtime | ||
| }; | ||
| }) | ||
| .sort((a, b) => b.time - a.time); | ||
| function findClawdbotConfig() { | ||
| const candidates = [ | ||
| path.join(HOME, 'clawd', 'clawdbot.json'), | ||
| path.join(HOME, '.config', 'clawdbot', 'clawdbot.json'), | ||
| path.join(HOME, 'clawdbot.json'), | ||
| ]; | ||
| for (const p of candidates) { | ||
| if (fs.existsSync(p)) return p; | ||
| } | ||
| return null; | ||
| } | ||
| function createBackup(configPath, backupName) { | ||
| const backupDir = ensureBackupDir(configPath); | ||
| const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); | ||
| const fileName = `${backupName}_${timestamp}.json`; | ||
| const backupPath = path.join(backupDir, fileName); | ||
| function configureClawdbot(configPath) { | ||
| const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); | ||
| // Backup first | ||
| const backupPath = path.join(BACKUP_DIR, `clawdbot_${Date.now()}.json`); | ||
| ensureDir(BACKUP_DIR); | ||
| fs.copyFileSync(configPath, backupPath); | ||
| return backupPath; | ||
| } | ||
| console.log(` š¾ Backup: ${path.basename(backupPath)}`); | ||
| function restoreBackup(configPath, backupPath) { | ||
| fs.copyFileSync(backupPath, configPath); | ||
| } | ||
| // Update config | ||
| if (!config.models) config.models = {}; | ||
| if (!config.models.providers) config.models.providers = {}; | ||
| config.models.providers.anthropic = { | ||
| baseUrl: PROXY_BASE_URL, | ||
| models: [] | ||
| }; | ||
| fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); | ||
| console.log(` ā Updated: ${configPath}`); | ||
| async function prompt(question) { | ||
| const rl = readline.createInterface({ | ||
| input: process.stdin, | ||
| output: process.stdout | ||
| }); | ||
| return new Promise(resolve => { | ||
| rl.question(question, answer => { | ||
| rl.close(); | ||
| resolve(answer.trim()); | ||
| }); | ||
| }); | ||
| return backupPath; | ||
| } | ||
@@ -277,4 +293,4 @@ | ||
| async function doActivate(configPath) { | ||
| console.log('\nš§ Activating codecli-hostapi...\n'); | ||
| async function doInstall() { | ||
| console.log('\nš§ Installing codecli-hostapi...\n'); | ||
@@ -299,48 +315,17 @@ // Check prerequisites | ||
| // Read current config | ||
| const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); | ||
| // Auto backup before activation | ||
| if (!isOurProxy(config)) { | ||
| const backupName = hasHostProxy(config) ? 'hostbak' : 'initbak'; | ||
| const backupPath = createBackup(configPath, backupName); | ||
| console.log(`\nš¾ Auto backup created: ${path.basename(backupPath)}`); | ||
| } | ||
| // Update config | ||
| console.log('\nš Configuring clawdbot...'); | ||
| if (!config.models) config.models = {}; | ||
| if (!config.models.providers) config.models.providers = {}; | ||
| config.models.providers.anthropic = { | ||
| baseUrl: PROXY_BASE_URL, | ||
| models: [] | ||
| }; | ||
| fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); | ||
| console.log(' ā Updated clawdbot.json with proxy settings'); | ||
| // Install proxy script | ||
| console.log('\nš¦ Installing proxy...'); | ||
| const proxyPath = '/usr/local/bin/anthropic-cli-proxy.py'; | ||
| const tempPath = '/tmp/anthropic-cli-proxy.py'; | ||
| fs.writeFileSync(tempPath, PROXY_SCRIPT); | ||
| console.log('\nš¦ Installing proxy script...'); | ||
| ensureDir(DATA_DIR); | ||
| fs.writeFileSync(PROXY_SCRIPT_PATH, PROXY_SCRIPT); | ||
| fs.chmodSync(PROXY_SCRIPT_PATH, '755'); | ||
| console.log(` ā Installed: ${PROXY_SCRIPT_PATH}`); | ||
| let installedProxyPath = tempPath; | ||
| try { | ||
| run(`sudo cp ${tempPath} ${proxyPath}`, { silent: true }); | ||
| run(`sudo chmod +x ${proxyPath}`, { silent: true }); | ||
| installedProxyPath = proxyPath; | ||
| console.log(` ā Proxy installed to ${proxyPath}`); | ||
| } catch { | ||
| console.log(` ā Could not install to ${proxyPath}, using ${tempPath}`); | ||
| } | ||
| // Setup systemd services (Linux only) | ||
| // Setup systemd service (Linux only) | ||
| if (process.platform === 'linux') { | ||
| console.log('\nāļø Setting up systemd services...'); | ||
| console.log('\nāļø Setting up systemd service...'); | ||
| const user = process.env.USER || 'ubuntu'; | ||
| const home = process.env.HOME; | ||
| const proxyService = `[Unit] | ||
| Description=Anthropic CLI Proxy for Clawdbot | ||
| Description=Anthropic CLI Proxy (codecli-hostapi) | ||
| After=network.target | ||
@@ -351,7 +336,7 @@ | ||
| User=${user} | ||
| ExecStart=/usr/bin/python3 ${installedProxyPath} | ||
| ExecStart=/usr/bin/python3 ${PROXY_SCRIPT_PATH} | ||
| Restart=always | ||
| RestartSec=5 | ||
| StandardOutput=append:/var/log/anthropic-proxy.log | ||
| StandardError=append:/var/log/anthropic-proxy.log | ||
| StandardOutput=append:/var/log/codecli-hostapi.log | ||
| StandardError=append:/var/log/codecli-hostapi.log | ||
@@ -362,51 +347,85 @@ [Install] | ||
| let clawdbotPath = ''; | ||
| try { | ||
| clawdbotPath = run('which clawdbot', { silent: true }).trim(); | ||
| } catch { | ||
| clawdbotPath = '/usr/local/bin/clawdbot'; | ||
| fs.writeFileSync('/tmp/codecli-hostapi.service', proxyService); | ||
| run('sudo cp /tmp/codecli-hostapi.service /etc/systemd/system/', { ignoreError: true, silent: true }); | ||
| run('sudo systemctl daemon-reload', { ignoreError: true, silent: true }); | ||
| run('sudo systemctl enable codecli-hostapi.service', { ignoreError: true, silent: true }); | ||
| run('sudo systemctl restart codecli-hostapi.service', { ignoreError: true, silent: true }); | ||
| console.log(' ā Systemd service installed and started'); | ||
| } else { | ||
| // Non-Linux: start in background | ||
| console.log('\nš Starting proxy...'); | ||
| spawn('python3', [PROXY_SCRIPT_PATH], { | ||
| detached: true, | ||
| stdio: 'ignore' | ||
| }).unref(); | ||
| console.log(' ā Proxy started in background'); | ||
| } | ||
| // Save config | ||
| const config = loadConfig(); | ||
| config.installed = true; | ||
| config.installedAt = new Date().toISOString(); | ||
| saveConfig(config); | ||
| console.log('\nā Installation complete!'); | ||
| console.log(`\nš” API Endpoint: ${PROXY_BASE_URL}/v1/messages`); | ||
| console.log(' Use this as your Anthropic API base URL'); | ||
| // Check for known apps | ||
| const clawdbotConfig = findClawdbotConfig(); | ||
| if (clawdbotConfig) { | ||
| console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'); | ||
| console.log('š Detected: clawdbot'); | ||
| const configure = await prompt(' Configure clawdbot to use this proxy? (y/n): '); | ||
| if (configure.toLowerCase() === 'y') { | ||
| configureClawdbot(clawdbotConfig); | ||
| // Restart clawdbot service if exists | ||
| if (process.platform === 'linux') { | ||
| run('sudo systemctl restart clawdbot.service 2>/dev/null || true', { ignoreError: true, silent: true }); | ||
| console.log(' ā Clawdbot service restarted'); | ||
| } | ||
| } | ||
| } | ||
| const clawdbotService = `[Unit] | ||
| Description=Clawdbot Gateway | ||
| After=network.target anthropic-proxy.service | ||
| Wants=anthropic-proxy.service | ||
| return true; | ||
| } | ||
| [Service] | ||
| Type=simple | ||
| User=${user} | ||
| Environment=HOME=${home} | ||
| WorkingDirectory=${home} | ||
| ExecStart=${clawdbotPath} gateway | ||
| Restart=always | ||
| RestartSec=10 | ||
| StandardOutput=append:/var/log/clawdbot.log | ||
| StandardError=append:/var/log/clawdbot.log | ||
| async function doStop() { | ||
| console.log('\nš Stopping proxy...\n'); | ||
| [Install] | ||
| WantedBy=multi-user.target | ||
| `; | ||
| if (process.platform === 'linux') { | ||
| run('sudo systemctl stop codecli-hostapi.service', { ignoreError: true, silent: true }); | ||
| console.log(' ā Systemd service stopped'); | ||
| } else { | ||
| const pid = getProxyPid(); | ||
| if (pid) { | ||
| run(`kill ${pid}`, { ignoreError: true, silent: true }); | ||
| console.log(` ā Killed process ${pid}`); | ||
| } else { | ||
| console.log(' ā Proxy not running'); | ||
| } | ||
| } | ||
| fs.writeFileSync('/tmp/anthropic-proxy.service', proxyService); | ||
| fs.writeFileSync('/tmp/clawdbot.service', clawdbotService); | ||
| return true; | ||
| } | ||
| run('sudo cp /tmp/anthropic-proxy.service /etc/systemd/system/', { ignoreError: true, silent: true }); | ||
| run('sudo cp /tmp/clawdbot.service /etc/systemd/system/', { ignoreError: true, silent: true }); | ||
| run('sudo systemctl daemon-reload', { ignoreError: true, silent: true }); | ||
| run('sudo systemctl enable anthropic-proxy.service', { ignoreError: true, silent: true }); | ||
| run('sudo systemctl enable clawdbot.service', { ignoreError: true, silent: true }); | ||
| async function doStart() { | ||
| console.log('\nš Starting proxy...\n'); | ||
| console.log(' ā Systemd services created and enabled'); | ||
| if (!fs.existsSync(PROXY_SCRIPT_PATH)) { | ||
| console.log(' ā Proxy not installed. Run "Install" first.'); | ||
| return false; | ||
| } | ||
| // Start services | ||
| console.log('\nš Starting services...'); | ||
| run('sudo systemctl restart anthropic-proxy.service', { ignoreError: true, silent: true }); | ||
| run('sleep 2', { silent: true }); | ||
| run('sudo systemctl restart clawdbot.service', { ignoreError: true, silent: true }); | ||
| if (isProxyRunning()) { | ||
| console.log(' ā Proxy already running'); | ||
| return true; | ||
| } | ||
| console.log(' ā Services started'); | ||
| if (process.platform === 'linux') { | ||
| run('sudo systemctl start codecli-hostapi.service', { ignoreError: true, silent: true }); | ||
| console.log(' ā Systemd service started'); | ||
| } else { | ||
| // Non-Linux: just start proxy in background | ||
| console.log('\nš Starting proxy...'); | ||
| spawn('python3', [installedProxyPath], { | ||
| spawn('python3', [PROXY_SCRIPT_PATH], { | ||
| detached: true, | ||
@@ -416,14 +435,39 @@ stdio: 'ignore' | ||
| console.log(' ā Proxy started in background'); | ||
| console.log('\n To start clawdbot, run: clawdbot gateway'); | ||
| } | ||
| console.log('\nā Activation complete!'); | ||
| console.log(`\nš” API Endpoint: ${PROXY_BASE_URL}/v1/messages`); | ||
| return true; | ||
| } | ||
| async function doRestore(configPath) { | ||
| async function doBackup() { | ||
| console.log('\nš¾ Creating backup...\n'); | ||
| ensureDir(BACKUP_DIR); | ||
| // Backup our own config | ||
| if (fs.existsSync(CONFIG_PATH)) { | ||
| const backupPath = path.join(BACKUP_DIR, `config_${Date.now()}.json`); | ||
| fs.copyFileSync(CONFIG_PATH, backupPath); | ||
| console.log(` ā Config: ${path.basename(backupPath)}`); | ||
| } | ||
| // Backup clawdbot if exists | ||
| const clawdbotConfig = findClawdbotConfig(); | ||
| if (clawdbotConfig) { | ||
| const backupPath = path.join(BACKUP_DIR, `clawdbot_${Date.now()}.json`); | ||
| fs.copyFileSync(clawdbotConfig, backupPath); | ||
| console.log(` ā Clawdbot: ${path.basename(backupPath)}`); | ||
| } | ||
| console.log(`\nš Backups stored in: ${BACKUP_DIR}`); | ||
| return true; | ||
| } | ||
| async function doRestore() { | ||
| console.log('\nš Available backups:\n'); | ||
| const backups = listBackups(configPath); | ||
| if (backups.length === 0) { | ||
| ensureDir(BACKUP_DIR); | ||
| const files = fs.readdirSync(BACKUP_DIR).filter(f => f.endsWith('.json')); | ||
| if (files.length === 0) { | ||
| console.log(' No backups found.'); | ||
@@ -433,13 +477,35 @@ return false; | ||
| backups.forEach((b, i) => { | ||
| const timeStr = b.time.toLocaleString(); | ||
| console.log(` ${i + 1}. ${b.name} (${timeStr})`); | ||
| }); | ||
| // Group by type | ||
| const clawdbotBackups = files.filter(f => f.startsWith('clawdbot_')); | ||
| const configBackups = files.filter(f => f.startsWith('config_')); | ||
| let options = []; | ||
| let idx = 1; | ||
| if (clawdbotBackups.length > 0) { | ||
| console.log(' Clawdbot backups:'); | ||
| clawdbotBackups.slice(0, 5).forEach(f => { | ||
| const stat = fs.statSync(path.join(BACKUP_DIR, f)); | ||
| console.log(` ${idx}. ${f} (${stat.mtime.toLocaleString()})`); | ||
| options.push({ type: 'clawdbot', file: f }); | ||
| idx++; | ||
| }); | ||
| } | ||
| if (configBackups.length > 0) { | ||
| console.log(' Config backups:'); | ||
| configBackups.slice(0, 5).forEach(f => { | ||
| const stat = fs.statSync(path.join(BACKUP_DIR, f)); | ||
| console.log(` ${idx}. ${f} (${stat.mtime.toLocaleString()})`); | ||
| options.push({ type: 'config', file: f }); | ||
| idx++; | ||
| }); | ||
| } | ||
| console.log(` 0. Cancel`); | ||
| const choice = await prompt('\nSelect backup to restore: '); | ||
| const idx = parseInt(choice, 10); | ||
| const choiceIdx = parseInt(choice, 10); | ||
| if (idx === 0 || isNaN(idx) || idx < 1 || idx > backups.length) { | ||
| if (choiceIdx === 0 || isNaN(choiceIdx) || choiceIdx < 1 || choiceIdx > options.length) { | ||
| console.log('Cancelled.'); | ||
@@ -449,21 +515,24 @@ return false; | ||
| const selected = backups[idx - 1]; | ||
| const selected = options[choiceIdx - 1]; | ||
| const backupPath = path.join(BACKUP_DIR, selected.file); | ||
| // Backup current config before restore | ||
| const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); | ||
| if (isOurProxy(config)) { | ||
| createBackup(configPath, 'hostbak'); | ||
| console.log(' š¾ Current config backed up as hostbak'); | ||
| } | ||
| if (selected.type === 'clawdbot') { | ||
| const clawdbotConfig = findClawdbotConfig(); | ||
| if (clawdbotConfig) { | ||
| fs.copyFileSync(backupPath, clawdbotConfig); | ||
| console.log(`\nā Restored clawdbot config from: ${selected.file}`); | ||
| restoreBackup(configPath, selected.path); | ||
| console.log(`\nā Restored from: ${selected.name}`); | ||
| // Restart services if on Linux | ||
| if (process.platform === 'linux') { | ||
| const restart = await prompt('\nRestart clawdbot service? (y/n): '); | ||
| if (restart.toLowerCase() === 'y') { | ||
| run('sudo systemctl restart clawdbot.service', { ignoreError: true, silent: true }); | ||
| console.log(' ā Clawdbot restarted'); | ||
| if (process.platform === 'linux') { | ||
| const restart = await prompt('Restart clawdbot service? (y/n): '); | ||
| if (restart.toLowerCase() === 'y') { | ||
| run('sudo systemctl restart clawdbot.service', { ignoreError: true, silent: true }); | ||
| console.log(' ā Clawdbot restarted'); | ||
| } | ||
| } | ||
| } else { | ||
| console.log(' ā Clawdbot config not found on this system'); | ||
| } | ||
| } else if (selected.type === 'config') { | ||
| fs.copyFileSync(backupPath, CONFIG_PATH); | ||
| console.log(`\nā Restored config from: ${selected.file}`); | ||
| } | ||
@@ -474,54 +543,36 @@ | ||
| async function doBackup(configPath) { | ||
| console.log('\nš¾ Manual Backup\n'); | ||
| async function showStatus() { | ||
| console.log('\nš Status\n'); | ||
| const defaultName = 'manual'; | ||
| const name = await prompt(`Backup name (default: ${defaultName}): `) || defaultName; | ||
| const config = loadConfig(); | ||
| const running = isProxyRunning(); | ||
| const backupPath = createBackup(configPath, name); | ||
| console.log(`\nā Backup created: ${path.basename(backupPath)}`); | ||
| return true; | ||
| } | ||
| console.log(` Installed: ${config.installed ? 'ā Yes' : 'ā No'}`); | ||
| console.log(` Running: ${running ? 'ā Yes' : 'ā No'}`); | ||
| console.log(` Endpoint: ${PROXY_BASE_URL}/v1/messages`); | ||
| async function showStatus(configPath) { | ||
| console.log('\nš Current Status\n'); | ||
| const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); | ||
| const baseUrl = config?.models?.providers?.anthropic?.baseUrl || '(not set)'; | ||
| console.log(` Config: ${configPath}`); | ||
| console.log(` Anthropic baseUrl: ${baseUrl}`); | ||
| if (isOurProxy(config)) { | ||
| console.log(` Status: ā codecli-hostapi is ACTIVE`); | ||
| } else if (hasHostProxy(config)) { | ||
| console.log(` Status: ā Another local proxy is configured`); | ||
| } else { | ||
| console.log(` Status: ā Using default/remote API`); | ||
| } | ||
| const backups = listBackups(configPath); | ||
| console.log(`\n Backups: ${backups.length} found`); | ||
| if (backups.length > 0) { | ||
| console.log(' Recent:'); | ||
| backups.slice(0, 3).forEach(b => { | ||
| console.log(` - ${b.name}`); | ||
| }); | ||
| } | ||
| if (process.platform === 'linux') { | ||
| console.log('\n Services:'); | ||
| try { | ||
| const proxyStatus = run('systemctl is-active anthropic-proxy.service', { silent: true }).trim(); | ||
| console.log(` - anthropic-proxy: ${proxyStatus}`); | ||
| const status = run('systemctl is-active codecli-hostapi.service', { silent: true }).trim(); | ||
| console.log(` Service: ${status}`); | ||
| } catch { | ||
| console.log(` - anthropic-proxy: not installed`); | ||
| console.log(` Service: not installed`); | ||
| } | ||
| try { | ||
| const clawdbotStatus = run('systemctl is-active clawdbot.service', { silent: true }).trim(); | ||
| console.log(` - clawdbot: ${clawdbotStatus}`); | ||
| } catch { | ||
| console.log(` - clawdbot: not installed`); | ||
| } | ||
| } | ||
| // Check clawdbot | ||
| const clawdbotConfig = findClawdbotConfig(); | ||
| if (clawdbotConfig) { | ||
| const cfg = JSON.parse(fs.readFileSync(clawdbotConfig, 'utf8')); | ||
| const baseUrl = cfg?.models?.providers?.anthropic?.baseUrl || '(default)'; | ||
| const usingProxy = baseUrl === PROXY_BASE_URL; | ||
| console.log(`\n Clawdbot: ${clawdbotConfig}`); | ||
| console.log(` BaseURL: ${baseUrl}`); | ||
| console.log(` Using proxy: ${usingProxy ? 'ā Yes' : 'ā No'}`); | ||
| } | ||
| // List backups | ||
| ensureDir(BACKUP_DIR); | ||
| const backups = fs.readdirSync(BACKUP_DIR).filter(f => f.endsWith('.json')); | ||
| console.log(`\n Backups: ${backups.length} files in ${BACKUP_DIR}`); | ||
| } | ||
@@ -533,19 +584,15 @@ | ||
| console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'); | ||
| console.log('ā codecli-hostapi - Setup Tool ā'); | ||
| console.log('ā Proxy Anthropic API via Claude CLI ā'); | ||
| console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'); | ||
| console.log('ā codecli-hostapi v1.0.0 ā'); | ||
| console.log('ā Turn Claude CLI into a local API ā'); | ||
| console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'); | ||
| const configPath = findClawdbotConfig(); | ||
| if (!configPath) { | ||
| console.error('ā clawdbot.json not found. Please run "clawdbot init" first.'); | ||
| process.exit(1); | ||
| } | ||
| await showStatus(); | ||
| await showStatus(configPath); | ||
| console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'); | ||
| console.log(' 1. Activate - Enable codecli-hostapi'); | ||
| console.log(' 2. Restore - Restore from backup'); | ||
| console.log(' 3. Backup - Create manual backup'); | ||
| console.log(' 4. Status - Show current status'); | ||
| console.log(' 1. Install - Install & start proxy'); | ||
| console.log(' 2. Start - Start proxy service'); | ||
| console.log(' 3. Stop - Stop proxy service'); | ||
| console.log(' 4. Backup - Create backup'); | ||
| console.log(' 5. Restore - Restore from backup'); | ||
| console.log(' 6. Status - Show current status'); | ||
| console.log(' 0. Exit'); | ||
@@ -558,13 +605,19 @@ console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'); | ||
| case '1': | ||
| await doActivate(configPath); | ||
| await doInstall(); | ||
| break; | ||
| case '2': | ||
| await doRestore(configPath); | ||
| await doStart(); | ||
| break; | ||
| case '3': | ||
| await doBackup(configPath); | ||
| await doStop(); | ||
| break; | ||
| case '4': | ||
| await showStatus(configPath); | ||
| await doBackup(); | ||
| break; | ||
| case '5': | ||
| await doRestore(); | ||
| break; | ||
| case '6': | ||
| await showStatus(); | ||
| break; | ||
| case '0': | ||
@@ -571,0 +624,0 @@ case '': |
+1
-1
| { | ||
| "name": "codecli-hostapi", | ||
| "version": "1.0.0", | ||
| "version": "1.1.0", | ||
| "description": "One-command setup to proxy Anthropic API requests through Claude Code CLI", | ||
@@ -5,0 +5,0 @@ "bin": { |
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
20553
8.37%520
9.47%7
-12.5%