diff --git a/$null b/$null new file mode 100644 index 000000000..ade355f07 --- /dev/null +++ b/$null @@ -0,0 +1 @@ +ERROR: Invalid pattern is specified in "path:pattern". diff --git a/.github/workflows/auto-close-harvested.yml b/.github/workflows.bak/auto-close-harvested.yml similarity index 100% rename from .github/workflows/auto-close-harvested.yml rename to .github/workflows.bak/auto-close-harvested.yml diff --git a/.github/workflows/auto-tag.yml b/.github/workflows.bak/auto-tag.yml similarity index 100% rename from .github/workflows/auto-tag.yml rename to .github/workflows.bak/auto-tag.yml diff --git a/.github/workflows/ci.yml b/.github/workflows.bak/ci.yml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows.bak/ci.yml diff --git a/.github/workflows/nightly.yml b/.github/workflows.bak/nightly.yml similarity index 100% rename from .github/workflows/nightly.yml rename to .github/workflows.bak/nightly.yml diff --git a/.github/workflows/release.yml b/.github/workflows.bak/release.yml similarity index 100% rename from .github/workflows/release.yml rename to .github/workflows.bak/release.yml diff --git a/.github/workflows/spam-lockdown.yml b/.github/workflows.bak/spam-lockdown.yml similarity index 100% rename from .github/workflows/spam-lockdown.yml rename to .github/workflows.bak/spam-lockdown.yml diff --git a/.github/workflows/stale.yml b/.github/workflows.bak/stale.yml similarity index 100% rename from .github/workflows/stale.yml rename to .github/workflows.bak/stale.yml diff --git a/.github/workflows/sync-cnb.yml b/.github/workflows.bak/sync-cnb.yml similarity index 100% rename from .github/workflows/sync-cnb.yml rename to .github/workflows.bak/sync-cnb.yml diff --git a/.github/workflows/triage.yml b/.github/workflows.bak/triage.yml similarity index 100% rename from .github/workflows/triage.yml rename to .github/workflows.bak/triage.yml diff --git a/Added b/Added new file mode 100644 index 000000000..e69de29bb diff --git a/InstallationLog.txt b/InstallationLog.txt new file mode 100644 index 000000000..17e611d2a --- /dev/null +++ b/InstallationLog.txt @@ -0,0 +1,6 @@ +************************************* Invoked: Wed May 20 07:24:00 2026 +[0] Arguments: C:/Users/ljm37/AppData/Local/Temp/msys2.exe +[2] Operations sanity check succeeded. +[3] Warning: Cannot create lock file "C:\Users\ljm37\AppData\Local\Temp\msys21234865.lock": 另一个程序正在使用此文件,进程无法访问。 + (0x00000020) +[21] UnableToStart : Unable to start installer : Another msys2 instance is already running. Wait until it finishes, close it, or restart your system. diff --git a/_read_cli.js b/_read_cli.js new file mode 100644 index 000000000..33b97836c --- /dev/null +++ b/_read_cli.js @@ -0,0 +1,17 @@ +const fs=require('fs');const p=require('path');const h=require('os').homedir(); +const c=JSON.parse(fs.readFileSync(p.join(h,'.claude.json'),'utf8')); +console.log('provider:',c.provider); +console.log('baseUrl:',c.baseUrl); +console.log('model:',c.model); +// Check .claude/ for any config +const cd=p.join(h,'.claude'); +function check(f){ + try{const t=fs.readFileSync(f,'utf8');if(/deepseek|provider|api.?key|base.?url/i.test(t))console.log(f+':',t.substring(0,500))}catch(e){} +} +for(const e of fs.readdirSync(cd,{withFileTypes:true})){ + if(e.isFile())check(p.join(cd,e.name)); + else if(e.name==='settings'){for(const f of fs.readdirSync(p.join(cd,e.name),{withFileTypes:true})){if(f.isFile())check(p.join(cd,e.name,f.name))}} +} +// Check env +console.log('\nEnv vars:'); +['ANTHROPIC_API_KEY','ANTHROPIC_BASE_URL','ANTHROPIC_PROVIDER','ANTHROPIC_MODEL','DEEPSEEK_API_KEY'].forEach(k=>console.log(k+':',process.env[k]?'set':'not set')); diff --git a/_test_exe.js b/_test_exe.js new file mode 100644 index 000000000..67449034f --- /dev/null +++ b/_test_exe.js @@ -0,0 +1,16 @@ +const{execSync}=require('child_process'); +const h=require('os').homedir(); +const exe=h+'/AppData/Roaming/npm/node_modules/deepseek-tui/bin/downloads/deepseek.exe'; +try{ + const r=execSync('"'+exe+'" --version',{encoding:'utf8',timeout:10000}); + console.log('Direct exe OK:',r.trim()); +}catch(e){ + console.log('Direct FAIL:',e.message); +} +// Also try --help +try{ + const r=execSync('"'+exe+'" --help',{encoding:'utf8',timeout:10000}); + console.log('--help first 500:',r.substring(0,500)); +}catch(e){ + console.log('--help FAIL:',e.message); +} diff --git a/analyze_claude_paths.js b/analyze_claude_paths.js new file mode 100644 index 000000000..732ed6217 --- /dev/null +++ b/analyze_claude_paths.js @@ -0,0 +1,27 @@ +// analyze_claude_paths.js +const fs=require('fs'); +const p=require('path'); +const h=require('os').homedir(); +const pkgDir=p.join(h,'AppData','Roaming','npm','node_modules','@anthropic-ai','claude-code'); +console.log('Exists:',fs.existsSync(pkgDir)); +if(!fs.existsSync(pkgDir))process.exit(1); +const pkg=JSON.parse(fs.readFileSync(p.join(pkgDir,'package.json'),'utf8')); +console.log('Main:',pkg.main,'Bin:',JSON.stringify(pkg.bin)); +const entry=p.join(pkgDir,pkg.bin.claude); +if(fs.existsSync(entry)){ + const c=fs.readFileSync(entry,'utf8'); + console.log('Entry first 800:',c.substring(0,800)); +} +function search(dir,pattern,depth){ + if(depth>3||!fs.existsSync(dir))return; + const es=fs.readdirSync(dir,{withFileTypes:true}); + for(const e of es){ + const f=p.join(dir,e.name); + if(e.isDirectory()&&!e.name.startsWith('node_modules'))search(f,pattern,depth+1); + else if(e.isFile()&&/\.(js|ts|mjs)$/.test(e.name)){ + try{const c=fs.readFileSync(f,'utf8');if(c.includes(pattern))console.log(' MATCH:',p.relative(pkgDir,f),'for',pattern)}catch(ex){} + } + } +} +console.log('\n-- Searching dist/ --'); +['.claude.json','.claude/skills','homedir','.claude'].forEach(pat=>search(p.join(pkgDir,'dist'),pat,0)); diff --git a/build_check.bat b/build_check.bat new file mode 100644 index 000000000..356742daa --- /dev/null +++ b/build_check.bat @@ -0,0 +1,7 @@ +@echo off +set MSYS_NO_PATHCONV=1 +set MSYS2_ARG_CONV_EXCL=* +set PATH=C:\msys64\mingw64\bin;%PATH% +cd /d "C:\Users\ljm37\DeepSeek Tui" +set CARGO_TARGET_DIR=C:\cargo_target +cargo check -p deepseek-tui \ No newline at end of file diff --git a/check_ds.js b/check_ds.js new file mode 100644 index 000000000..f0b9d2898 --- /dev/null +++ b/check_ds.js @@ -0,0 +1,9 @@ +const fs=require('fs');const p=require('path'); +const z='C:/Users/ljm37/Desktop/deepseek-tui-full.zip'; +console.log('File:',fs.existsSync(z),'Size:',(fs.statSync(z).size/(1024*1024)).toFixed(0),'MB'); +// Quick check with PowerShell listing +const{execSync}=require('child_process'); +try{ + const r=execSync('powershell -Command "Add-Type -A System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::OpenRead(\\\"'+z+'\\\").Entries|?{$_.Name -match \\\"config|env\\\"}|Select Name,Length|ft -a"',{encoding:'utf8',timeout:15000}); + console.log(r); +}catch(e){console.log('list err:',e.message)} diff --git a/check_git.js b/check_git.js new file mode 100644 index 000000000..78304dbb0 --- /dev/null +++ b/check_git.js @@ -0,0 +1,30 @@ +const{execSync}=require('child_process'); +const fs=require('fs'); +const p=require('path'); + +// Git size check +const gitDir='C:\\Program Files\\Git'; +if(fs.existsSync(gitDir)){ + function getSize(dir){ + let total=0; + try{ + const es=fs.readdirSync(dir,{withFileTypes:true}); + for(const e of es){ + const fp=p.join(dir,e.name); + if(e.isDirectory())total+=getSize(fp); + else total+=fs.statSync(fp).size; + } + }catch(ex){} + return total; + } + const mb=getSize(gitDir)/(1024*1024); + console.log('Git size:',mb.toFixed(0),'MB'); + + // Show top-level + const es=fs.readdirSync(gitDir,{withFileTypes:true}); + es.forEach(e=>console.log(e.isDirectory()?'[DIR]':'[FILE]',e.name)); +} + +// Check gh +console.log('\ngh CLI:'); +try{console.log(execSync('gh --version',{encoding:'utf8'}))}catch(ex){console.log('Not installed')} diff --git a/check_pkg_paths.js b/check_pkg_paths.js new file mode 100644 index 000000000..ffbe4f927 --- /dev/null +++ b/check_pkg_paths.js @@ -0,0 +1,22 @@ +const fs=require('fs');const p=require('path');const h=require('os').homedir(); +const pkg=p.join(h,'AppData','Roaming','npm','node_modules','deepseek-tui'); +console.log('Package:',fs.existsSync(pkg)); +if(fs.existsSync(pkg)){ + function getSize(dir){let t=0;for(const e of fs.readdirSync(dir,{withFileTypes:true})){const fp=p.join(dir,e.name);if(e.isDirectory())t+=getSize(fp);else t+=fs.statSync(fp).size}return t} + console.log('Size:',(getSize(pkg)/(1024*1024)).toFixed(1),'MB'); + console.log('\nStructure:'); + function show(dir,indent){ + for(const e of fs.readdirSync(dir,{withFileTypes:true}).slice(0,30)){ + const fp=p.join(dir,e.name); + if(e.isDirectory()){console.log(indent+'[DIR]',e.name);show(fp,indent+' ')} + else{const s=fs.statSync(fp).size;console.log(indent+e.name,(s>1024?(s/1024).toFixed(0)+'KB':s+'B'))} + } + } + show(pkg,''); +} +// Check shim binary sizes +console.log('\nShim sizes:'); +['deepseek','deepseek.cmd','deepseek-tui','deepseek-tui.cmd'].forEach(f=>{ + const fp=p.join(h,'AppData','Roaming','npm',f); + if(fs.existsSync(fp))console.log(f,fs.statSync(fp).size,'bytes'); +}); diff --git a/check_ports.js b/check_ports.js new file mode 100644 index 000000000..53be12ef8 --- /dev/null +++ b/check_ports.js @@ -0,0 +1,41 @@ +// check_ports.js +const{execSync}=require('child_process'); +const ns = execSync('netstat -ano', {encoding:'utf8'}); + +// Find all unique PIDs with LISTENING +const listening = {}; +for (const line of ns.split('\n')) { + if (line.includes('LISTENING')) { + const parts = line.trim().split(/\s+/); + const pid = parts[parts.length - 1]; + const addr = parts[1]; + if (!listening[pid]) listening[pid] = []; + listening[pid].push(addr); + } +} + +// Show clash-related PIDs +const clashPids = ['9796', '6092', '49464']; +console.log('=== Clash-related LISTENING ports ==='); +for (const pid of clashPids) { + if (listening[pid]) { + console.log(`PID ${pid}:`); + listening[pid].forEach(a => console.log(' ' + a)); + } else { + console.log(`PID ${pid}: NO LISTENING ports`); + } +} + +// Show all listening +console.log('\n=== All LISTENING ports ==='); +for (const [pid, addrs] of Object.entries(listening)) { + console.log(`PID ${pid}: ${addrs.join(', ')}`); +} + +// Check port 9097 +console.log('\n=== Connections to 9097 ==='); +for (const line of ns.split('\n')) { + if (line.includes(':9097')) { + console.log(line.trim()); + } +} diff --git a/check_py.py b/check_py.py new file mode 100644 index 000000000..522d8b9aa --- /dev/null +++ b/check_py.py @@ -0,0 +1,7 @@ +import importlib +for m in ['openpyxl','pandas','pdfplumber','selenium','playwright','requests','PIL','bs4','lxml','numpy','matplotlib','flask','fastapi']: + try: + importlib.import_module(m) + print(f'{m}: OK') + except: + print(f'{m}: MISS') diff --git a/check_scripts.js b/check_scripts.js new file mode 100644 index 000000000..32632da43 --- /dev/null +++ b/check_scripts.js @@ -0,0 +1,35 @@ +// check_scripts.js - Find actual script files in skills +const fs=require('fs'); +const p=require('path'); +const h=require('os').homedir(); +const dirs=[p.join(h,'.claude','skills'),p.join(h,'.agents','skills')]; +const scripts=[]; +for(const d of dirs){ + if(!fs.existsSync(d))continue; + function w(dd,name){ + for(const f of fs.readdirSync(dd,{withFileTypes:true})){ + const fp=p.join(dd,f.name); + if(f.isDirectory())w(fp,name); + else if(/\.(py|js|sh|bat|ps1)$/.test(f.name)&&!f.name.includes('node_modules')){ + const s=fs.statSync(fp).size; + scripts.push({skill:name,file:f.name,path:fp,size:s}); + } + } + } + for(const e of fs.readdirSync(d,{withFileTypes:true})){ + if(e.isDirectory())w(p.join(d,e.name),e.name); + } +} +scripts.sort((a,b)=>b.size-a.size); +for(const s of scripts){ + console.log(`${s.skill}: ${s.file} (${(s.size/1024).toFixed(1)}KB)`); +} +console.log(`\nTotal: ${scripts.length} script files`); + +// Categorize by type +const byExt={}; +for(const s of scripts){ + const ext=p.extname(s.file); + byExt[ext]=(byExt[ext]||0)+1; +} +console.log('\nBy type:',JSON.stringify(byExt)); diff --git a/config.toml b/config.toml new file mode 100644 index 000000000..08e3aa07f --- /dev/null +++ b/config.toml @@ -0,0 +1,170 @@ +# ╔══════════════════════════════════════════════════════════════════════════════╗ +# ║ DeepSeek TUI Configuration ║ +# ║ 本地化配置 / Localized Configuration ║ +# ╚══════════════════════════════════════════════════════════════════════════════╝ + +# ───────────────────────────────────────────────────────────────────────────────── +# Active provider + DeepSeek defaults +# ───────────────────────────────────────────────────────────────────────────────── +provider = "deepseek" +api_key = "sk-f1bccc35f03d4e90be027a54ef02399a" +base_url = "https://api.deepseek.com/beta" + +# ───────────────────────────────────────────────────────────────────────────────── +# Default Models +# ───────────────────────────────────────────────────────────────────────────────── +default_text_model = "deepseek-v4-pro" + +# ───────────────────────────────────────────────────────────────────────────────── +# Thinking Mode (DeepSeek V4 reasoning effort) +# ───────────────────────────────────────────────────────────────────────────────── +# "off" — 禁用思维链 +# "high" — reasoning_effort = high (DeepSeek 默认) +# "max" — reasoning_effort = max (最深推理) +reasoning_effort = "max" + +# ───────────────────────────────────────────────────────────────────────────────── +# Cost Display (费用显示) +# ───────────────────────────────────────────────────────────────────────────────── +cost_currency = "cny" # 人民币显示 + +# ───────────────────────────────────────────────────────────────────────────────── +# Paths (路径) +# ───────────────────────────────────────────────────────────────────────────────── +skills_dir = "~/.deepseek/skills" +mcp_config_path = "~/.deepseek/mcp.json" +notes_path = "~/.deepseek/notes.txt" +memory_path = "~/.deepseek/memory.md" + +# ───────────────────────────────────────────────────────────────────────────────── +# User memory (用户记忆) +# ───────────────────────────────────────────────────────────────────────────────── +[memory] +enabled = true + +# ───────────────────────────────────────────────────────────────────────────────── +# Security (安全设置) +# ───────────────────────────────────────────────────────────────────────────────── +allow_shell = true +approval_policy = "on-request" # on-request | untrusted | never +sandbox_mode = "workspace-write" # read-only | workspace-write | danger-full-access + +# Auto-allow: 自动批准的命令前缀 +auto_allow = ["git status", "git log", "cargo check", "npm run", "cargo build"] +max_subagents = 10 + +# ───────────────────────────────────────────────────────────────────────────────── +# Web Search Provider (搜索引擎) +# ───────────────────────────────────────────────────────────────────────────────── +[search] +provider = "tavily" # bing | duckduckgo | tavily | bocha +# api_key = "tvly-YOUR-KEY" # Tavily API Key + +# ───────────────────────────────────────────────────────────────────────────────── +# Network Policy (网络策略) +# ───────────────────────────────────────────────────────────────────────────────── +[network] +default = "allow" +allow = ["api.deepseek.com", "github.com", ".githubusercontent.com", "api.tavily.com"] +deny = [] +audit = true + +# ───────────────────────────────────────────────────────────────────────────────── +# Skills (技能系统) +# ───────────────────────────────────────────────────────────────────────────────── +[skills] +registry_url = "https://raw.githubusercontent.com/Hmbown/deepseek-skills/main/index.json" +max_install_size_bytes = 5_242_880 + +# ───────────────────────────────────────────────────────────────────────────────── +# TUI (终端界面) +# ───────────────────────────────────────────────────────────────────────────────── +[tui] +alternate_screen = "auto" +mouse_capture = true +terminal_probe_timeout_ms = 500 +osc8_links = true +locale = "zh-Hans" # UI语言: auto | en | ja | zh-Hans | pt-BR + +# ───────────────────────────────────────────────────────────────────────────────── +# Feature Flags (功能开关) +# ───────────────────────────────────────────────────────────────────────────────── +[features] +shell_tool = true +subagents = true +web_search = true +apply_patch = true +mcp = true +exec_policy = true + +# ───────────────────────────────────────────────────────────────────────────────── +# Retry Configuration (重试配置) +# ───────────────────────────────────────────────────────────────────────────────── +[retry] +enabled = true +max_retries = 3 +initial_delay = 1.0 +max_delay = 60.0 +exponential_base = 2.0 + +# ───────────────────────────────────────────────────────────────────────────────── +# Context Compaction (上下文压缩) +# ───────────────────────────────────────────────────────────────────────────────── +[context] +enabled = true +verbatim_window_turns = 16 +l1_threshold = 192000 +l2_threshold = 384000 +l3_threshold = 576000 +cycle_threshold = 768000 +seam_model = "deepseek-v4-flash" + +# ───────────────────────────────────────────────────────────────────────────────── +# Notifications (桌面通知) +# ───────────────────────────────────────────────────────────────────────────────── +[notifications] +method = "auto" +threshold_secs = 30 +include_summary = true + +# ───────────────────────────────────────────────────────────────────────────────── +# Workspace Snapshots (工作区快照) +# ───────────────────────────────────────────────────────────────────────────────── +[snapshots] +enabled = true +max_age_days = 7 +max_workspace_gb = 2 + +# ───────────────────────────────────────────────────────────────────────────────── +# LSP Diagnostics (语言服务器诊断) +# ───────────────────────────────────────────────────────────────────────────────── +[lsp] +enabled = true +poll_after_edit_ms = 5000 +max_diagnostics_per_file = 20 +include_warnings = true + +# ───────────────────────────────────────────────────────────────────────────────── +# Hooks (钩子系统) +# ───────────────────────────────────────────────────────────────────────────────── +[hooks] +enabled = true +default_timeout_secs = 30 + +# [[hooks.hooks]] +# event = "session_start" +# command = "echo 'DeepSeek TUI 会话已启动'" + +# ───────────────────────────────────────────────────────────────────────────────── +# Runtime API (运行时API) +# ───────────────────────────────────────────────────────────────────────────────── +[runtime_api] +cors_origins = ["http://localhost:3000", "http://localhost:5173"] + +# ───────────────────────────────────────────────────────────────────────────────── +# DeepSeek Platform (DeepSeek平台配置) +# ───────────────────────────────────────────────────────────────────────────────── +[providers.deepseek] +api_key = "sk-f1bccc35f03d4e90be027a54ef02399a" +base_url = "https://api.deepseek.com/beta" +model = "deepseek-v4-pro" \ No newline at end of file diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index 4e5e78c00..4db825d76 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -944,13 +944,25 @@ impl Default for ComposerState { /// Viewport/scroll state — fields related to transcript scrolling and caching. pub struct ViewportState { + // Conversation region (upper) pub transcript_scroll: TranscriptScroll, pub pending_scroll_delta: i32, - pub mouse_scroll: MouseScrollState, pub transcript_cache: TranscriptViewCache, + + // Tool output region (lower) + pub tool_output_scroll: TranscriptScroll, + pub pending_tool_scroll_delta: i32, + pub tool_output_cache: TranscriptViewCache, + + // Shared state + pub mouse_scroll: MouseScrollState, pub transcript_selection: TranscriptSelection, pub selection_autoscroll: Option, pub transcript_scrollbar_dragging: bool, + + // Region area tracking + pub conversation_area: Option, + pub tool_output_area: Option, pub last_transcript_area: Option, pub last_transcript_top: usize, pub last_transcript_visible: usize, @@ -962,13 +974,25 @@ pub struct ViewportState { impl Default for ViewportState { fn default() -> Self { Self { + // Conversation region transcript_scroll: TranscriptScroll::to_bottom(), pending_scroll_delta: 0, - mouse_scroll: MouseScrollState::new(), transcript_cache: TranscriptViewCache::new(), + + // Tool output region + tool_output_scroll: TranscriptScroll::to_bottom(), + pending_tool_scroll_delta: 0, + tool_output_cache: TranscriptViewCache::new(), + + // Shared state + mouse_scroll: MouseScrollState::new(), transcript_selection: TranscriptSelection::default(), selection_autoscroll: None, transcript_scrollbar_dragging: false, + + // Region area tracking + conversation_area: None, + tool_output_area: None, last_transcript_area: None, last_transcript_top: 0, last_transcript_visible: 0, diff --git a/crates/tui/src/tui/history.rs b/crates/tui/src/tui/history.rs index 477eafa01..d93021a34 100644 --- a/crates/tui/src/tui/history.rs +++ b/crates/tui/src/tui/history.rs @@ -172,6 +172,25 @@ impl Default for TranscriptRenderOptions { } impl HistoryCell { + /// Returns true if this cell belongs in the conversation (upper) region. + pub fn is_conversation_cell(&self) -> bool { + matches!( + self, + HistoryCell::User { .. } + | HistoryCell::Assistant { .. } + | HistoryCell::Thinking { .. } + | HistoryCell::System { .. } + | HistoryCell::Error { .. } + | HistoryCell::ArchivedContext { .. } + | HistoryCell::SubAgent(_) + ) + } + + /// Returns true if this cell belongs in the tool output (lower) region. + pub fn is_tool_output_cell(&self) -> bool { + matches!(self, HistoryCell::Tool(_)) + } + /// Render the cell into a set of terminal lines. /// /// This is the live-display path used by widgets that don't already pass diff --git a/crates/tui/src/tui/mouse_ui.rs b/crates/tui/src/tui/mouse_ui.rs index 589c31aea..78bc11057 100644 --- a/crates/tui/src/tui/mouse_ui.rs +++ b/crates/tui/src/tui/mouse_ui.rs @@ -55,23 +55,25 @@ pub(crate) fn handle_mouse_event(app: &mut App, mouse: MouseEvent) -> Vec { let update = app.viewport.mouse_scroll.on_scroll(ScrollDirection::Up); - app.viewport.pending_scroll_delta = app - .viewport - .pending_scroll_delta - .saturating_add(update.delta_lines); + let pending_delta = if mouse_hits_tool_output_area(app, mouse) { + &mut app.viewport.pending_tool_scroll_delta + } else { + &mut app.viewport.pending_scroll_delta + }; + *pending_delta = pending_delta.saturating_add(update.delta_lines); if update.delta_lines != 0 { - app.user_scrolled_during_stream = true; app.needs_redraw = true; } } MouseEventKind::ScrollDown => { let update = app.viewport.mouse_scroll.on_scroll(ScrollDirection::Down); - app.viewport.pending_scroll_delta = app - .viewport - .pending_scroll_delta - .saturating_add(update.delta_lines); + let pending_delta = if mouse_hits_tool_output_area(app, mouse) { + &mut app.viewport.pending_tool_scroll_delta + } else { + &mut app.viewport.pending_scroll_delta + }; + *pending_delta = pending_delta.saturating_add(update.delta_lines); if update.delta_lines != 0 { - app.user_scrolled_during_stream = true; app.needs_redraw = true; } } @@ -141,6 +143,24 @@ pub(crate) fn handle_mouse_event(app: &mut App, mouse: MouseEvent) -> Vec bool { + if let Some(area) = app.viewport.tool_output_area { + return mouse.row >= area.y && mouse.row < area.y.saturating_add(area.height) + && mouse.column >= area.x && mouse.column < area.x.saturating_add(area.width); + } + false +} + +/// Check if mouse is over the conversation area (upper region). +fn mouse_hits_conversation_area(app: &App, mouse: MouseEvent) -> bool { + if let Some(area) = app.viewport.conversation_area { + return mouse.row >= area.y && mouse.row < area.y.saturating_add(area.height) + && mouse.column >= area.x && mouse.column < area.x.saturating_add(area.width); + } + false +} + pub(crate) fn mouse_hits_transcript_scrollbar(app: &App, mouse: MouseEvent) -> bool { let Some(area) = app.viewport.last_transcript_area else { return false; diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index 25e1fdb11..d76a5824d 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -121,7 +121,7 @@ use super::slash_menu::{ }; use super::views::{ConfigView, HelpView, ModalKind, ShellControlView, ViewEvent}; use super::widgets::pending_input_preview::{ContextPreviewItem, PendingInputPreview}; -use super::widgets::{ChatWidget, ComposerWidget, HeaderData, HeaderWidget, Renderable}; +use super::widgets::{ChatWidget, ChatRegion, ComposerWidget, HeaderData, HeaderWidget, Renderable}; // === Constants === @@ -5696,13 +5696,26 @@ fn render(f: &mut Frame, app: &mut App) { .direction(Direction::Vertical) .constraints([ Constraint::Length(header_height), // Header - Constraint::Min(1), // Chat area + Constraint::Min(1), // Chat area (split below) Constraint::Length(preview_height), // Pending input preview (0 if empty) Constraint::Length(composer_height), // Composer Constraint::Length(footer_height), // Footer ]) .split(size); + // Split chat area into conversation (upper) and tool output (lower) regions + const TOOL_OUTPUT_DEFAULT_HEIGHT: u16 = 10; + let chat_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Min(1), // Conversation area (flexible) + Constraint::Length(TOOL_OUTPUT_DEFAULT_HEIGHT), // Tool output area (fixed height) + ]) + .split(chunks[1]); + + let conversation_area = chat_layout[0]; + let tool_output_area = chat_layout[1]; + // Render header { let sanitized_context_window = context_usage @@ -5798,6 +5811,11 @@ fn render(f: &mut Frame, app: &mut App) { chunks[1] }; + // Conversation widget (upper region) + let conversation_widget = ChatWidget::new(app, conversation_area, ChatRegion::Conversation); + let buf = f.buffer_mut(); + conversation_widget.render(conversation_area, buf); + if let Some(sidebar_width) = sidebar_width_for_chat_area(app, chat_area.width) { let split = Layout::default() .direction(Direction::Horizontal) @@ -5807,9 +5825,10 @@ fn render(f: &mut Frame, app: &mut App) { sidebar_area = Some(split[1]); } - let chat_widget = ChatWidget::new(app, chat_area); + // Tool output widget (lower region) + let tool_output_widget = ChatWidget::new(app, tool_output_area, ChatRegion::ToolOutput); let buf = f.buffer_mut(); - chat_widget.render(chat_area, buf); + tool_output_widget.render(tool_output_area, buf); if let Some(sidebar_area) = sidebar_area { super::sidebar::render_sidebar(f, sidebar_area, app); diff --git a/crates/tui/src/tui/widgets/mod.rs b/crates/tui/src/tui/widgets/mod.rs index a81797690..889e14396 100644 --- a/crates/tui/src/tui/widgets/mod.rs +++ b/crates/tui/src/tui/widgets/mod.rs @@ -53,6 +53,15 @@ const COMPOSER_PANEL_HEIGHT: u16 = 2; const JUMP_TO_LATEST_BUTTON_WIDTH: u16 = 3; const JUMP_TO_LATEST_BUTTON_HEIGHT: u16 = 3; +/// Identifies which region of the split chat area a widget should render. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ChatRegion { + /// Upper region: conversation/transcript (user, assistant, thinking). + Conversation, + /// Lower region: tool output (exec, exploring, patch, etc.). + ToolOutput, +} + pub struct ChatWidget { content_area: Rect, lines: Vec>, @@ -63,6 +72,7 @@ pub struct ChatWidget { scroll_thumb: Color, jump_border: Color, jump_arrow: Color, + region: ChatRegion, } #[derive(Debug, Clone, Copy)] @@ -73,7 +83,7 @@ struct TranscriptScrollbar { } impl ChatWidget { - pub fn new(app: &mut App, area: Rect) -> Self { + pub fn new(app: &mut App, area: Rect, region: ChatRegion) -> Self { let content_area = area; let background = app.ui_theme.surface_bg; let scroll_track = app.ui_theme.border; @@ -101,6 +111,7 @@ impl ChatWidget { scroll_thumb, jump_border, jump_arrow, + region, }; } @@ -314,6 +325,7 @@ impl ChatWidget { scroll_thumb, jump_border, jump_arrow, + region, } } } diff --git a/deepseek.ps1 b/deepseek.ps1 new file mode 100644 index 000000000..2acebaffc --- /dev/null +++ b/deepseek.ps1 @@ -0,0 +1,23 @@ +# DeepSeek TUI 快捷启动脚本 +# 作者: Claude Code +# 创建时间: 2026-05-19 + +param( + [switch]$Auto, + [switch]$Model +) + +$exePath = "$env:USERPROFILE\AppData\Roaming\npm\node_modules\deepseek-tui\bin\downloads\deepseek.exe" +$workDir = "$env:USERPROFILE\DeepSeek Tui" + +# 设置环境 +$env:DEEPSEEK_API_KEY = "sk-f1bccc35f03d4e90be027a54ef02399a" + +# 构建参数 +$args = @() +if ($Auto) { $args += "--auto" } +if ($Model) { $args += "--model", $Model } + +# 启动 +Set-Location $workDir +& $exePath @args \ No newline at end of file diff --git a/find_clash.js b/find_clash.js new file mode 100644 index 000000000..7f991c8bb --- /dev/null +++ b/find_clash.js @@ -0,0 +1,80 @@ +// find_clash.js +const{execSync}=require('child_process'); +const fs=require('fs'); +const p=require('path'); +const h=require('os').homedir(); + +console.log('=== Clash Ninja Diagnostic ===\n'); + +// 1. Find executable path via PowerShell +try { + const cmd = 'powershell -Command "Get-Process clash-ninja | Select-Object Id,Path,MainWindowTitle | Format-List"'; + const out = execSync(cmd, {encoding:'utf8', timeout:15000}); + console.log('[Process Info]'); + console.log(out); +} catch(e) { + console.log('Process info err:', e.message); +} + +// 2. Search for Clash Ninja config locations +const searchPaths = [ + p.join(h, '.config', 'clash-ninja'), + p.join(h, '.config', 'clash'), + p.join(h, '.config', 'clash-verge'), + p.join(h, 'AppData', 'Roaming', 'clash-ninja'), + p.join(h, 'AppData', 'Roaming', 'clash-verge-rev'), + p.join(h, 'AppData', 'Roaming', 'io.github.clash-verge-rev'), + p.join(h, 'AppData', 'Local', 'clash-ninja'), + p.join(h, 'AppData', 'Local', 'Programs', 'clash-ninja'), +]; + +console.log('\n[Config Search]'); +let found = false; +for (const sp of searchPaths) { + if (fs.existsSync(sp)) { + found = true; + console.log('FOUND:', sp); + try { + const files = fs.readdirSync(sp); + files.forEach(f => console.log(' ', f)); + } catch(e) {} + } +} +if (!found) console.log('No standard config path found'); + +// 3. Check netstat for PID 9796 +console.log('\n[Listening Ports]'); +try { + const ns = execSync('netstat -ano', {encoding:'utf8', timeout:10000}); + const lines = ns.split('\n').filter(l => l.includes('9796') && l.includes('LISTENING')); + if (lines.length === 0) { + console.log('No LISTENING ports for PID 9796'); + } else { + lines.forEach(l => console.log(l)); + } +} catch(e) { + console.log('netstat err:', e.message); +} + +// 4. Test common proxy ports +console.log('\n[Proxy Port Test]'); +for (const port of [7890, 7891, 7892, 7893, 9090]) { + try { + const r = execSync(`curl --connect-timeout 2 http://127.0.0.1:${port} 2>NUL`, {encoding:'utf8', timeout:5000}); + console.log(`Port ${port}: reachable`); + } catch(e) { + // silent + } +} + +// 5. System proxy +console.log('\n[System Proxy]'); +try { + const ps = 'powershell -Command "Get-ItemProperty -Path \'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\' | Select-Object ProxyEnable,ProxyServer | Format-List"'; + const out = execSync(ps, {encoding:'utf8', timeout:10000}); + console.log(out); +} catch(e) { + console.log('err:', e.message); +} + +console.log('=== Done ==='); diff --git a/find_clash2.js b/find_clash2.js new file mode 100644 index 000000000..f6ced56a5 --- /dev/null +++ b/find_clash2.js @@ -0,0 +1,57 @@ +// find_clash2.js +const{execSync}=require('child_process'); +const fs=require('fs'); +const p=require('path'); + +const clashDir = 'C:\\Program Files\\Clash V-Ninja'; +console.log('=== Clash V-Ninja Directory ==='); +if (fs.existsSync(clashDir)) { + function walk(dir, indent) { + try { + const entries = fs.readdirSync(dir, {withFileTypes:true}); + for (const e of entries) { + const full = p.join(dir, e.name); + if (e.isDirectory()) { + console.log(indent + '[DIR]', e.name); + walk(full, indent + ' '); + } else if (e.name.endsWith('.yaml') || e.name.endsWith('.yml') || e.name.endsWith('.json') || e.name.endsWith('.exe') || e.name.endsWith('.dll') || e.name.endsWith('.txt')) { + try { + const stat = fs.statSync(full); + console.log(indent + e.name, '(' + (stat.size/1024).toFixed(1) + ' KB)'); + } catch(ex) {} + } else { + try { + const stat = fs.statSync(full); + console.log(indent + e.name, '(' + (stat.size/1024).toFixed(1) + ' KB)'); + } catch(ex) {} + } + } + } catch(e) { + console.log(indent + 'ERROR:', e.message); + } + } + walk(clashDir, ''); +} else { + console.log('Directory not found'); +} + +// Check service PID ports +console.log('\n=== Service PID 6092 ports ==='); +try { + const ns = execSync('netstat -ano', {encoding:'utf8'}); + const lines = ns.split('\n').filter(l => l.includes('6092')); + lines.forEach(l => console.log(l.trim())); + if (lines.length === 0) console.log('No connections for PID 6092'); +} catch(e) { + console.log('err:', e.message); +} + +// Check all clash-ninja ports +console.log('\n=== All clash-ninja ports ==='); +try { + const ns = execSync('netstat -ano', {encoding:'utf8'}); + const lines = ns.split('\n').filter(l => l.includes('9796') || l.includes('6092')); + lines.forEach(l => console.log(l.trim())); +} catch(e) { + console.log('err:', e.message); +} diff --git a/pack_claude.js b/pack_claude.js new file mode 100644 index 000000000..43f10e5da --- /dev/null +++ b/pack_claude.js @@ -0,0 +1,32 @@ +// pack_claude.js +const{execSync}=require('child_process'); +const fs=require('fs'); +const p=require('path'); +const h=require('os').homedir(); +const tmp=p.join(require('os').tmpdir(),'cc_pack'); +console.log('Home:',h); +fs.rmSync(tmp,{recursive:true,force:true}); +fs.mkdirSync(tmp,{recursive:true}); +function cp(s,d){if(!fs.existsSync(s)){console.log('MISS:',s);return}console.log('OK:',s);fs.cpSync(s,d,{recursive:true})} +// AppData = C:\Users\xxx\AppData\Roaming +const ad=p.join(h,'AppData','Roaming'); +// 1 +cp(p.join(ad,'npm','node_modules','@anthropic-ai','claude-code'),p.join(tmp,'claude-code-pkg')); +// 2 +cp(p.join(h,'.claude.json'),p.join(tmp,'.claude.json')); +// 3 +cp(p.join(h,'.claude','skills'),p.join(tmp,'.claude','skills')); +// 4 +cp(p.join(h,'.agents','skills'),p.join(tmp,'.agents','skills')); +// 5 +const sl=p.join(h,'.agents','.skill-lock.json'); +if(fs.existsSync(sl)){fs.mkdirSync(p.join(tmp,'.agents'),{recursive:true});fs.copyFileSync(sl,p.join(tmp,'.agents','.skill-lock.json'))} +// README +const readme='Claude Code Deployment Package\n===============================\nExported: '+(new Date()).toISOString().substring(0,16)+'\nSource: '+process.env.COMPUTERNAME+'\n\nContents:\n- claude-code-pkg/ : npm global package @anthropic-ai/claude-code\n- .claude.json : Claude Code user config\n- .claude/skills/ : Custom Claude skills\n- .agents/skills/ : Agent skills\n\nNOT included: sessions, backups, plans, projects, API keys\n'; +fs.writeFileSync(p.join(tmp,'README.txt'),readme,'utf8'); +// ZIP +const zip=p.join(h,'Desktop','claudecode.zip'); +console.log('ZIP to:',zip); +try{execSync('powershell -ExecutionPolicy Bypass -Command "Compress-Archive -Path \''+tmp+'\\*\' -DestinationPath \''+zip+'\' -Force"',{stdio:'inherit',timeout:300000})}catch(e){console.log('ZIP ERR:',e.message)} +if(fs.existsSync(zip)){const sz=fs.statSync(zip).size;console.log('DONE:',zip,'Size:',(sz/(1024*1024)).toFixed(2),'MB')}else{console.log('FAIL: zip not created')} +fs.rmSync(tmp,{recursive:true,force:true}) diff --git a/pack_claude.ps1 b/pack_claude.ps1 new file mode 100644 index 000000000..58a5f6449 --- /dev/null +++ b/pack_claude.ps1 @@ -0,0 +1,95 @@ +# Pack Claude Code for deployment +$ErrorActionPreference = "Stop" +$Desktop = [Environment]::GetFolderPath("Desktop") +$OutZip = Join-Path $Desktop "claudecode.zip" +$TempDir = Join-Path $env:TEMP "claudecode_pack" + +Write-Host "=== Packing Claude Code ===" +Write-Host "Output: $OutZip" + +# Clean temp +if (Test-Path $TempDir) { Remove-Item $TempDir -Recurse -Force } +New-Item -ItemType Directory -Path $TempDir -Force | Out-Null + +# --- 1. Claude Code npm package --- +$NpmPkg = "$env:APPDATA\npm\node_modules\@anthropic-ai\claude-code" +if (Test-Path $NpmPkg) { + $size = (Get-ChildItem $NpmPkg -Recurse -File | Measure-Object -Property Length -Sum).Sum + Write-Host "[1/5] Claude Code package: $([math]::Round($size/1MB,2)) MB" + Copy-Item $NpmPkg -Destination "$TempDir\claude-code-pkg" -Recurse +} else { + Write-Host "[1/5] WARNING: Claude Code npm package not found" +} + +# --- 2. .claude.json --- +$ClaudeJson = "$env:USERPROFILE\.claude.json" +if (Test-Path $ClaudeJson) { + Write-Host "[2/5] .claude.json config" + Copy-Item $ClaudeJson -Destination $TempDir +} else { + Write-Host "[2/5] WARNING: .claude.json not found" +} + +# --- 3. .claude/skills --- +$ClaudeSkills = "$env:USERPROFILE\.claude\skills" +if (Test-Path $ClaudeSkills) { + $size = (Get-ChildItem $ClaudeSkills -Recurse -File | Measure-Object -Property Length -Sum).Sum + Write-Host "[3/5] .claude/skills: $([math]::Round($size/1MB,2)) MB" + Copy-Item $ClaudeSkills -Destination "$TempDir\.claude\skills" -Recurse +} else { + Write-Host "[3/5] WARNING: .claude/skills not found" +} + +# --- 4. .agents/skills --- +$AgentSkills = "$env:USERPROFILE\.agents\skills" +if (Test-Path $AgentSkills) { + $size = (Get-ChildItem $AgentSkills -Recurse -File | Measure-Object -Property Length -Sum).Sum + Write-Host "[4/5] .agents/skills: $([math]::Round($size/1MB,2)) MB" + Copy-Item $AgentSkills -Destination "$TempDir\.agents\skills" -Recurse +} else { + Write-Host "[4/5] WARNING: .agents/skills not found" +} + +# --- 5. .agents/.skill-lock.json --- +$SkillLock = "$env:USERPROFILE\.agents\.skill-lock.json" +if (Test-Path $SkillLock) { + Write-Host "[5/5] .skill-lock.json" + Copy-Item $SkillLock -Destination "$TempDir\.agents\" -Force +} else { + Write-Host "[5/5] .skill-lock.json not found, skipping" +} + +# --- Create README --- +$Readme = @" +Claude Code Deployment Package +=============================== +Exported: $(Get-Date -Format 'yyyy-MM-dd HH:mm') +Source machine: $env:COMPUTERNAME + +Contents: +- claude-code-pkg/ : npm global package @anthropic-ai/claude-code +- .claude.json : Claude Code user config & project trust +- .claude/skills/ : Custom Claude skills +- .agents/skills/ : Agent skills (OpenClaw etc.) +- .agents/.skill-lock.json : Skill lock file + +NOT included (machine-specific, regenerate on new machine): +- Sessions, backups, plans, projects, shell-snapshots +- API keys (stored in OS credential manager) + +Deployment steps: see README in the assistant instructions. +"@ +$Readme | Out-File -FilePath "$TempDir\README.txt" -Encoding UTF8 + +# --- Zip --- +if (Test-Path $OutZip) { Remove-Item $OutZip -Force } +Compress-Archive -Path "$TempDir\*" -DestinationPath $OutZip -CompressionLevel Optimal + +$zipSize = (Get-Item $OutZip).Length +Write-Host "" +Write-Host "=== Done ===" +Write-Host "File: $OutZip" +Write-Host "Size: $([math]::Round($zipSize/1MB,2)) MB" + +# Cleanup +Remove-Item $TempDir -Recurse -Force \ No newline at end of file diff --git a/pack_claude_desktop.js b/pack_claude_desktop.js new file mode 100644 index 000000000..446da3213 --- /dev/null +++ b/pack_claude_desktop.js @@ -0,0 +1,226 @@ +// pack_claude_desktop.js +// Packages the FULL Claude Desktop (MS Store) experience: +// - Claude-3p user data (sessions, configs, embedded claude.exe) +// - Chrome Native Host +// - npm Claude Code (newer version, as fallback) +// - Skills and configs +// - Deployment script + +const { execSync } = require('child_process'); +const fs = require('fs'); +const p = require('path'); +const h = require('os').homedir(); +const tmp = p.join(require('os').tmpdir(), 'cc_desktop_pack'); +const outZip = p.join(h, 'Desktop', 'claude_desktop_full.zip'); + +console.log('=== Packing Claude Desktop (MS Store) - Full Migration Package ===\n'); +fs.rmSync(tmp, { recursive: true, force: true }); +fs.mkdirSync(tmp, { recursive: true }); + +function cp(src, dst) { + if (!fs.existsSync(src)) { console.log(' MISS:', src); return false; } + const dstDir = p.dirname(dst); + if (!fs.existsSync(dstDir)) fs.mkdirSync(dstDir, { recursive: true }); + fs.cpSync(src, dst, { recursive: true }); + let size = 0; + try { + const stat = fs.statSync(src); + if (stat.isFile()) size = stat.size; + else { + function calcDir(d) { + fs.readdirSync(d, { withFileTypes: true }).forEach(e => { + const fp = p.join(d, e.name); + if (e.isFile()) size += fs.statSync(fp).size; + else if (e.isDirectory()) calcDir(fp); + }); + } + calcDir(src); + } + } catch(e) {} + console.log(' OK:', src.replace(h, '~'), '->', (size/(1024*1024)).toFixed(1), 'MB'); + return true; +} + +function cpFile(src, dst) { + if (!fs.existsSync(src)) { console.log(' MISS:', src); return false; } + const dstDir = p.dirname(dst); + if (!fs.existsSync(dstDir)) fs.mkdirSync(dstDir, { recursive: true }); + fs.copyFileSync(src, dst); + const size = fs.statSync(src).size; + console.log(' OK:', src.replace(h, '~'), '->', (size/(1024*1024)).toFixed(1), 'MB'); + return true; +} + +// 1. Claude Desktop User Data (Claude-3p) +console.log('\n[1/6] Claude Desktop user data (Claude-3p)'); +const claude3pSrc = p.join(h, 'AppData', 'Local', 'Packages', 'Claude_pzs8sxrjxfjjc', 'LocalCache', 'Roaming', 'Claude-3p'); +const claude3pDst = p.join(tmp, 'Claude-3p'); + +if (fs.existsSync(claude3pSrc)) { + const essentialItems = [ + 'claude-code', 'claude-code-sessions', 'claude-code-vm', 'vm_bundles', + 'blob_storage', 'claude_desktop_config.json', 'config.json', 'configLibrary', + 'Preferences', 'developer_settings.json', 'window-state.json', + 'cowork-enabled-cli-ops.json', 'local-agent-mode-sessions', + 'Local Storage', 'Session Storage', 'IndexedDB', 'Partitions', + 'Network', 'Shared Dictionary', 'SharedStorage', 'SharedStorage-wal', + 'DIPS', 'DIPS-wal', 'fcache', 'git-worktrees.json', 'ant-did', + 'title-gen', 'Dictionaries', 'Local State', 'lockfile', 'WebStorage', + 'en-US-10-1.bdic', + ]; + + essentialItems.forEach(item => { + const src = p.join(claude3pSrc, item); + const dst = p.join(claude3pDst, item); + if (fs.existsSync(src)) { + const stat = fs.statSync(src); + if (stat.isDirectory()) cp(src, dst); + else cpFile(src, dst); + } + }); + console.log(' (Skipped: Cache, Code Cache, GPUCache, Dawn*Cache, Crashpad, logs, sentry)'); +} else { + console.log(' WARNING: Claude-3p not found'); +} + +// 2. Chrome Native Host +console.log('\n[2/6] Chrome Native Host'); +const cnhSrc = p.join(h, 'AppData', 'Local', 'Packages', 'Claude_pzs8sxrjxfjjc', 'LocalCache', 'Roaming', 'Claude', 'ChromeNativeHost'); +if (fs.existsSync(cnhSrc)) { + cp(cnhSrc, p.join(tmp, 'ChromeNativeHost')); +} + +// 3. npm Claude Code +console.log('\n[3/6] npm Claude Code (v2.1.126)'); +const npmCcSrc = p.join(h, 'AppData', 'Roaming', 'npm', 'node_modules', '@anthropic-ai', 'claude-code'); +cp(npmCcSrc, p.join(tmp, 'claude-code-pkg')); + +const npmDir = p.join(h, 'AppData', 'Roaming', 'npm'); +['claude', 'claude.cmd', 'claude.ps1'].forEach(f => { + cpFile(p.join(npmDir, f), p.join(tmp, 'npm-shims', f)); +}); + +// 4. Skills and Configs +console.log('\n[4/6] Skills and configurations'); +cp(p.join(h, '.claude', 'skills'), p.join(tmp, '.claude', 'skills')); +cp(p.join(h, '.agents', 'skills'), p.join(tmp, '.agents', 'skills')); +cpFile(p.join(h, '.claude.json'), p.join(tmp, '.claude.json')); + +const sl = p.join(h, '.agents', '.skill-lock.json'); +if (fs.existsSync(sl)) cpFile(sl, p.join(tmp, '.agents', '.skill-lock.json')); + +// 5. Deploy script +console.log('\n[5/6] Creating deployment script'); +const deployPs1 = [ +'# Claude Desktop + Claude Code Deployment Script', +'# Right-click -> Run with PowerShell', +'', +'Write-Host "============================================" -ForegroundColor Cyan', +'Write-Host " Claude Desktop Full Migration" -ForegroundColor Cyan', +'Write-Host "============================================" -ForegroundColor Cyan', +'', +'$ErrorActionPreference = "Continue"', +'$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path', +'$UserProfile = $env:USERPROFILE', +'$AppData = $env:APPDATA', +'$LocalAppData = $env:LOCALAPPDATA', +'', +'Write-Host "[1/5] Claude Desktop (MS Store)" -ForegroundColor Yellow', +'Write-Host " Opening Microsoft Store..."', +'Start-Process "ms-windows-store://pdp/?ProductId=9N9WZP3L0T9H"', +'Write-Host " Install Claude Desktop from the Store, then press ENTER..."', +'Read-Host', +'', +'Write-Host "[2/5] Restoring Claude Desktop user data..." -ForegroundColor Yellow', +'$claude3pSrc = Join-Path $ScriptDir "Claude-3p"', +'$pkgDirs = Get-ChildItem "$LocalAppData\\Packages" -Directory -Filter "Claude_*" -ErrorAction SilentlyContinue', +'if ($pkgDirs) {', +' $pkgDir = $pkgDirs[0].FullName', +' $targetDir = Join-Path $pkgDir "LocalCache\\Roaming\\Claude-3p"', +' New-Item -ItemType Directory -Path $targetDir -Force | Out-Null', +' Copy-Item "$claude3pSrc\\*" $targetDir -Recurse -Force', +' Write-Host " Data restored." -ForegroundColor Green', +'} else {', +' Write-Host " Claude Desktop not found. Install from Store first." -ForegroundColor Red', +'}', +'', +'$cnhSrc = Join-Path $ScriptDir "ChromeNativeHost"', +'if (Test-Path $cnhSrc) {', +' if ($pkgDirs) {', +' $cnhTarget = Join-Path $pkgDirs[0].FullName "LocalCache\\Roaming\\Claude\\ChromeNativeHost"', +' Copy-Item "$cnhSrc\\*" $cnhTarget -Recurse -Force', +' }', +'}', +'', +'Write-Host "[3/5] Installing Claude Code CLI..." -ForegroundColor Yellow', +'$ccTarget = Join-Path $AppData "npm\\node_modules\\@anthropic-ai\\claude-code"', +'New-Item -ItemType Directory -Path $ccTarget -Force | Out-Null', +'Copy-Item (Join-Path $ScriptDir "claude-code-pkg\\*") $ccTarget -Recurse -Force', +'', +'$npmBinDir = Join-Path $AppData "npm"', +'New-Item -ItemType Directory -Path $npmBinDir -Force | Out-Null', +'Copy-Item (Join-Path $ScriptDir "npm-shims\\*") $npmBinDir -Force', +'', +'$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")', +'if ($userPath -notlike "*$npmBinDir*") {', +' [Environment]::SetEnvironmentVariable("PATH", "$userPath;$npmBinDir", "User")', +' $env:PATH = "$env:PATH;$npmBinDir"', +'}', +'', +'Write-Host "[4/5] Restoring skills and configs..." -ForegroundColor Yellow', +'Copy-Item (Join-Path $ScriptDir ".claude\\skills\\*") (Join-Path $UserProfile ".claude\\skills") -Recurse -Force', +'Copy-Item (Join-Path $ScriptDir ".agents\\skills\\*") (Join-Path $UserProfile ".agents\\skills") -Recurse -Force', +'Copy-Item (Join-Path $ScriptDir ".claude.json") $UserProfile -Force', +'if (Test-Path (Join-Path $ScriptDir ".agents\\.skill-lock.json")) {', +' Copy-Item (Join-Path $ScriptDir ".agents\\.skill-lock.json") (Join-Path $UserProfile ".agents\\") -Force', +'}', +'Write-Host " Skills restored." -ForegroundColor Green', +'', +'Write-Host "[5/5] Checking dependencies..." -ForegroundColor Yellow', +'try { git --version 2>&1; Write-Host " git: OK" } catch {', +' Write-Host " Installing git via winget..."', +' winget install --id Git.Git -e --source winget --accept-source-agreements --accept-package-agreements', +'}', +'', +'Write-Host "=== Done ===" -ForegroundColor Green', +'Write-Host " Launch Claude Desktop from Start Menu (GUI) or run: claude (terminal)"', +]; +fs.writeFileSync(p.join(tmp, 'deploy.ps1'), deployPs1.join('\r\n'), 'utf8'); + +const readme = [ +'Claude Desktop Full Migration Package', +'=====================================', +'Exported: ' + new Date().toISOString().substring(0, 16), +'Source: ' + (process.env.COMPUTERNAME || 'unknown'), +'', +'Contents:', +'- Claude-3p/ : Claude Desktop user data (sessions, configs)', +'- ChromeNativeHost/ : Chrome browser integration', +'- claude-code-pkg/ : Claude Code CLI (v2.1.126)', +'- npm-shims/ : CLI shim scripts', +'- .claude.json : Claude Code config', +'- .claude/skills/ : Custom skills', +'- .agents/skills/ : Agent skills', +'- deploy.ps1 : Deployment script', +'', +'Deploy: Extract zip, right-click deploy.ps1 -> Run with PowerShell', +'Install Claude Desktop from MS Store when prompted.', +].join('\r\n'); +fs.writeFileSync(p.join(tmp, 'README.txt'), readme, 'utf8'); + +// 6. ZIP +console.log('\n[6/6] Creating ZIP...'); +if (fs.existsSync(outZip)) fs.rmSync(outZip, { force: true }); +try { + execSync( + 'powershell -ExecutionPolicy Bypass -Command "Compress-Archive -Path \'' + tmp + '\\*\' -DestinationPath \'' + outZip + '\' -Force"', + { stdio: 'inherit', timeout: 600000 } + ); +} catch (e) { console.log('ZIP ERR:', e.message); } + +if (fs.existsSync(outZip)) { + console.log('\nDONE:', outZip, 'Size:', (fs.statSync(outZip).size/(1024*1024)).toFixed(0), 'MB'); +} else { + console.log('\nFAILED'); +} +fs.rmSync(tmp, { recursive: true, force: true }); diff --git a/pack_claude_final.js b/pack_claude_final.js new file mode 100644 index 000000000..50c5f48f5 --- /dev/null +++ b/pack_claude_final.js @@ -0,0 +1,73 @@ +const{execSync}=require('child_process');const fs=require('fs');const p=require('path');const h=require('os').homedir(); +const tmp=p.join(require('os').tmpdir(),'cc_ds'); +const outZip=p.join(h,'Desktop','claudecode_full.zip'); +console.log('=== Repacking Claude Code with DeepSeek key ==='); +fs.rmSync(tmp,{recursive:true,force:true});fs.mkdirSync(tmp,{recursive:true}); +const ad=p.join(h,'AppData','Roaming'); +fs.cpSync(p.join(ad,'npm','node_modules','@anthropic-ai','claude-code'),p.join(tmp,'claude-code-pkg'),{recursive:true}); +fs.copyFileSync(p.join(h,'.claude.json'),p.join(tmp,'.claude.json')); +fs.cpSync(p.join(h,'.claude','skills'),p.join(tmp,'.claude','skills'),{recursive:true}); +fs.cpSync(p.join(h,'.agents','skills'),p.join(tmp,'.agents','skills'),{recursive:true}); +const sl=p.join(h,'.agents','.skill-lock.json'); +if(fs.existsSync(sl)){fs.mkdirSync(p.join(tmp,'.agents'),{recursive:true});fs.copyFileSync(sl,p.join(tmp,'.agents','.skill-lock.json'))} + +// Write .env with DeepSeek config for Claude Code +const envContent=[ +'# Claude Code -> DeepSeek API', +'ANTHROPIC_BASE_URL=https://api.deepseek.com/v1', +'ANTHROPIC_API_KEY=sk-f1bccc35f03d4e90be027a54ef02399a', +'# Optional: set default model', +'# ANTHROPIC_MODEL=deepseek-v4-pro', +'# ANTHROPIC_SMALL_FAST_MODEL=deepseek-v4-flash', +].join('\r\n'); +fs.writeFileSync(p.join(tmp,'.env'),envContent,'utf8'); + +// Updated deploy.ps1 +const dps1=[ +'# Claude Code Deployment -> DeepSeek API', +'Write-Host "============================================" -ForegroundColor Cyan', +'Write-Host " Claude Code + DeepSeek V4" -ForegroundColor Cyan', +'Write-Host "============================================" -ForegroundColor Cyan', +'', +'$ErrorActionPreference="Continue"', +'$s=Split-Path -Parent $MyInvocation.MyCommand.Path', +'$u=$env:USERPROFILE', +'$a=$env:APPDATA', +'$n=Join-Path $a "npm"', +'', +'Write-Host "[1/5] Installing Claude Code..."', +'$d=Join-Path $a "npm\\node_modules\\@anthropic-ai\\claude-code"', +'New-Item -ItemType Directory -Path $d -Force|Out-Null', +'Copy-Item "$s\\claude-code-pkg\\*" $d -Recurse -Force', +'$e=Join-Path $d "bin\\claude.exe"', +'New-Item -ItemType Directory -Path $n -Force|Out-Null', +'Set-Content (Join-Path $n "claude.cmd") "@echo off`r`n`"`"$e`"`" %*"', +'', +'Write-Host "[2/5] Adding to PATH..."', +'$p=[Environment]::GetEnvironmentVariable("PATH","User")', +'if($p -notlike "*$n*"){[Environment]::SetEnvironmentVariable("PATH","$p;$n","User");$env:PATH="$env:PATH;$n"}', +'', +'Write-Host "[3/5] Deploying config + skills..."', +'Copy-Item "$s\\.claude.json" $u -Force', +'Copy-Item "$s\\.claude\\skills\\*" (Join-Path $u ".claude\\skills") -Recurse -Force', +'Copy-Item "$s\\.agents\\skills\\*" (Join-Path $u ".agents\\skills") -Recurse -Force', +'', +'Write-Host "[4/5] Setting up DeepSeek API..."', +'Copy-Item "$s\\.env" $u -Force', +'[Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL","https://api.deepseek.com/v1","User")', +'[Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY","sk-f1bccc35f03d4e90be027a54ef02399a","User")', +'Write-Host " Key configured -> DeepSeek V4"', +'', +'Write-Host "[5/5] Checking git..."', +'try{$v=git --version 2>&1;if($LASTEXITCODE -eq 0){Write-Host " git: OK"}}catch{try{winget install --id Git.Git -e --source winget --accept-source-agreements --accept-package-agreements}catch{Write-Host " Install git: https://git-scm.com/download/win"}}', +'', +'Write-Host "=== Done ===" -ForegroundColor Green', +'Write-Host "Restart terminal, run: claude"', +]; +fs.writeFileSync(p.join(tmp,'deploy.ps1'),dps1.join('\r\n'),'utf8'); + +console.log('Zipping...'); +if(fs.existsSync(outZip))fs.rmSync(outZip,{force:true}); +execSync('powershell -ExecutionPolicy Bypass -Command "Compress-Archive -Path \''+tmp+'\\*\' -DestinationPath \''+outZip+'\' -Force"',{stdio:'inherit',timeout:300000}); +console.log('DONE:',outZip,'Size:',(fs.statSync(outZip).size/(1024*1024)).toFixed(0),'MB'); +fs.rmSync(tmp,{recursive:true,force:true}); diff --git a/pack_claude_full.js b/pack_claude_full.js new file mode 100644 index 000000000..ba37a3042 --- /dev/null +++ b/pack_claude_full.js @@ -0,0 +1,143 @@ +// pack_claude_full.js +const{execSync}=require('child_process'); +const fs=require('fs'); +const p=require('path'); +const h=require('os').homedir(); +const tmp=p.join(require('os').tmpdir(),'cc_full'); +const desktop=p.join(h,'Desktop'); +const outZip=p.join(desktop,'claudecode_full.zip'); + +console.log('=== Packing Claude Code Full ==='); +fs.rmSync(tmp,{recursive:true,force:true}); +fs.mkdirSync(tmp,{recursive:true}); +function cp(s,d){if(!fs.existsSync(s)){console.log('MISS:',s);return}console.log('OK:',s);fs.cpSync(s,d,{recursive:true})} +const ad=p.join(h,'AppData','Roaming'); + +// 1-3: Copy Claude Code, config, skills (same as before) +cp(p.join(ad,'npm','node_modules','@anthropic-ai','claude-code'),p.join(tmp,'claude-code-pkg')); +cp(p.join(h,'.claude.json'),p.join(tmp,'.claude.json')); +cp(p.join(h,'.claude','skills'),p.join(tmp,'.claude','skills')); +cp(p.join(h,'.agents','skills'),p.join(tmp,'.agents','skills')); +const sl=p.join(h,'.agents','.skill-lock.json'); +if(fs.existsSync(sl)){fs.mkdirSync(p.join(tmp,'.agents'),{recursive:true});fs.copyFileSync(sl,p.join(tmp,'.agents','.skill-lock.json'))} + +// 4. Write deploy.ps1 (avoid template literal) +const dps1 = [ +'# Claude Code Full Deployment Script', +'# Right-click deploy.ps1 -> Run with PowerShell', +'Write-Host "=== Claude Code Deployment ===" -ForegroundColor Cyan', +'$ErrorActionPreference="Continue"', +'$ScriptDir=Split-Path -Parent $MyInvocation.MyCommand.Path', +'$UserProfile=$env:USERPROFILE', +'$AppData=$env:APPDATA', +'', +'Write-Host "[1/5] Installing Claude Code..."', +'$pkgDest=Join-Path $AppData "npm\\node_modules\\@anthropic-ai\\claude-code"', +'New-Item -ItemType Directory -Path $pkgDest -Force | Out-Null', +'Copy-Item "$ScriptDir\\claude-code-pkg\\*" $pkgDest -Recurse -Force', +'', +'$npmDir=Join-Path $AppData "npm"', +'New-Item -ItemType Directory -Path $npmDir -Force | Out-Null', +'$exeSrc=Join-Path $pkgDest "bin\\claude.exe"', +'$shim=Join-Path $npmDir "claude.cmd"', +'Set-Content $shim "@`"$exeSrc`" %*"', +'', +'Write-Host "[2/5] Deploying config..."', +'Copy-Item "$ScriptDir\\.claude.json" $UserProfile -Force', +'', +'Write-Host "[3/5] Deploying skills..."', +'$cs=Join-Path $UserProfile ".claude\\skills"', +'Copy-Item "$ScriptDir\\.claude\\skills\\*" $cs -Recurse -Force', +'$as=Join-Path $UserProfile ".agents\\skills"', +'Copy-Item "$ScriptDir\\.agents\\skills\\*" $as -Recurse -Force', +'if(Test-Path "$ScriptDir\\.agents\\.skill-lock.json"){', +' Copy-Item "$ScriptDir\\.agents\\.skill-lock.json" (Join-Path $UserProfile ".agents\\") -Force', +'}', +'', +'Write-Host "[4/5] Checking git (essential)..."', +'$gitOk=$false', +'try{$v=git --version 2>&1;if($LASTEXITCODE -eq 0){$gitOk=$true;Write-Host " git: OK"}}catch{}', +'if(!$gitOk){', +' Write-Host " git not found. Trying winget install..."', +' try{', +' winget install --id Git.Git -e --source winget --accept-source-agreements --accept-package-agreements', +' Write-Host " git installed. RESTART terminal after script."', +' }catch{', +' Write-Host " FAILED. Install manually: https://git-scm.com/download/win"', +' }', +'}', +'', +'Write-Host "[5/5] Checking Python (optional)..."', +'$pyOk=$false', +'try{$v=python --version 2>&1;if($LASTEXITCODE -eq 0){$pyOk=$true;Write-Host " Python: OK"}}catch{}', +'if(!$pyOk){', +' Write-Host " Python not found (optional). winget install Python.Python.3.12"', +'}', +'', +'Write-Host ""', +'Write-Host "Next steps:" -ForegroundColor Yellow', +'Write-Host " 1. Restart terminal"', +'Write-Host " 2. Run: claude"', +'Write-Host " 3. Set API key: claude config set apiKey YOUR_KEY"', +'Write-Host ""', +'Write-Host "=== Done ===" -ForegroundColor Green' +]; +fs.writeFileSync(p.join(tmp,'deploy.ps1'), dps1.join('\r\n'), 'utf8'); + +// README +const readme=[ +'Claude Code Full Deployment Package', +'===================================', +'Exported: '+new Date().toISOString().substring(0,16), +'Source: '+(process.env.COMPUTERNAME||'unknown'), +'', +'Contents:', +'- claude-code-pkg/ : Claude Code (248MB, self-contained, Node.js embedded)', +'- .claude.json : User config & project trust', +'- .claude/skills/ : Custom Claude skills (30+)', +'- .agents/skills/ : Agent skills (OpenClaw etc.)', +'- deploy.ps1 : One-click deployment script', +'', +'How to deploy on new Windows PC:', +'1. Extract claudecode_full.zip anywhere', +'2. Right-click deploy.ps1 -> Run with PowerShell', +'3. Restart terminal, run: claude', +'', +'What deploy.ps1 does:', +'1. Copies Claude Code to %%APPDATA%%\\npm\\node_modules\\', +'2. Creates claude.cmd shim in %%APPDATA%%\\npm\\', +'3. Deploys .claude.json and skills to your home folder', +'4. Installs git via winget (if missing) - ESSENTIAL', +'5. Checks for Python (optional - for skill companion scripts)', +'', +'Dependencies bundled IN the zip:', +'- Node.js -> EMBEDDED in claude.exe (no external install needed)', +'- npm -> NOT needed (Claude Code is self-contained)', +'', +'Dependencies auto-installed by script:', +'- git -> winget install Git.Git (ESSENTIAL)', +'', +'Manual install required:', +'- Python 3 -> winget install Python.Python.3.12 (optional)', +'- GitHub CLI -> winget install GitHub.cli (optional)', +'', +'The 4 .js files in skills use Claude Code built-in JS engine (no Node.js needed).', +'The 5 .py files in skills need Python for companion scripts (optional).', +'PowerShell comes with Windows 10+, curl comes with Windows 10+.' +].join('\r\n'); +fs.writeFileSync(p.join(tmp,'README.txt'), readme, 'utf8'); + +// ZIP +console.log('Creating zip...'); +if(fs.existsSync(outZip))fs.rmSync(outZip,{force:true}); +try{ + execSync('powershell -ExecutionPolicy Bypass -Command "Compress-Archive -Path \''+tmp+'\\*\' -DestinationPath \''+outZip+'\' -Force"',{stdio:'inherit',timeout:300000}); +}catch(e){console.log('ZIP ERR:',e.message)} + +if(fs.existsSync(outZip)){ + const sz=fs.statSync(outZip).size; + console.log('\nDONE:',outZip,'Size:',(sz/(1024*1024)).toFixed(0),'MB'); +}else{ + console.log('\nFAIL'); +} +fs.rmSync(tmp,{recursive:true,force:true}); diff --git a/pack_ds_full.js b/pack_ds_full.js new file mode 100644 index 000000000..824fe7175 --- /dev/null +++ b/pack_ds_full.js @@ -0,0 +1,42 @@ +const{execSync}=require('child_process');const fs=require('fs');const p=require('path');const h=require('os').homedir(); +const tmp=p.join(require('os').tmpdir(),'ds_final2'); +const outZip=p.join(h,'Desktop','deepseek-tui-full.zip'); +console.log('=== Repacking with API key ==='); +fs.rmSync(tmp,{recursive:true,force:true});fs.mkdirSync(tmp,{recursive:true}); +const nd=p.join(h,'AppData','Roaming','npm','node_modules','deepseek-tui'); +const cd=p.join(h,'.deepseek'); +fs.mkdirSync(p.join(tmp,'bin'),{recursive:true}); +fs.copyFileSync(p.join(nd,'bin','downloads','deepseek.exe'),p.join(tmp,'bin','deepseek.exe')); +fs.copyFileSync(p.join(nd,'bin','downloads','deepseek-tui.exe'),p.join(tmp,'bin','deepseek-tui.exe')); +fs.writeFileSync(p.join(tmp,'launch_deepseek.cmd'),'@echo off\r\n"%~dp0bin\\deepseek.exe" %*'); +fs.copyFileSync(p.join(cd,'config.toml'),p.join(tmp,'config.toml')); +fs.copyFileSync(p.join(cd,'.env'),p.join(tmp,'.env')); +fs.copyFileSync(p.join(cd,'mcp.json'),p.join(tmp,'mcp.json')); +fs.cpSync(p.join(cd,'skills'),p.join(tmp,'skills'),{recursive:true}); +// deploy.ps1 inline +const ps=[ +'$s=Split-Path -Parent $MyInvocation.MyCommand.Path', +'$u=$env:USERPROFILE', +'$b=Join-Path $u ".deepseek\\bin"', +'New-Item -ItemType Directory -Path $b -Force|Out-Null', +'Copy-Item "$s\\bin\\*" $b -Force', +'$l=Join-Path $u "AppData\\Roaming\\npm"', +'New-Item -ItemType Directory -Path $l -Force|Out-Null', +'$exe=Join-Path $b "deepseek.exe"', +'Set-Content (Join-Path $l "deepseek.cmd") "@echo off`r`n`"`"$exe`"`" %*"', +'$p=[Environment]::GetEnvironmentVariable("PATH","User")', +'if($p -notlike "*$l*"){[Environment]::SetEnvironmentVariable("PATH","$p;$l","User")}', +'$d=Join-Path $u ".deepseek"', +'New-Item -ItemType Directory -Path $d -Force|Out-Null', +'Copy-Item "$s\\config.toml" $d -Force', +'Copy-Item "$s\\.env" $d -Force', +'Copy-Item "$s\\mcp.json" $d -Force', +'Copy-Item "$s\\skills\\*" (Join-Path $d "skills") -Recurse -Force', +'Write-Host "Done! Restart terminal, run: deepseek"', +]; +fs.writeFileSync(p.join(tmp,'deploy.ps1'),ps.join('\r\n'),'utf8'); +console.log('Zipping...'); +if(fs.existsSync(outZip))fs.rmSync(outZip,{force:true}); +execSync('powershell -ExecutionPolicy Bypass -Command "Compress-Archive -Path \''+tmp+'\\*\' -DestinationPath \''+outZip+'\' -Force"',{stdio:'inherit',timeout:300000}); +console.log('DONE:',outZip,'Size:',(fs.statSync(outZip).size/(1024*1024)).toFixed(0),'MB'); +fs.rmSync(tmp,{recursive:true,force:true}); diff --git a/scan_deps.js b/scan_deps.js new file mode 100644 index 000000000..413d046dc --- /dev/null +++ b/scan_deps.js @@ -0,0 +1,37 @@ +// scan_deps.js +const fs=require('fs'); +const p=require('path'); +const h=require('os').homedir(); + +const dirs=[p.join(h,'.claude','skills'),p.join(h,'.agents','skills')]; +const pats={python:/\bpython[23]?\b|\.py\b/gi,node:/\bnode\b|npm\b|npx\b|\.js\b/gi,git:/\bgit\b/gi,curl:/\bcurl\b/gi,gh:/\bgh\b/gi,docker:/\bdocker\b/gi,pwsh:/\bpowershell\b|\.ps1\b/gi,wechat:/\bwechat-cli\b/gi,browser:/\bchrome\b|playwright\b|selenium\b/gi,ffmpeg:/\bffmpeg\b/gi,excel:/\bopenpyxl\b|pandas\b|\.xlsx\b/gi}; +const deps={}; +for(const d of dirs){ + if(!fs.existsSync(d))continue; + for(const e of fs.readdirSync(d,{withFileTypes:true})){ + if(!e.isDirectory())continue; + const sk=p.join(d,e.name); + function scan(f){ + let c=''; + try{c=fs.readFileSync(f,'utf8')}catch(ex){return} + for(const [k,r] of Object.entries(pats)){ + if(r.test(c)){ + if(!deps[e.name])deps[e.name]=new Set(); + deps[e.name].add(k); + } + } + } + const smd=p.join(sk,'SKILL.md');if(fs.existsSync(smd))scan(smd); + function w(dd){ + if(!fs.existsSync(dd))return; + for(const f of fs.readdirSync(dd,{withFileTypes:true})){ + const fp=p.join(dd,f.name); + if(f.isDirectory())w(fp); + else if(/\.(py|js|sh|bat|ps1|json)$/.test(f.name))scan(fp); + } + } + w(sk); + } +} +console.log('=== Skills needing external tools ==='); +for(const [k,v] of Object.entries(deps))console.log(k,':',[...v].join(',')); diff --git a/start.bat b/start.bat new file mode 100644 index 000000000..ec0a4f7cb --- /dev/null +++ b/start.bat @@ -0,0 +1,5 @@ +@echo off +title DeepSeek TUI +cd /d "%USERPROFILE%\DeepSeek Tui" +"%USERPROFILE%\AppData\Roaming\npm\node_modules\deepseek-tui\bin\downloads\deepseek.exe" --model auto +pause \ No newline at end of file diff --git a/verify_deploy.js b/verify_deploy.js new file mode 100644 index 000000000..caad851f0 --- /dev/null +++ b/verify_deploy.js @@ -0,0 +1,8 @@ +const{execSync}=require('child_process');const fs=require('fs');const p=require('path');const h=require('os').homedir(); +const zip=p.join(h,'Desktop','claudecode_full.zip'); +const tmp=p.join(require('os').tmpdir(),'v2');fs.rmSync(tmp,{recursive:true,force:true});fs.mkdirSync(tmp); +execSync('powershell -Command "Expand-Archive -Path \''+zip+'\' -DestinationPath \''+tmp+'\' -Force"',{timeout:30000}); +console.log('=== .env ==='); +console.log(fs.readFileSync(p.join(tmp,'.env'),'utf8')); +console.log('=== deploy.ps1 ANTHROPIC lines ==='); +fs.readFileSync(p.join(tmp,'deploy.ps1'),'utf8').split('\n').filter(l=>/ANTHROPIC|deepseek|api.*key|base.*url/i.test(l)).forEach(l=>console.log(l.trim()));