diff --git a/claude-config.js b/claude-config.js new file mode 100644 index 0000000..75a91b1 --- /dev/null +++ b/claude-config.js @@ -0,0 +1,232 @@ +#!/usr/bin/env node + +const fs = require('fs') +const os = require('os') +const path = require('path') +const readline = require('readline') + +async function askQuestion (rl, prompt, defaultValue = '') { + const suffix = defaultValue ? ` [${defaultValue}]` : '' + const answer = await new Promise(resolve => { + rl.question(`${prompt}${suffix}>> `, value => resolve(value)) + }) + + const trimmed = answer.trim() + return trimmed || defaultValue || '' +} + +function readJsonIfExists (filePath) { + if (!fs.existsSync(filePath)) { + return null + } + + try { + const content = fs.readFileSync(filePath, 'utf8') + return JSON.parse(content) + } catch (error) { + // fall through to JS parsing below + } + + try { + const content = fs.readFileSync(filePath, 'utf8') + const normalized = content + .replace(/module\.exports\s*=\s*/, 'return ') + .replace(/export\s+default\s*/, 'return ') + .replace(/export\s+const\s+(\w+)\s*=\s*/, 'const $1 = ') + .replace(/export\s+let\s+(\w+)\s*=\s*/, 'let $1 = ') + .replace(/export\s+function\s+(\w+)\s*\(/, 'function $1(') + + const wrapper = new Function(`return (function(){${normalized}})()`) + return wrapper() + } catch (error) { + return null + } +} + +function writeJson (filePath, value) { + fs.mkdirSync(path.dirname(filePath), { recursive: true }) + fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8') +} + +function resolveValue (source, fallbackPaths) { + for (const candidate of fallbackPaths) { + if (!candidate) { + continue + } + + const parts = candidate.split('.') + let current = source + let found = true + + for (const part of parts) { + if ( + current === null || + current === undefined || + typeof current !== 'object' || + !(part in current) + ) { + found = false + break + } + current = current[part] + } + + if (found && current !== undefined && current !== null && current !== '') { + return current + } + } + + return '' +} + +async function main () { + const settingsFile = path.join(os.homedir(), '.claude', 'settings.json') + const claudeFile = path.join(os.homedir(), '.claude.json') + + const existingSettings = readJsonIfExists(settingsFile) || {} + const existingEnv = existingSettings.env || {} + + const existingAuthToken = existingEnv.ANTHROPIC_AUTH_TOKEN || '' + const existingBaseUrl = existingEnv.ANTHROPIC_BASE_URL || '' + const existingModel = existingEnv.ANTHROPIC_MODEL || 'glm-5.2' + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }) + + console.log( + 'Please \n - enter a [file path] to load secret values from a file, \n - or [leave blank] to enter values directly.' + ) + + const filePath = (await askQuestion(rl, '', '')).trim() + + let authToken = existingAuthToken + let baseUrl = existingBaseUrl + let model = existingModel + + if (filePath) { + const secret = readJsonIfExists(filePath) + if (!secret || typeof secret !== 'object' || Array.isArray(secret)) { + rl.close() + console.error( + 'The selected file did not contain a JSON or JavaScript object.' + ) + process.exit(1) + } + + const keys = Object.keys(secret) + let selectedSecret = secret + + if (keys.length > 0) { + console.log('Available keys:') + keys.forEach((key, index) => console.log(`${index + 1}. ${key}`)) + const chosenKey = ( + await askQuestion(rl, 'Choose a configuration from the file', '') + ).trim() + + if (chosenKey) { + const numericChoice = Number(chosenKey) + if ( + !Number.isNaN(numericChoice) && + numericChoice >= 1 && + numericChoice <= keys.length + ) { + selectedSecret = secret[keys[numericChoice - 1]] + } else { + selectedSecret = secret[chosenKey] + } + } + } + + if ( + !selectedSecret || + typeof selectedSecret !== 'object' || + Array.isArray(selectedSecret) + ) { + rl.close() + console.error('The selected key did not contain an object.') + process.exit(1) + } + + authToken = + resolveValue(selectedSecret, [ + 'apikey', + 'apiKey', + 'authToken', + 'token' + ]) || existingAuthToken + baseUrl = + resolveValue(selectedSecret, [ + 'url.anthropic', + 'url.anthropic.baseUrl', + 'baseUrl', + 'url' + ]) || existingBaseUrl + model = resolveValue(selectedSecret, ['model']) || existingModel + + if (!authToken) { + console.log('Auth token was not found in the selected file.') + authToken = await askQuestion( + rl, + 'ANTHROPIC_AUTH_TOKEN', + existingAuthToken + ) + } + + if (!baseUrl) { + console.log('Base URL was not found in the selected file.') + baseUrl = await askQuestion(rl, 'ANTHROPIC_BASE_URL', existingBaseUrl) + } + + if (!model) { + console.log('Model was not found in the selected file.') + model = await askQuestion(rl, 'ANTHROPIC_MODEL', existingModel) + } + + console.log('Loaded values from the selected file or original:') + console.log(` Auth Token: ${authToken}`) + console.log(` Base URL: ${baseUrl}`) + console.log(` Model: ${model}`) + } else { + console.log('\nPlease provide your Anthropic configuration.') + authToken = await askQuestion(rl, 'ANTHROPIC_AUTH_TOKEN', existingAuthToken) + baseUrl = await askQuestion(rl, 'ANTHROPIC_BASE_URL', existingBaseUrl) + model = await askQuestion(rl, 'ANTHROPIC_MODEL', existingModel) + } + + rl.close() + + if (!authToken || !baseUrl || !model) { + console.error( + 'Missing required values. Please re-run the script and provide all fields.' + ) + process.exit(1) + } + + const nextSettings = { + ...existingSettings, + env: { + ...existingSettings.env, + ANTHROPIC_AUTH_TOKEN: authToken, + ANTHROPIC_BASE_URL: baseUrl, + ANTHROPIC_MODEL: model + } + } + + writeJson(settingsFile, nextSettings) + + const nextClaudeConfig = { + hasCompletedOnboarding: true + } + + writeJson(claudeFile, nextClaudeConfig) + + console.log(`\nUpdated ${settingsFile}`) + console.log(`Updated ${claudeFile}`) +} + +main().catch(error => { + console.error(error.message) + process.exit(1) +}) diff --git a/claude-config.sh b/claude-config.sh new file mode 100644 index 0000000..4c56f5b --- /dev/null +++ b/claude-config.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Update ~/.claude/settings.json with Claude environment settings +settings_file="$HOME/.claude/settings.json" +mkdir -p "$(dirname "$settings_file")" + +existing_auth_token="" +existing_base_url="" +existing_model="glm-5.2" + +if [ -f "$settings_file" ]; then + existing_auth_token="$(jq -r '.env.ANTHROPIC_AUTH_TOKEN // empty' "$settings_file" 2>/dev/null)" + existing_base_url="$(jq -r '.env.ANTHROPIC_BASE_URL // empty' "$settings_file" 2>/dev/null)" + existing_model="$(jq -r '.env.ANTHROPIC_MODEL // "glm-5.2"' "$settings_file" 2>/dev/null)" +fi + +echo "Please provide your Anthropic configuration." +read -s -r -p "Anthropic auth token${existing_auth_token:+ [current value hidden]}: " auth_token +echo +read -r -p "Anthropic base URL${existing_base_url:+ [current: $existing_base_url]}: " base_url +read -r -p "Anthropic model${existing_model:+ [current: $existing_model]}: " model + +if [ -z "$auth_token" ]; then + auth_token="$existing_auth_token" +fi +if [ -z "$base_url" ]; then + base_url="$existing_base_url" +fi +if [ -z "$model" ]; then + model="$existing_model" +fi + +if [ -z "$auth_token" ] || [ -z "$base_url" ] || [ -z "$model" ]; then + echo "Missing required values. Please re-run the script and provide all fields." >&2 + exit 1 +fi + +if [ -f "$settings_file" ]; then + jq --arg auth_token "$auth_token" \ + --arg base_url "$base_url" \ + --arg model "$model" \ + '.env.ANTHROPIC_AUTH_TOKEN = $auth_token | + .env.ANTHROPIC_BASE_URL = $base_url | + .env.ANTHROPIC_MODEL = $model' "$settings_file" > "$settings_file.tmp" && mv "$settings_file.tmp" "$settings_file" +else + cat > "$settings_file" < "$HOME/.claude.json.tmp" && mv "$HOME/.claude.json.tmp" "$HOME/.claude.json" +else + echo '{"hasCompletedOnboarding": true}' > "$HOME/.claude.json" +fi diff --git a/claude-install.sh b/claude-install.sh new file mode 100644 index 0000000..cc3f118 --- /dev/null +++ b/claude-install.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# Install Claude CLI +curl -fsSL https://claude.ai/install.sh | bash diff --git a/md5.js b/md5.js new file mode 100644 index 0000000..8d2a610 --- /dev/null +++ b/md5.js @@ -0,0 +1,12 @@ +// convert plain text password to 32bit MD5 encrypted hexadecimal to be used in FutuOpenD.xml +// usage: node md5.js "your_password_here" + +const crypto = require('crypto') + +function md5Hex32 (str) { + return crypto.createHash('md5').update(String(str), 'utf8').digest('hex') // 32 hex chars +} + +// Example: +const password = process.argv[2] || 'your_password_here' +console.log(md5Hex32(password)) diff --git a/nixhome/.bashrc b/nixhome/.bashrc index 1381b9a..ec2c0f6 100644 --- a/nixhome/.bashrc +++ b/nixhome/.bashrc @@ -144,9 +144,9 @@ elif [ -f /etc/ubuntu_version ]; then MYOSVERSION=Ubt`cat /etc/ubuntu_version 2>/dev/null` fi if [ "$color_prompt" = yes ]; then - PS1='<\[\033[$PSTYLE;${PTYPE}2m\]\t\[\033[00m\] \[\033[$PSTYLE;${PTYPE}5m\]\u\[\033[00m\] @\[\033[$PSTYLE;${PTYPE}1m\]\h\[\033[00m\] =\[\033[$PSTYLE;${PTYPE}6m\]$MYIPPUB\[\033[00m\] #\[\033[$PSTYLE;${PTYPE}5m\]$(uname -m),$(uname),$MYOSVERSION\[\033[00m\] \[\033[$PSTYLE;${PTYPE}2m\]\w/\[\033[00m\]>\n[\[\033[05;34m\]\W/\[\033[00m\]] ' + PS1='<\[\033[$PSTYLE;${PTYPE}2m\]\t\[\033[00m\] \[\033[$PSTYLE;${PTYPE}5m\]\u\[\033[00m\] @\[\033[$PSTYLE;${PTYPE}1m\]\h\[\033[00m\] =\[\033[$PSTYLE;${PTYPE}6m\]$MYIPPUB\[\033[00m\] #\[\033[$PSTYLE;${PTYPE}5m\]$(uname -m),$(uname),$MYOSVERSION\[\033[00m\] \[\033[$PSTYLE;${PTYPE}2m\]\w/\[\033[00m\]>\n\[\033[05;44m\]\W/\[\033[00m\] ' else - PS1='<\t \u @\h =$MYIPPUB #$(uname -m),$(uname),$MYOSVERSION \w/>\n[\W/] ' # \w shows absolute path, \W shows current folder. + PS1='<\t \u @\h =$MYIPPUB #$(uname -m),$(uname),$MYOSVERSION \w/>\n\W/ ' # \w shows absolute path, \W shows current folder. fi unset color_prompt force_color_prompt