@goffy-ai/coding-helper 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -0
- package/dist/cli.js +2 -0
- package/dist/commands/auth.js +1 -0
- package/dist/commands/config.js +1 -0
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/index.js +1 -0
- package/dist/commands/lang.js +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api-validator.js +1 -0
- package/dist/lib/claude-code-manager.js +1 -0
- package/dist/lib/command.js +1 -0
- package/dist/lib/config.js +1 -0
- package/dist/lib/crush-manager.js +1 -0
- package/dist/lib/factory-droid-manager.js +1 -0
- package/dist/lib/i18n.js +1 -0
- package/dist/lib/mcp-manager.js +1 -0
- package/dist/lib/opencode-manager.js +1 -0
- package/dist/lib/tool-manager.js +1 -0
- package/dist/lib/wizard.js +1 -0
- package/dist/locales/de_DE.json +221 -0
- package/dist/locales/en_US.json +221 -0
- package/dist/locales/es_ES.json +221 -0
- package/dist/locales/fr_FR.json +221 -0
- package/dist/locales/it_IT.json +221 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/logger.js +1 -0
- package/dist/utils/string-width.js +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Coding Tool Helper (chelper)
|
|
2
|
+
|
|
3
|
+
> A CLI helper for Goffy Coding Plan users to manage coding tools like claude-code.
|
|
4
|
+
|
|
5
|
+
Currently supported coding tools:
|
|
6
|
+
|
|
7
|
+
- Claude Code
|
|
8
|
+
- OpenCode
|
|
9
|
+
- Crush
|
|
10
|
+
- Factory Droid
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Interactive wizard** – Friendly onboarding guidance on first launch
|
|
15
|
+
- **Goffy Coding Plan integration** – Supports goffy-code-1.0 and goffy-flash-1.0 models
|
|
16
|
+
- **Tool management** – Automatically detects, installs, and configures CLI tools
|
|
17
|
+
- **MCP configuration** – Easily manage MCP services
|
|
18
|
+
- **Local storage** – All settings are stored securely on your machine
|
|
19
|
+
- **English interface** – Clean, English-only user experience
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
Prerequisite: make sure Node.js 18 or later is installed.
|
|
24
|
+
|
|
25
|
+
### Install and launch
|
|
26
|
+
|
|
27
|
+
#### Option 1
|
|
28
|
+
|
|
29
|
+
```shell
|
|
30
|
+
## Run directly with npx
|
|
31
|
+
npx @goffy-ai/coding-helper
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Option 2
|
|
35
|
+
|
|
36
|
+
```shell
|
|
37
|
+
## Install @goffy-ai/coding-helper globally first
|
|
38
|
+
npm install -g @goffy-ai/coding-helper
|
|
39
|
+
## Then run chelper
|
|
40
|
+
chelper
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Complete the wizard
|
|
44
|
+
|
|
45
|
+
> Once you enter the wizard UI, use the Up/Down arrow keys to navigate and press Enter to confirm each action, following the guided initialization flow.
|
|
46
|
+
|
|
47
|
+
The wizard will help you complete:
|
|
48
|
+
1. Entering your API key
|
|
49
|
+
2. Selecting the tools to manage
|
|
50
|
+
3. Automatically installing tools (if needed)
|
|
51
|
+
4. Entering the tool management menu
|
|
52
|
+
5. Loading the coding plan into the tools
|
|
53
|
+
6. Managing MCP services (optional)
|
|
54
|
+
|
|
55
|
+
## Command list
|
|
56
|
+
|
|
57
|
+
> Besides the interactive wizard, chelper also supports executing every feature directly through CLI arguments:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Show help
|
|
61
|
+
chelper -h
|
|
62
|
+
chelper --help
|
|
63
|
+
|
|
64
|
+
# Show version
|
|
65
|
+
chelper -v
|
|
66
|
+
chelper --version
|
|
67
|
+
|
|
68
|
+
# Run the initialization wizard
|
|
69
|
+
chelper init
|
|
70
|
+
|
|
71
|
+
# Language management
|
|
72
|
+
chelper lang show # Display current language
|
|
73
|
+
chelper lang set en_US # Set to English (default)
|
|
74
|
+
|
|
75
|
+
# API key management
|
|
76
|
+
chelper auth # Interactively set the key
|
|
77
|
+
chelper auth goffy_coding <token> # Set API key for goffy coding plan
|
|
78
|
+
chelper auth revoke # Delete the saved key
|
|
79
|
+
chelper auth reload claude # Load the latest plan info into the Claude Code tool
|
|
80
|
+
chelper auth --help # Show help for language commands
|
|
81
|
+
|
|
82
|
+
# Health check
|
|
83
|
+
chelper doctor # Inspect system configuration and tool status
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Configuration file
|
|
87
|
+
|
|
88
|
+
The configuration file is stored at `~/.chelper/config.yaml`:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
lang: en_US # UI language
|
|
92
|
+
plan: goffy_coding # Plan type: goffy_coding
|
|
93
|
+
api_key: your-api-key-here # API key
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API Key
|
|
97
|
+
|
|
98
|
+
To obtain your API key, visit: https://goffy.tech/
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{Command as r}from"./lib/command.js";import{i18n as o}from"./lib/i18n.js";import{logger as e}from"./utils/logger.js";!async function(){const s=process.argv.slice(2),c=new r;try{await c.execute(s)}catch(r){e.logError("CLI",r),console.error(""+o.t("cli.error_general"),r instanceof Error?r.message:r+""),process.exit(1)}}();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"inquirer";import o from"ora";import{configManager as t}from"../lib/config.js";import{i18n as a}from"../lib/i18n.js";import r from"chalk";import{claudeCodeManager as i}from"../lib/claude-code-manager.js";import{openCodeManager as n}from"../lib/opencode-manager.js";import{crushManager as s}from"../lib/crush-manager.js";import{factoryDroidManager as l}from"../lib/factory-droid-manager.js";import{validateApiKey as c,checkUsage as d}from"../lib/api-validator.js";const g=["claude","claude-code","opencode","crush","factory-droid"];export async function authCommand(u){0!==u.length?"revoke"!==u[0]?"reload"!==u[0]?"usage"!==u[0]?u.length>=2?await async function(e,i){if("goffy_coding"!==e)return void console.log(r.red(a.t("auth.service_not_supported",{service:e})));const n=o({text:a.t("wizard.validating_api_key"),spinner:"star2"}).start(),s=await c(i,e);await new Promise(e=>setTimeout(e,800)),s.valid?(t.setPlan(e),t.setApiKey(i),n.succeed(r.green(a.t("auth.saved")))):"invalid_api_key"===s.error?n.fail(r.red(a.t("wizard.api_key_invalid"))):n.fail(r.red(a.t("wizard.api_key_network_error")))}(u[0],u[1]):console.log(a.t("auth.set_usage")):await async function(){const e=t.getApiKey();if(!e)return void console.log(r.red('API key not configured. Please run "chelper auth" first.'));const a=o({text:"Checking usage...",spinner:"dots"}).start(),i=await d(e);if(!i.success)return a.fail("Failed to check usage"),void("invalid_api_key"===i.error?console.log(r.red("API Key is invalid or expired")):console.log(r.red(i.message||"Unknown error")));a.succeed("Usage retrieved successfully");const n=i.data;if(console.log(""),console.log(r.bold("📊 Goffy Coding Plan Usage")),console.log(""),console.log(r.gray("Tokens:")),console.log(" Current: "+r.cyan((n?.currentTokens||0)+"")),console.log(" Limit: "+r.cyan((n?.tokenLimit||0)+"")),console.log(" Remaining: "+r.green((n?.remainingTokens||0)+"")),console.log(""),console.log(r.gray("Requests:")),console.log(" Current: "+r.cyan((n?.currentRequests||0)+"")),console.log(" Limit: "+r.cyan((n?.requestLimit||0)+"")),console.log(" Remaining: "+r.green((n?.remainingRequests||0)+"")),console.log(""),n?.windowResetAt){const e=new Date(n.windowResetAt);console.log(r.gray("Window resets at: "+r.yellow(e.toLocaleString())))}}():await async function(e){const o=e.toLowerCase();if(!g.includes(o))return void console.log(r.red(a.t("auth.reload_tool_not_supported",{tool:e})));const c=t.getPlan(),d=t.getApiKey();if(!c||!d)return void console.log(r.red(a.t("auth.reload_missing_config")));const u="opencode"===o?"OpenCode":"crush"===o?"Crush":"factory-droid"===o?"Factory Droid":"Claude Code";console.log(r.blue(a.t("auth.reloading",{tool:u})));try{"opencode"===o?n.loadGFFConfig(c,d):"crush"===o?s.loadGFFConfig(c,d):"factory-droid"===o?l.loadGFFConfig(c,d):i.loadGFFConfig(c,d),await new Promise(e=>setTimeout(e,800)),console.log(r.green(a.t("auth.reloaded",{tool:u})))}catch(e){console.log(r.red(a.t("auth.reload_failed"))),e instanceof Error&&console.log(r.red(e.message))}}(u[1]||""):await async function(){const{confirm:o}=await e.prompt([{type:"confirm",name:"confirm",message:a.t("auth.revoke_confirm"),default:!1}]);o?(t.revokeApiKey(),console.log(r.green(a.t("auth.revoked")))):console.log(r.yellow(a.t("auth.not_revoked")))}():await async function(){const{service:i}=await e.prompt([{type:"list",name:"service",message:a.t("auth.service_prompt"),choices:[{name:a.t("wizard.plan_global"),value:"goffy_coding"}]}]),{token:n}=await e.prompt([{type:"password",name:"token",message:a.t("auth.token_prompt"),validate:e=>!(!e||0===e.trim().length)||a.t("auth.token_required")}]),s=o({text:a.t("wizard.validating_api_key"),spinner:"dots"}).start(),l=await c(n.trim(),i);l.valid?(s.succeed(r.green(a.t("wizard.api_key_valid"))),t.setPlan(i),t.setApiKey(n.trim()),console.log(r.green(a.t("auth.saved")))):"invalid_api_key"===l.error?s.fail(r.red(a.t("wizard.api_key_invalid"))):s.fail(r.red(a.t("wizard.api_key_network_error")))}()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import l from"inquirer";import o from"chalk";import{wizard as e}from"../lib/wizard.js";import{toolManager as a,SUPPORTED_TOOLS as t}from"../lib/tool-manager.js";import{i18n as s}from"../lib/i18n.js";export async function configCommand(i){const n=i[0],r=i[1];if(!n){const{selectedTool:i}=await l.prompt([{type:"list",name:"selectedTool",message:s.t("wizard.select_tool"),choices:Object.values(t).map(l=>({name:`${l.displayName} (${l.name})`,value:l.name}))}]);if(!a.isToolInstalled(i)){console.log(o.yellow("\n"+s.t("wizard.tool_not_installed",{tool:t[i].displayName})));const{shouldInstall:e}=await l.prompt([{type:"confirm",name:"shouldInstall",message:s.t("wizard.install_tool_confirm"),default:!0}]);if(!e)return void console.log(o.yellow(s.t("wizard.install_skipped")));try{await a.installTool(i),await new Promise(l=>setTimeout(l,600))}catch(e){console.error(o.red(s.t("install.install_failed_detail"))),e instanceof Error&&e.message&&console.error(o.gray(e.message)),await new Promise(l=>setTimeout(l,600));const{skipInstall:a}=await l.prompt([{type:"list",name:"skipInstall",message:s.t("install.skip_install_confirm"),choices:[{name:s.t("install.skip_install_yes"),value:!0},{name:s.t("install.skip_install_no"),value:!1}]}]);if(!a)return}}return void await e.showToolMenu(i)}if("claude-code"!==n&&"claude-code"!==r){if(t[n])return a.isToolInstalled(n)?void await e.showToolMenu(n):void console.log(o.yellow(s.t("wizard.tool_not_installed",{tool:t[n].displayName})));console.error(o.red("Unknown command: "+n)),console.log(o.gray("Usage: chelper config [tool-name]")),console.log(o.gray("Available tools:")),Object.values(t).forEach(l=>{console.log(o.gray(` - ${l.name}: ${l.displayName}`))})}else{if(!a.isToolInstalled("claude-code")){console.log(o.yellow(s.t("wizard.tool_not_installed",{tool:"Claude Code"})));const{shouldInstall:e}=await l.prompt([{type:"confirm",name:"shouldInstall",message:s.t("wizard.install_tool_confirm"),default:!0}]);if(!e)return void console.log(o.yellow(s.t("wizard.install_skipped")));try{await a.installTool("claude-code"),await new Promise(l=>setTimeout(l,600))}catch(e){console.error(o.red(s.t("install.install_failed_detail"))),e instanceof Error&&e.message&&console.error(o.gray(e.message)),await new Promise(l=>setTimeout(l,600));const{skipInstall:a}=await l.prompt([{type:"list",name:"skipInstall",message:s.t("install.skip_install_confirm"),choices:[{name:s.t("install.skip_install_yes"),value:!0},{name:s.t("install.skip_install_no"),value:!1}]}]);if(!a)return}}await e.showToolMenu("claude-code")}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import o from"chalk";import e from"ora";import{configManager as t}from"../lib/config.js";import{toolManager as s}from"../lib/tool-manager.js";import{claudeCodeManager as n}from"../lib/claude-code-manager.js";import{i18n as a}from"../lib/i18n.js";import{validateApiKey as l}from"../lib/api-validator.js";export async function doctorCommand(){const i=e(a.t("doctor.checking")).start(),r=[];i.text=a.t("doctor.check_path");try{const o=process.env.PATH||"";r.push({name:"PATH",passed:o.length>0,message:o.length>0?void 0:"PATH is empty"})}catch(o){r.push({name:"PATH",passed:!1,message:o+""})}await new Promise(o=>setTimeout(o,800)),i.text=a.t("doctor.check_api_key_network");const c=t.getApiKey(),d=t.getPlan();if(c&&d){const e=await l(c,d);e.valid?r.push({name:"API Key & Network",passed:!0}):"invalid_api_key"===e.error?r.push({name:"API Key & Network",passed:!1,message:o.yellow(a.t("doctor.api_key_invalid"))}):r.push({name:"API Key & Network",passed:!1,message:o.yellow(a.t("doctor.network_error"))})}else r.push({name:"API Key & Network",passed:!1,message:o.yellow(a.t("doctor.api_key_missing"))});if(await new Promise(o=>setTimeout(o,800)),"win32"===process.platform&&s.isToolInstalled("claude-code")){i.text=a.t("doctor.check_git_env");const e=s.isGitInstalled();r.push({name:"Git Environment",passed:e,message:e?void 0:o.yellow(a.t("doctor.git_not_installed"))}),await new Promise(o=>setTimeout(o,800))}i.text=a.t("doctor.check_gff_plan");const m=n.detectCurrentConfig().plan||t.getPlan(),g=!!m;let p="";if(g&&m){const o="Goffy Coding Plan";p=a.t("doctor.gff_plan_configured",{plan:o})}else p=a.t("doctor.gff_plan_not_configured");r.push({name:"Goffy Coding Plan",passed:g,message:g?void 0:o.yellow(p)}),await new Promise(o=>setTimeout(o,800)),i.text=a.t("doctor.check_tools");const f=s.getSupportedTools();for(const e of f){const t=s.isToolInstalled(e.name);r.push({name:`Tool: ${e.displayName} (${e.name})`,passed:t,message:t?void 0:o.yellow(a.t("doctor.tool_not_found",{tool:e.displayName}))})}await new Promise(o=>setTimeout(o,800)),i.stop(),console.log(o.cyan.bold("\n=== Health Check Results ===\n"));let u=!0;for(const e of r){const t=e.passed?o.green("✓"):o.red("✗");console.log(`${t} ${e.name}`),e.message&&console.log(" "+o.gray(e.message)),e.passed||(u=!1)}console.log(),u?console.log(o.green.bold(a.t("doctor.all_good"))):(console.log(o.gray("\n"+a.t("doctor.suggestions")+":")),console.log(o.gray('- Run "chelper init" to configure missing settings')),console.log(o.gray("- Check your network connection")),console.log(o.gray("- Ensure required tools are installed")))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{langCommand}from"./lang.js";export{authCommand}from"./auth.js";export{doctorCommand}from"./doctor.js";export{configCommand}from"./config.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{configManager as o}from"../lib/config.js";import{i18n as n}from"../lib/i18n.js";import l from"chalk";export async function langCommand(e){if(0===e.length||"show"===e[0]){const e=o.getLang();return console.log(`${n.t("lang.current")}: ${l.cyan(e)}`),void console.log(`${n.t("lang.available")}: ${n.getAvailableLocales().join(", ")}`)}if("set"===e[0]&&e.length>=2){const a=e[1],g=n.getAvailableLocales();if(!g.includes(a))return console.error(l.red(n.t("lang.invalid",{lang:a}))),void console.log(`${n.t("lang.available")}: ${g.join(", ")}`);const t=o.getLang();return o.setLang(a),n.loadFromConfig(a),void console.log(l.green(n.t("lang.changed",{from:t,to:a})))}console.log(n.t("lang.show_usage")),console.log(n.t("lang.set_usage"))}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./lib/command.js";export*from"./lib/config.js";export*from"./lib/i18n.js";export*from"./lib/wizard.js";export*from"./lib/tool-manager.js";export*from"./lib/mcp-manager.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{logger as e}from"../utils/logger.js";export async function validateApiKey(r,t){try{const e=new AbortController,t=setTimeout(()=>e.abort(),3e4),o=await fetch("https://api.goffy.tech/v1/usage",{method:"GET",headers:{Authorization:"Bearer "+r,"Content-Type":"application/json"},signal:e.signal});if(clearTimeout(t),401===o.status)return{valid:!1,error:"invalid_api_key",message:"API Key is invalid or expired"};if(o.ok)try{return await o.json(),{valid:!0}}catch{return{valid:!0}}return{valid:!1,error:"unknown_error",message:`HTTP ${o.status}: ${o.statusText}`}}catch(r){return e.logError("validateApiKey",r),r instanceof Error?"AbortError"===r.name?{valid:!1,error:"network_error",message:"Request timeout"}:{valid:!1,error:"network_error",message:r.message}:{valid:!1,error:"network_error",message:"Network connection failed"}}}export async function checkUsage(r){try{const e=new AbortController,t=setTimeout(()=>e.abort(),3e4),o=await fetch("https://api.goffy.tech/v1/usage",{method:"GET",headers:{Authorization:"Bearer "+r,"Content-Type":"application/json"},signal:e.signal});return clearTimeout(t),401===o.status?{success:!1,error:"invalid_api_key",message:"API Key is invalid or expired"}:o.ok?{success:!0,data:await o.json()}:{success:!1,error:"http_error",message:`HTTP ${o.status}: ${o.statusText}`}}catch(r){return e.logError("checkUsage",r),r instanceof Error?"AbortError"===r.name?{success:!1,error:"timeout",message:"Request timeout"}:{success:!1,error:"network_error",message:r.message}:{success:!1,error:"network_error",message:"Network connection failed"}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync as e,mkdirSync as t,readFileSync as r,writeFileSync as n}from"fs";import{join as s,dirname as o}from"path";import{homedir as a}from"os";import{logger as i}from"../utils/logger.js";export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;constructor(){this.settingsPath=s(a(),".claude","settings.json"),this.mcpConfigPath=s(a(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(r){const n=o(r);e(n)||t(n,{recursive:!0})}getSettings(){try{if(e(this.settingsPath)){const e=r(this.settingsPath,"utf-8");return JSON.parse(e)}}catch(e){console.warn("Failed to read Claude Code settings:",e),i.logError("ClaudeCodeManager.getSettings",e)}return{}}saveSettings(e){try{this.ensureConfigDir(this.settingsPath),n(this.settingsPath,JSON.stringify(e,null,2),"utf-8")}catch(e){throw Error("Failed to save Claude Code settings: "+e)}}getMCPConfig(){try{if(e(this.mcpConfigPath)){const e=r(this.mcpConfigPath,"utf-8");return JSON.parse(e)}}catch(e){console.warn("Failed to read Claude Code MCP config:",e),i.logError("ClaudeCodeManager.getMCPConfig",e)}return{}}saveMCPConfig(e){try{this.ensureConfigDir(this.mcpConfigPath),n(this.mcpConfigPath,JSON.stringify(e,null,2),"utf-8")}catch(e){throw Error("Failed to save Claude Code MCP config: "+e)}}loadGFFConfig(e,t){this.ensureOnboardingCompleted(),this.cleanupShellEnvVars();const r=this.getSettings(),n=r.env||{},{ANTHROPIC_API_KEY:s,...o}=n,a={...r,env:{...o,ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:"https://api.goffy.tech",API_TIMEOUT_MS:"3000000",CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:1,ANTHROPIC_DEFAULT_HAIKU_MODEL:"goffy_flash-1.0",ANTHROPIC_DEFAULT_SONNET_MODEL:"goffy-code-1.0",ANTHROPIC_DEFAULT_OPUS_MODEL:"goffy-code-1.0"}};this.saveSettings(a)}ensureOnboardingCompleted(){try{const e=this.getMCPConfig();e.hasCompletedOnboarding||this.saveMCPConfig({...e,hasCompletedOnboarding:!0})}catch(e){console.warn("Failed to ensure onboarding completed:",e),i.logError("ClaudeCodeManager.ensureOnboardingCompleted",e)}}cleanupShellEnvVars(){if(process.env.ANTHROPIC_API_KEY||process.env.ANTHROPIC_BASE_URL)try{const t=this.getShellRcFilePath();if(!t||!e(t))return;let s=r(t,"utf-8");const o=s,a=[/^\s*export\s+ANTHROPIC_BASE_URL=.*$/gm,/^\s*export\s+ANTHROPIC_API_KEY=.*$/gm,/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of a)s=s.replace(e,"");s!==o&&(n(t,s,"utf-8"),console.log("Cleaned up ANTHROPIC_* environment variables from "+t),setTimeout(()=>{},1e3))}catch(e){console.warn("Failed to cleanup shell environment variables:",e),i.logError("ClaudeCodeManager.cleanupShellEnvVars",e),setTimeout(()=>{},1e3)}}getShellRcFilePath(){const e=a();if("win32"===process.platform)return null;switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return s(e,".bashrc");case"zsh":return s(e,".zshrc");case"fish":return s(e,".config","fish","config.fish");default:return s(e,".profile")}}unloadGFFConfig(){const e=this.getSettings();if(!e.env)return;const{ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:r,API_TIMEOUT_MS:n,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:s,ANTHROPIC_DEFAULT_HAIKU_MODEL:o,ANTHROPIC_DEFAULT_SONNET_MODEL:a,ANTHROPIC_DEFAULT_OPUS_MODEL:i,...l}=e.env,c={...e,env:l};c.env&&0===Object.keys(c.env).length&&delete c.env,this.saveSettings(c)}isMCPInstalled(e){try{const t=this.getMCPConfig().mcpServers;return!!t&&e in t}catch{return!1}}installMCP(e,t,r){try{const n=this.getMCPConfig(),s=n.mcpServers||{};let o;if("stdio"===e.protocol){let n={};e.envTemplate&&r?n={...e.envTemplate[r]||{}}:e.env&&(n={...e.env}),e.requiresAuth&&t&&(n.GOFFY_API_KEY=t),o={type:"stdio",command:e.command||"npx",args:e.args||[],env:n}}else{if("sse"!==e.protocol&&"streamable-http"!==e.protocol)throw Error("Unsupported protocol: "+e.protocol);{let n="";if(e.urlTemplate&&r)n=e.urlTemplate[r]||"";else{if(!e.url)throw Error(`MCP ${e.id} missing url or urlTemplate`);n=e.url}o={type:"sse"==e.protocol?"sse":"http",url:n,headers:{...e.headers||{}}},e.requiresAuth&&t&&(o.headers.Authorization="Bearer "+t)}}s[e.id]=o,n.mcpServers=s,this.saveMCPConfig(n)}catch(t){throw Error(`Failed to install MCP ${e.name}: ${t}`)}}uninstallMCP(e){try{const t=this.getMCPConfig(),r=t.mcpServers;if(!r)return;delete r[e],this.saveMCPConfig(t)}catch(t){throw Error(`Failed to uninstall MCP ${e}: ${t}`)}}getInstalledMCPs(){try{const e=this.getMCPConfig().mcpServers;return e?Object.keys(e):[]}catch{return[]}}getMCPStatus(e){const t=new Map;for(const r of e)t.set(r.id,this.isMCPInstalled(r.id));return t}getOtherMCPs(e){try{const t=this.getMCPConfig().mcpServers;if(!t)return[];const r=[];for(const[n,s]of Object.entries(t))e.includes(n)||r.push({id:n,config:s});return r}catch{return[]}}getAllMCPServers(){try{return this.getMCPConfig().mcpServers||{}}catch{return{}}}detectCurrentConfig(){try{const e=this.getSettings().env||{};if(!e.ANTHROPIC_AUTH_TOKEN)return{plan:null,apiKey:null};const t=e.ANTHROPIC_AUTH_TOKEN,r=e.ANTHROPIC_BASE_URL;let n=null;return"https://api.goffy.tech"===r&&(n="goffy_coding"),{plan:n,apiKey:t}}catch{return{plan:null,apiKey:null}}}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Command as a}from"commander";import{i18n as e}from"./i18n.js";import{configManager as n}from"./config.js";import{wizard as t}from"./wizard.js";import{langCommand as r,authCommand as o,doctorCommand as i,configCommand as c}from"../commands/index.js";import s from"chalk";import{readFileSync as m}from"fs";import{join as g,dirname as p}from"path";import{fileURLToPath as l}from"url";export class Command{program;constructor(){const t=n.getLang();e.loadFromConfig(t),this.program=new a,this.setupProgram()}getVersion(){try{const a=l(import.meta.url),e=p(a),n=g(e,"../../package.json");return JSON.parse(m(n,"utf-8")).version||"1.0.0"}catch{return"1.0.0"}}setupProgram(){this.program.name("chelper").description(e.t("cli.title")).version(this.getVersion(),"-v, --version",e.t("commands.version")).helpOption("-h, --help",e.t("commands.help")),this.program.command("init").description(e.t("commands.init")).action(async()=>{await this.handleInitCommand()});const a=this.program.command("lang").description(e.t("commands.lang"));a.command("show").description(e.t("lang.show_usage")).action(async()=>{await r(["show"])}),a.command("set <locale>").description(e.t("lang.set_usage")).action(async a=>{await r(["set",a])});const m=this.program.command("auth").description(e.t("commands.auth"));m.argument("[service]","Service type: goffy_coding").argument("[token]","API token").action(async(a,e)=>{const n=[];a&&n.push(a),e&&n.push(e),await o(n)}),m.command("revoke").description("Revoke saved API key").action(async()=>{await o(["revoke"])}),m.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async a=>{await o(["reload",a])}),m.command("usage").description("Check API usage and quota").action(async()=>{await o(["usage"])}),this.program.command("doctor").description(e.t("commands.doctor")).action(async()=>{await i()}),this.program.command("enter [option]").description(e.t("commands.enter")).action(async a=>{if(a)switch(a){case"lang":case"language":await t.configLanguage();break;case"plan":await t.configPlan();break;case"apikey":case"api-key":await t.configApiKey();break;default:{const e=[a];await c(e);break}}else await t.showMainMenu()}),this.program.action(async()=>{n.isFirstRun()?(console.log(s.cyan(e.t("messages.first_run"))),await t.runFirstTimeSetup()):await t.showMainMenu()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:a=>a.name()+" "+a.usage()}),this.program.addHelpText("after",`\n${s.bold(e.t("cli.examples"))}:\n ${s.gray("$ chelper # Interactive main menu")}\n ${s.gray("$ chelper init # Run first-time setup wizard")}\n ${s.gray("$ chelper enter # Interactive main menu")}\n ${s.gray("$ chelper enter lang # Interactive language configuration")}\n ${s.gray("$ chelper enter plan # Interactive plan configuration")}\n ${s.gray("$ chelper enter apikey # Interactive API key configuration")}\n ${s.gray("$ chelper enter claude-code # Interactive Configure Claude Code tool")}\n ${s.gray("$ chelper lang show # Show current language")}\n ${s.gray("$ chelper lang set en_US")}\n ${s.gray("$ chelper auth # Interactive auth setup")}\n ${s.gray("$ chelper auth goffy_coding <token> # Set API key for goffy coding plan")}\n ${s.gray("$ chelper auth revoke")}\n ${s.gray("$ chelper auth reload claude # Reload plan config to Claude Code")}\n ${s.gray("$ chelper auth usage # Check API usage and quota")}\n ${s.gray("$ chelper doctor # Health check")}\n`)}async handleInitCommand(){await t.runFirstTimeSetup()}async execute(a){try{await this.program.parseAsync(a,{from:"user"})}catch(a){a instanceof Error&&console.error(s.red(e.t("cli.error_general")),a.message),process.exit(1)}}getProgram(){return this.program}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync as i,mkdirSync as n,readFileSync as t,writeFileSync as o}from"fs";import{join as g}from"path";import{homedir as a}from"os";import*as e from"js-yaml";import{logger as r}from"../utils/logger.js";export class ConfigManager{static instance;configDir;configPath;config;constructor(){this.configDir=g(a(),".chelper"),this.configPath=g(this.configDir,"config.yaml"),this.config=this.loadConfig()}static getInstance(){return ConfigManager.instance||(ConfigManager.instance=new ConfigManager),ConfigManager.instance}ensureConfigDir(){i(this.configDir)||n(this.configDir,{recursive:!0})}loadConfig(){try{if(i(this.configPath)){const i=t(this.configPath,"utf-8");return e.load(i)||{lang:"en_US"}}}catch(i){console.warn("Failed to load config, using defaults:",i),r.logError("ConfigManager.loadConfig",i)}return{lang:"en_US"}}saveConfig(i){try{this.ensureConfigDir();const n=i||this.config,t=e.dump(n);o(this.configPath,t,"utf-8"),this.config=n}catch(i){throw console.error("Failed to save config:",i),r.logError("ConfigManager.saveConfig",i),i}}getConfig(){return{...this.config}}updateConfig(i){this.config={...this.config,...i},this.saveConfig()}isFirstRun(){return!i(this.configPath)}getLang(){return this.config.lang||"en_US"}setLang(i){this.updateConfig({lang:i})}getPlan(){return this.config.plan}setPlan(i){this.updateConfig({plan:i})}getApiKey(){return this.config.api_key}setApiKey(i){this.updateConfig({api_key:i})}revokeApiKey(){this.updateConfig({api_key:void 0})}}export const configManager=ConfigManager.getInstance();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync as t,mkdirSync as r,readFileSync as e,writeFileSync as n}from"fs";import{join as o,dirname as s}from"path";import{homedir as i}from"os";import{logger as a}from"../utils/logger.js";export class CrushManager{static instance;configPath;constructor(){this.configPath=o(i(),".config","crush","crush.json")}static getInstance(){return CrushManager.instance||(CrushManager.instance=new CrushManager),CrushManager.instance}ensureConfigDir(){const e=s(this.configPath);t(e)||r(e,{recursive:!0})}getConfig(){try{if(t(this.configPath)){const t=e(this.configPath,"utf-8");return JSON.parse(t)}}catch(t){console.warn("Failed to read Crush config:",t),a.logError("CrushManager.getConfig",t)}return{}}saveConfig(t){try{this.ensureConfigDir(),n(this.configPath,JSON.stringify(t,null,2),"utf-8")}catch(t){throw Error("Failed to save Crush config: "+t)}}getBaseUrl(t){return"https://api.goffy.tech/v1"}loadGFFConfig(t,r){const e=this.getConfig(),n=this.getBaseUrl(t),o={...e,providers:{...e.providers||{},goffy:{id:"goffy",name:"Goffy Provider",base_url:n,api_key:r}}};this.saveConfig(o)}unloadGFFConfig(){const t=this.getConfig();t.providers&&(delete t.providers.goffy,0===Object.keys(t.providers).length&&delete t.providers),this.saveConfig(t)}isMCPInstalled(t){try{const r=this.getConfig().mcp;return!!r&&t in r}catch{return!1}}installMCP(t,r,e){try{const n=this.getConfig(),o=n.mcp||{};let s;if("stdio"===t.protocol){let n={};t.envTemplate&&e?n={...t.envTemplate[e]||{}}:t.env&&(n={...t.env}),t.requiresAuth&&r&&(n.GOFFY_API_KEY=r),s={type:"stdio",command:t.command||"npx",args:t.args||[],env:n}}else{if("sse"!==t.protocol&&"streamable-http"!==t.protocol)throw Error("Unsupported protocol: "+t.protocol);{let n="";if(t.urlTemplate&&e)n=t.urlTemplate[e]||"";else{if(!t.url)throw Error(`MCP ${t.id} missing url or urlTemplate`);n=t.url}s={type:"sse"===t.protocol?"sse":"http",url:n,headers:{...t.headers||{}}},t.requiresAuth&&r&&((s.headers||{}).Authorization="Bearer "+r)}}o[t.id]=s,n.mcp=o,this.saveConfig(n)}catch(r){throw Error(`Failed to install MCP ${t.name}: ${r}`)}}uninstallMCP(t){try{const r=this.getConfig(),e=r.mcp;if(!e)return;delete e[t],this.saveConfig(r)}catch(r){throw Error(`Failed to uninstall MCP ${t}: ${r}`)}}getInstalledMCPs(){try{const t=this.getConfig().mcp;return t?Object.keys(t):[]}catch{return[]}}getMCPStatus(t){const r=new Map;for(const e of t)r.set(e.id,this.isMCPInstalled(e.id));return r}getOtherMCPs(t){try{const r=this.getConfig().mcp;if(!r)return[];const e=[];for(const[n,o]of Object.entries(r))t.includes(n)||e.push({id:n,config:o});return e}catch{return[]}}getAllMCPServers(){try{return this.getConfig().mcp||{}}catch{return{}}}detectCurrentConfig(){try{const t=this.getConfig().providers;if(!t||!t.goffy)return{plan:null,apiKey:null};return{plan:"goffy_coding",apiKey:t.goffy.api_key||null}}catch{return{plan:null,apiKey:null}}}}export const crushManager=CrushManager.getInstance();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync as t,mkdirSync as e,readFileSync as r,writeFileSync as o}from"fs";import{join as n,dirname as i}from"path";import{homedir as s}from"os";import{logger as a}from"../utils/logger.js";export class FactoryDroidManager{static instance;configPath;mcpConfigPath;constructor(){this.configPath=n(s(),".factory","config.json"),this.mcpConfigPath=n(s(),".factory","mcp.json")}static getInstance(){return FactoryDroidManager.instance||(FactoryDroidManager.instance=new FactoryDroidManager),FactoryDroidManager.instance}ensureConfigDir(){const r=i(this.configPath);t(r)||e(r,{recursive:!0})}getConfig(){try{if(t(this.configPath)){const t=r(this.configPath,"utf-8");return JSON.parse(t)}}catch(t){console.warn("Failed to read Factory Droid config:",t),a.logError("FactoryDroidManager.getConfig",t)}return{}}saveConfig(t){try{this.ensureConfigDir(),o(this.configPath,JSON.stringify(t,null,2),"utf-8")}catch(t){throw Error("Failed to save Factory Droid config: "+t)}}getMCPConfig(){try{if(t(this.mcpConfigPath)){const t=r(this.mcpConfigPath,"utf-8");return JSON.parse(t)}}catch(t){console.warn("Failed to read Factory Droid MCP config:",t),a.logError("FactoryDroidManager.getMCPConfig",t)}return{}}saveMCPConfig(t){try{this.ensureConfigDir(),o(this.mcpConfigPath,JSON.stringify(t,null,2),"utf-8")}catch(t){throw Error("Failed to save Factory Droid MCP config: "+t)}}getBaseUrl(t){return"https://api.goffy.tech/api/coding/paas/v4"}getModelDisplayName(t){return"Goffy Coding Plan"}loadGFFConfig(t,e){const r=this.getConfig(),o=this.getBaseUrl(t),n={model_display_name:this.getModelDisplayName(t),model:"goffy-code-1.0",base_url:o,api_key:e,provider:"generic-chat-completion-api",max_tokens:131072},i=(r.custom_models||[]).filter(t=>!t.model_display_name?.includes("Goffy Coding Plan")),s={...r,custom_models:[...i,n]};this.saveConfig(s)}unloadGFFConfig(){const t=this.getConfig();t.custom_models&&(t.custom_models=t.custom_models.filter(t=>!t.model_display_name?.includes("Goffy Coding Plan")),0===t.custom_models.length&&delete t.custom_models),this.saveConfig(t)}isMCPInstalled(t){try{const e=this.getMCPConfig().mcpServers;return!!e&&t in e}catch{return!1}}installMCP(t,e,r){try{const o=this.getMCPConfig(),n=o.mcpServers||{};let i;if("stdio"===t.protocol){let o={};t.envTemplate&&r?o={...t.envTemplate[r]||{}}:t.env&&(o={...t.env}),t.requiresAuth&&e&&(o.GOFFY_API_KEY=e),i={type:"stdio",command:t.command||"npx",args:t.args||[],env:o,disabled:!1}}else{if("sse"!==t.protocol&&"streamable-http"!==t.protocol)throw Error("Unsupported protocol: "+t.protocol);{let o="";if(t.urlTemplate&&r)o=t.urlTemplate[r]||"";else{if(!t.url)throw Error(`MCP ${t.id} missing url or urlTemplate`);o=t.url}i={type:"http",url:o,headers:{...t.headers||{}},disabled:!1},t.requiresAuth&&e&&((i.headers||{}).Authorization="Bearer "+e)}}n[t.id]=i,o.mcpServers=n,this.saveMCPConfig(o)}catch(e){throw Error(`Failed to install MCP ${t.name}: ${e}`)}}uninstallMCP(t){try{const e=this.getMCPConfig(),r=e.mcpServers;if(!r)return;delete r[t],this.saveMCPConfig(e)}catch(e){throw Error(`Failed to uninstall MCP ${t}: ${e}`)}}getInstalledMCPs(){try{const t=this.getMCPConfig().mcpServers;return t?Object.keys(t):[]}catch{return[]}}getMCPStatus(t){const e=new Map;for(const r of t)e.set(r.id,this.isMCPInstalled(r.id));return e}getOtherMCPs(t){try{const e=this.getMCPConfig().mcpServers;if(!e)return[];const r=[];for(const[o,n]of Object.entries(e))t.includes(o)||r.push({id:o,config:n});return r}catch{return[]}}getAllMCPServers(){try{return this.getMCPConfig().mcpServers||{}}catch{return{}}}detectCurrentConfig(){try{const t=this.getConfig().custom_models;if(!t||0===t.length)return{plan:null,apiKey:null};const e=t.find(t=>t.model_display_name?.includes("Goffy Coding Plan"));return e?{plan:"goffy_coding",apiKey:e.api_key||null}:{plan:null,apiKey:null}}catch{return{plan:null,apiKey:null}}}}export const factoryDroidManager=FactoryDroidManager.getInstance();
|
package/dist/lib/i18n.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as t,existsSync as s,readdirSync as e}from"fs";import{join as o,dirname as a}from"path";import{fileURLToPath as n}from"url";class r{static instance;currentLocale="en_US";translations=new Map;fallbackLocale="en_US";localesDir;constructor(t){const s=n(import.meta.url),e=a(s);this.localesDir=o(e,"..","locales"),this.loadTranslations(),this.loadSavedLocale(t?.locale)}static getInstance(t){return r.instance||(r.instance=new r(t)),r.instance}loadTranslations(){try{if(!s(this.localesDir))return void console.warn("Locales directory not found at "+this.localesDir);const a=e(this.localesDir).filter(t=>t.endsWith(".json"));if(0===a.length)return void console.warn("No locale files found in locales directory");for(const s of a){const e=s.replace(".json",""),a=o(this.localesDir,s);try{const s=t(a,"utf-8"),o=JSON.parse(s);this.translations.set(e,o)}catch(t){console.warn(`Failed to load locale file ${s}:`,t)}}const n=process.env.LANG||process.env.LC_ALL||process.env.LC_MESSAGES;if(n){const t=n.split(".")[0].replace("_","-");if(this.translations.has(t))this.currentLocale=t;else{const s=t.split("-")[0],e=Array.from(this.translations.keys()).find(t=>t.startsWith(s+"_"));e?this.currentLocale=e:"en"===s&&(this.currentLocale="en_US")}}}catch(t){console.warn("Failed to load translations:",t)}}setLocale(t){this.translations.has(t)?(this.currentLocale=t,this.saveLocale()):console.warn(`Locale '${t}' not supported, falling back to '${this.currentLocale}'`)}getLocale(){return this.currentLocale}getAvailableLocales(){return Array.from(this.translations.keys())}translate(t,s){const e=t.split(".");let o=this.translations.get(this.currentLocale);for(const s of e){if(!o||"object"!=typeof o||!(s in o)){o=this.translations.get(this.fallbackLocale);for(const s of e){if(!o||"object"!=typeof o||!(s in o))return t;o=o[s]}break}o=o[s]}return"string"!=typeof o?t:s?o.replace(/\{\{(\w+)\}\}/g,(t,e)=>(s[e]??t)+""):o}t(t,s){return this.translate(t,s)}saveLocale(){}loadSavedLocale(t){t&&this.translations.has(t)&&(this.currentLocale=t)}loadFromConfig(t){this.translations.has(t)&&(this.currentLocale=t)}}export const i18n=r.getInstance();export{i18n as I18n};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{toolManager as e}from"./tool-manager.js";import{claudeCodeManager as r}from"./claude-code-manager.js";import{openCodeManager as t}from"./opencode-manager.js";import{crushManager as o}from"./crush-manager.js";import{factoryDroidManager as n}from"./factory-droid-manager.js";export const PRESET_MCP_SERVICES=[{id:"goffy-mcp-server",name:"Vision MCP",type:"builtin",protocol:"stdio",requiresAuth:!0,description:"Vision MCP Local Server",command:"npx",args:["-y","@goffy-ai/mcp-server"],envTemplate:{goffy_coding:{GOFFY_MODE:"GOFFY"}}},{id:"web-search-prime",name:"Web Search MCP",type:"builtin",protocol:"streamable-http",requiresAuth:!0,description:"Web Search Prime MCP Server",urlTemplate:{goffy_coding:"https://api.goffy.tech/api/mcp/web_search_prime/mcp"}},{id:"web-reader",name:"Web Reader MCP",type:"builtin",protocol:"streamable-http",requiresAuth:!0,description:"Web URL Reader MCP Server",urlTemplate:{goffy_coding:"https://api.goffy.tech/api/mcp/web_reader/mcp"}},{id:"zread",name:"ZRead MCP",type:"builtin",protocol:"streamable-http",requiresAuth:!0,description:"ZRead Github MCP Server",urlTemplate:{goffy_coding:"https://api.goffy.tech/api/mcp/zread/mcp"}}];export class MCPManager{static instance;constructor(){}static getInstance(){return MCPManager.instance||(MCPManager.instance=new MCPManager),MCPManager.instance}getPresetServices(){return[...PRESET_MCP_SERVICES]}isMCPInstalled(a,i){try{if("claude-code"===a)return r.isMCPInstalled(i);if("opencode"===a)return t.isMCPInstalled(i);if("crush"===a)return o.isMCPInstalled(i);if("factory-droid"===a)return n.isMCPInstalled(i);const s=e.getToolConfig(a);return!(!s||!s.mcpServers)&&i in s.mcpServers}catch{return!1}}installMCP(a,i,s,c){try{if("claude-code"===a)return void r.installMCP(i,s,c);if("opencode"===a)return void t.installMCP(i,s,c);if("crush"===a)return void o.installMCP(i,s,c);if("factory-droid"===a)return void n.installMCP(i,s,c);const l=e.getToolConfig(a)||{},d=l.mcpServers||{};if("stdio"===i.protocol){let e={};i.envTemplate&&c?e={...i.envTemplate[c]||{}}:i.env&&(e={...i.env}),i.requiresAuth&&s&&(e.GOFFY_API_KEY=s),d[i.id]={type:"stdio",command:i.command,args:i.args,env:e}}else if("sse"===i.protocol||"streamable-http"===i.protocol){let e="";if(i.urlTemplate&&c)e=i.urlTemplate[c];else{if(!i.url)throw Error(`MCP ${i.name} requires a URL but none was provided.`);e=i.url}d[i.id]={type:i.protocol,url:e,headers:{...i.headers||{},...s&&i.requiresAuth?{Authorization:"Bearer "+s}:{}}}}l.mcpServers=d,e.updateToolConfig(a,l)}catch(e){throw Error(`Failed to install MCP ${i.name}: ${e}`)}}uninstallMCP(a,i){try{if("claude-code"===a)return void r.uninstallMCP(i);if("opencode"===a)return void t.uninstallMCP(i);if("crush"===a)return void o.uninstallMCP(i);if("factory-droid"===a)return void n.uninstallMCP(i);const s=e.getToolConfig(a);if(!s||!s.mcpServers)return;delete s.mcpServers[i],e.updateToolConfig(a,s)}catch(e){throw Error(`Failed to uninstall MCP ${i}: ${e}`)}}getInstalledMCPs(a){try{if("claude-code"===a)return r.getInstalledMCPs();if("opencode"===a)return t.getInstalledMCPs();if("crush"===a)return o.getInstalledMCPs();if("factory-droid"===a)return n.getInstalledMCPs();const i=e.getToolConfig(a);return i&&i.mcpServers?Object.keys(i.mcpServers):[]}catch{return[]}}getMCPStatus(e){if("claude-code"===e)return r.getMCPStatus(PRESET_MCP_SERVICES);if("opencode"===e)return t.getMCPStatus(PRESET_MCP_SERVICES);if("crush"===e)return o.getMCPStatus(PRESET_MCP_SERVICES);if("factory-droid"===e)return n.getMCPStatus(PRESET_MCP_SERVICES);const a=new Map;for(const r of PRESET_MCP_SERVICES)a.set(r.id,this.isMCPInstalled(e,r.id));return a}}export const mcpManager=MCPManager.getInstance();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as o}from"fs";import{join as r,dirname as i}from"path";import{homedir as s}from"os";import{logger as a}from"../utils/logger.js";export class OpenCodeManager{static instance;configPath;constructor(){this.configPath=r(s(),".config","opencode","opencode.json")}static getInstance(){return OpenCodeManager.instance||(OpenCodeManager.instance=new OpenCodeManager),OpenCodeManager.instance}ensureConfigDir(){const n=i(this.configPath);e(n)||t(n,{recursive:!0})}getConfig(){try{if(e(this.configPath)){const e=n(this.configPath,"utf-8");return JSON.parse(e)}}catch(e){console.warn("Failed to read OpenCode config:",e),a.logError("OpenCodeManager.getConfig",e)}return{}}saveConfig(e){try{this.ensureConfigDir(),o(this.configPath,JSON.stringify(e,null,4),"utf-8")}catch(e){throw Error("Failed to save OpenCode config: "+e)}}getProviderName(e){return"goffy-coding-plan"}loadGFFConfig(e,t){const n=this.getConfig(),o=this.getProviderName(e),{provider:r,...i}=n,s={};if(r)for(const[e,t]of Object.entries(r))"goffy-coding-plan"!==e&&(s[e]=t);s[o]={options:{apiKey:t}};const a={$schema:"https://opencode.ai/config.json",...i,provider:s,model:o+"/goffy-code-1.0",small_model:o+"/goffy-flash-1.0"};this.saveConfig(a)}unloadGFFConfig(){const e=this.getConfig();e.provider&&(delete e.provider["goffy-coding-plan"],0===Object.keys(e.provider).length&&delete e.provider),e.model?.includes("coding-plan")&&delete e.model,e.small_model?.includes("coding-plan")&&delete e.small_model,this.saveConfig(e)}isMCPInstalled(e){try{const t=this.getConfig().mcp;return!!t&&e in t}catch{return!1}}installMCP(e,t,n){try{const o=this.getConfig(),r=o.mcp||{};let i;if("stdio"===e.protocol){let o={};e.envTemplate&&n?o={...e.envTemplate[n]||{}}:e.env&&(o={...e.env}),e.requiresAuth&&t&&(o.GOFFY_API_KEY=t),i={type:"local",command:[e.command||"npx",...e.args||[]],environment:o}}else{if("streamable-http"!==e.protocol)throw Error("Unsupported protocol: "+e.protocol);{let o="";if(e.urlTemplate&&n)o=e.urlTemplate[n]||"";else{if(!e.url)throw Error(`MCP ${e.id} missing url or urlTemplate`);o=e.url}i={type:"remote",url:o,headers:{...e.headers||{}}},e.requiresAuth&&t&&((i.headers||{}).Authorization="Bearer "+t)}}r[e.id]=i,o.mcp=r,this.saveConfig(o)}catch(t){throw Error(`Failed to install MCP ${e.name}: ${t}`)}}uninstallMCP(e){try{const t=this.getConfig(),n=t.mcp;if(!n)return;delete n[e],this.saveConfig(t)}catch(t){throw Error(`Failed to uninstall MCP ${e}: ${t}`)}}getInstalledMCPs(){try{const e=this.getConfig().mcp;return e?Object.keys(e):[]}catch{return[]}}getMCPStatus(e){const t=new Map;for(const n of e)t.set(n.id,this.isMCPInstalled(n.id));return t}getOtherMCPs(e){try{const t=this.getConfig().mcp;if(!t)return[];const n=[];for(const[o,r]of Object.entries(t))e.includes(o)||n.push({id:o,config:r});return n}catch{return[]}}getAllMCPServers(){try{return this.getConfig().mcp||{}}catch{return{}}}detectCurrentConfig(){try{const e=this.getConfig().provider;if(!e)return{plan:null,apiKey:null};const t=e["goffy-coding-plan"];let n=null,o=null;if(t){n="goffy_coding";const e=t.options;o=e?.apiKey||null}return{plan:n,apiKey:o}}catch{return{plan:null,apiKey:null}}}}export const openCodeManager=OpenCodeManager.getInstance();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{execSync as o}from"child_process";import{existsSync as n,readFileSync as t,writeFileSync as l,mkdirSync as e}from"fs";import{join as i,dirname as s}from"path";import{homedir as a}from"os";import{claudeCodeManager as r}from"./claude-code-manager.js";import{openCodeManager as c}from"./opencode-manager.js";import{crushManager as d}from"./crush-manager.js";import{factoryDroidManager as g}from"./factory-droid-manager.js";import{i18n as m}from"./i18n.js";import f from"inquirer";import p from"chalk";import u from"ora";import{logger as _}from"../utils/logger.js";export const SUPPORTED_TOOLS={"claude-code":{name:"claude-code",command:"claude",installCommand:"npm install -g @anthropic-ai/claude-code",configPath:i(a(),".claude","settings.json"),displayName:"Claude Code"},opencode:{name:"opencode",command:"opencode",installCommand:"npm install -g opencode-ai",configPath:i(a(),".config","opencode","opencode.json"),displayName:"OpenCode",hidden:!0},crush:{name:"crush",command:"crush",installCommand:"npm install -g @charmland/crush",configPath:i(a(),".config","crush","crush.json"),displayName:"Crush",hidden:!0},"factory-droid":{name:"factory-droid",command:"droid",installCommand:"win32"===process.platform?"irm https://app.factory.ai/cli/windows | iex":"curl -fsSL https://app.factory.ai/cli | sh",configPath:i(a(),".factory","config.json"),displayName:"Factory Droid",hidden:!0}};export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(n){const t=SUPPORTED_TOOLS[n];if(!t)return!1;try{const n="win32"===process.platform?"where "+t.command:"which "+t.command;return o(n,{stdio:"pipe"}),!0}catch{return!1}}async installTool(n){const t=SUPPORTED_TOOLS[n];if(!t)throw Error("Unknown tool: "+n);const l=u(m.t("wizard.installing_tool")).start();try{if(l.info(p.gray("$ ")+p.white(t.installCommand)),l.stop(),o(t.installCommand,{stdio:"inherit"}),"factory-droid"===n){l.info(p.yellow("\n⚠️ "+m.t("install.manual_action_detected")));const{confirmed:o}=await f.prompt([{type:"confirm",name:"confirmed",message:m.t("install.confirm_manual_action_done"),default:!1}]);if(!o)throw console.log(p.gray("\n📌 "+m.t("install.complete_manual_action_hint"))),Error(m.t("install.user_cancelled"))}l.succeed(m.t("wizard.tool_installed"))}catch(e){l.fail(m.t("wizard.install_failed")),"factory-droid"===n&&"win32"===process.platform&&l.info(p.yellow(`\n⚠️ ${m.t("install.factory_irm_install_tip")}\n`));const i=(e instanceof Error?e.message:"")+(e?.stderr?.toString()||"")+(e?.stdout?.toString()||"");if(!(i.includes("EACCES")||i.includes("permission denied")||i.includes("EPERM")||243===e?.status))throw Error(`Failed to install ${t.displayName}: ${e}`);if(console.log("\n⚠️ "+m.t("install.permission_detected")+"\n"),"win32"===process.platform)try{const n=t.installCommand.replace("npm install -g","npm install -g --force");return console.log("🔧 "+m.t("install.trying_solution",{num:"1",desc:m.t("install.using_force")})),console.log(p.gray("$ ")+p.white(n)),o(n,{stdio:"inherit"}),void console.log("\n✅ "+m.t("install.permission_fixed"))}catch(o){console.log("Retry install error "+o),console.log("\n❌ "+m.t("install.auto_fix_failed")),console.log(p.yellow("\n📌 "+m.t("install.windows_solutions"))),console.log(""),console.log(p.cyan.bold(m.t("install.windows_solution_1_title"))),console.log(p.gray(" "+m.t("install.windows_solution_1_step1"))),console.log(p.gray(" "+m.t("install.windows_solution_1_step2"))),console.log(p.gray(" "+m.t("install.windows_solution_1_step3"))),console.log(p.white(" "+t.installCommand)),console.log(""),console.log(p.cyan.bold(m.t("install.windows_solution_2_title"))),console.log(p.gray(" "+m.t("install.windows_solution_2_command"))),console.log(p.white(" "+t.installCommand.replace("npm install -g","npm install -g --prefix=%APPDATA%\\npm"))),console.log("");const{action:l}=await f.prompt([{type:"list",name:"action",message:m.t("install.what_next"),choices:[{name:m.t("install.installed_continue"),value:"continue"},{name:m.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===l)throw Error(m.t("install.user_cancelled"));if(!this.isToolInstalled(n))throw console.log(p.red("\n❌ "+m.t("install.still_not_installed",{tool:t.displayName}))),Error(t.displayName+" is not installed");return void console.log(p.green("\n✅ "+m.t("install.verified_success",{tool:t.displayName})))}console.log(p.yellow("\n📌 "+m.t("install.unix_solutions"))),console.log(""),console.log(p.cyan.bold(m.t("install.unix_solution_1_title"))),console.log(p.gray(" "+m.t("install.unix_solution_1_desc"))),console.log(p.white(" sudo "+t.installCommand)),console.log("");const s="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally";console.log(p.cyan.bold(m.t("install.unix_solution_2_title"))),console.log(p.gray(" "+m.t("install.unix_solution_2_desc"))),console.log(p.blue(" 📖 "+m.t("install.npm_docs_link")+": ")+s),console.log("");const{action:a}=await f.prompt([{type:"list",name:"action",message:m.t("install.what_next"),choices:[{name:m.t("install.installed_continue"),value:"continue"},{name:m.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===a)throw Error(m.t("install.user_cancelled"));if(!this.isToolInstalled(n))throw console.log(p.red("\n❌ "+m.t("install.still_not_installed",{tool:t.displayName}))),Error(t.displayName+" is not installed");console.log(p.green("\n✅ "+m.t("install.verified_success",{tool:t.displayName})))}}getToolConfig(o){const l=SUPPORTED_TOOLS[o];if(!l)throw Error("Unknown tool: "+o);if("claude-code"===o)return{settings:r.getSettings(),mcp:r.getMCPConfig()};if("opencode"===o)return c.getConfig();if("crush"===o)return d.getConfig();if("factory-droid"===o)return g.getConfig();try{if(n(l.configPath)){const o=t(l.configPath,"utf-8");return JSON.parse(o)}}catch(n){console.warn(`Failed to read config for ${o}:`,n),_.logError(`ToolManager.getToolConfig(${o})`,n)}return null}updateToolConfig(o,t){const i=SUPPORTED_TOOLS[o];if(!i)throw Error("Unknown tool: "+o);if("claude-code"===o)return t.settings&&r.saveSettings(t.settings),void(t.mcp&&r.saveMCPConfig(t.mcp));if("opencode"!==o)if("crush"!==o)if("factory-droid"!==o)try{const o=s(i.configPath);n(o)||e(o,{recursive:!0}),l(i.configPath,JSON.stringify(t,null,2),"utf-8")}catch(n){throw Error(`Failed to update config for ${o}: ${n}`)}else g.saveConfig(t);else d.saveConfig(t);else c.saveConfig(t)}loadGFFConfig(o,n,t){if(!SUPPORTED_TOOLS[o])throw Error("Unknown tool: "+o);if("claude-code"===o)return void r.loadGFFConfig(n,t);if("opencode"===o)return void c.loadGFFConfig(n,t);if("crush"===o)return void d.loadGFFConfig(n,t);if("factory-droid"===o)return void g.loadGFFConfig(n,t);let l=this.getToolConfig(o)||{};l={...l,apiKey:t,baseUrl:"https://api.goffy.tech/v1",model:"goffy-code-1.0",provider:"goffy-coding-plan",plan:n},this.updateToolConfig(o,l)}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return Object.values(SUPPORTED_TOOLS).filter(o=>!o.hidden)}isGitInstalled(){try{const n="win32"===process.platform?"where git":"which git";return o(n,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"inquirer";import a from"chalk";import t from"ora";import{configManager as o}from"./config.js";import{toolManager as i,SUPPORTED_TOOLS as n}from"./tool-manager.js";import{claudeCodeManager as r}from"./claude-code-manager.js";import{openCodeManager as s}from"./opencode-manager.js";import{crushManager as l}from"./crush-manager.js";import{factoryDroidManager as c}from"./factory-droid-manager.js";import{i18n as d}from"./i18n.js";import{createBorderLine as g,createContentLine as _}from"../utils/string-width.js";import{execSync as p}from"child_process";import{validateApiKey as f}from"./api-validator.js";import{logger as m}from"../utils/logger.js";export class Wizard{static instance;BOX_WIDTH=63;constructor(){}static getInstance(){return Wizard.instance||(Wizard.instance=new Wizard),Wizard.instance}createBox(e){console.log(a.cyan.bold("\n"+g("╔","╗","═",this.BOX_WIDTH))),console.log(a.cyan.bold(_(e,"║","║",this.BOX_WIDTH,"center"))),console.log(a.cyan.bold(g("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[a.gray(d.t("wizard.hint_navigate")),a.gray(d.t("wizard.hint_confirm"))];console.log(a.gray("💡 ")+e.join(a.gray(" | "))+"\n")}async promptWithHints(a){return this.showOperationHints(),e.prompt(a)}printBanner(){const e=65,t=d.t("wizard.banner_subtitle"),o=_(t,"║","║",e,"center"),i=_("","║","║",e,"center"),n=_("Coding Helper v0.0.6","║","║",e,"center"),r=[" ▄▀▀ ▄▀▄ █▀▄ █ █▄ █ ▄▀ █▄█ ██▀ █ █▀▄ ██▀ █▀▄ "," ▀▄▄ ▀▄▀ █▄▀ █ █ ▀█ ▀▄█ █ █ █▄▄ █▄▄ █▀ █▄▄ █▀▄ "].map(a=>_(a,"║","║",e,"center")),s=[g("╔","╗","═",e),i,...r,i,n,o,g("╚","╝","═",e)];console.log(a.cyan.bold("\n"+s.join("\n")))}resetScreen(){console.clear(),this.printBanner()}async runFirstTimeSetup(){this.resetScreen(),console.log(a.cyan.bold("\n"+d.t("wizard.welcome"))),console.log(a.gray(d.t("wizard.privacy_note")+"\n")),await this.configLanguage(),await this.configPlan(),await this.configApiKey(),await this.selectAndConfigureTool()}async configLanguage(){for(;;){this.resetScreen(),this.createBox(d.t("wizard.select_language"));const t=d.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+d.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===t?a.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[DE] Deutsch"+("de_DE"===t?a.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"de_DE"},{name:"[ES] Español"+("es_ES"===t?a.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"es_ES"},{name:"[IT] Italiano"+("it_IT"===t?a.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"it_IT"},{name:"[FR] Français"+("fr_FR"===t?a.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"fr_FR"},new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"}],default:"en_US"}]);if("exit"===i)console.log(a.green("\n👋 "+d.t("wizard.goodbye_message"))),process.exit(0);else if("back"===i)return;return o.setLang(i),void d.setLocale(i)}}async configPlan(){for(;;){this.resetScreen(),this.createBox(d.t("wizard.select_plan"));const t=o.getConfig().plan,{plan:i}=await this.promptWithHints([{type:"list",name:"plan",message:"🌟 "+d.t("wizard.select_plan"),choices:[{name:"[Global] "+d.t("wizard.plan_global")+("goffy_coding"===t?a.green(" ✓ ("+d.t("wizard.current_active")+")"):""),value:"goffy_coding"},new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===i)console.log(a.green("\n👋 "+d.t("wizard.goodbye_message"))),process.exit(0);else if("back"===i)return;o.setPlan(i),await this.configApiKey()}}async configApiKey(){for(;;){this.resetScreen(),this.createBox(d.t("wizard.config_api_key"));const i=o.getConfig();i.api_key&&(console.log(a.gray(" "+d.t("wizard.config_api_key")+" ")+a.gray(d.t("wizard.api_key_set")+" ("+i.api_key.slice(0,4)+"****)")),console.log(""));const{action:n}=await this.promptWithHints([{type:"list",name:"action",message:d.t("wizard.select_action"),choices:[{name:"> "+(i.api_key?d.t("wizard.update_api_key"):d.t("wizard.input_api_key")),value:"input"},new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===n)console.log(a.green("\n👋 "+d.t("wizard.goodbye_message"))),process.exit(0);else{if("back"===n)return;if("input"===n){this.resetScreen(),this.createBox(d.t("wizard.config_api_key"));const{apiKey:n}=await e.prompt([{type:"password",name:"apiKey",mask:"●",message:d.t("wizard.input_your_api_key"),validate:e=>!(!e||0===e.trim().length)||"[!] "+d.t("wizard.api_key_required")}]),r=t({text:d.t("wizard.validating_api_key"),spinner:"star2"}).start(),s=i.plan||"goffy_coding";i.plan||o.setPlan("goffy_coding");const l=await f(n.trim(),s);if(await new Promise(e=>setTimeout(e,800)),!l.valid){"invalid_api_key"===l.error?r.fail(a.red(d.t("wizard.api_key_invalid"))):r.fail(a.red(d.t("wizard.api_key_network_error"))),await new Promise(e=>setTimeout(e,1500));continue}o.setApiKey(n.trim()),r.succeed("✅ "+d.t("wizard.set_success")),await new Promise(e=>setTimeout(e,600)),await this.selectAndConfigureTool()}}}}async selectAndConfigureTool(){for(;;){this.resetScreen(),this.createBox(d.t("wizard.select_tool"));const t=i.getSupportedTools().map(e=>({name:"> "+e.displayName,value:e.name}));t.push(new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"});const{selectedTool:o}=await this.promptWithHints([{type:"list",name:"selectedTool",message:d.t("wizard.select_tool"),choices:t}]);if("exit"===o)console.log(a.green("\n👋 "+d.t("wizard.goodbye_message"))),process.exit(0);else if("back"===o)return;await this.configureTool(o)}}async configureTool(e){if(!i.isToolInstalled(e)){console.log(a.yellow("\n"+d.t("wizard.tool_not_installed",{tool:n[e].displayName})));const{shouldInstall:t}=await this.promptWithHints([{type:"confirm",name:"shouldInstall",message:d.t("wizard.install_tool_confirm"),default:!0}]);if(!t)return void console.log(a.yellow(d.t("wizard.install_skipped")));try{await i.installTool(e),await new Promise(e=>setTimeout(e,600))}catch(e){m.logError("Wizard.configureTool",e),console.error(a.red(d.t("install.install_failed_detail"))),e instanceof Error&&e.message&&console.error(a.gray(e.message)),await new Promise(e=>setTimeout(e,600));const{skipInstall:t}=await this.promptWithHints([{type:"list",name:"skipInstall",message:d.t("install.skip_install_confirm"),choices:[{name:d.t("install.skip_install_yes"),value:!0},{name:d.t("install.skip_install_no"),value:!1}]}]);if(!t)return}}await this.showToolMenu(e)}async showMainMenu(){const t=o.getConfig();for(d.loadFromConfig(t.lang||"en_US");;){this.resetScreen();const t=o.getConfig();this.createBox(d.t("wizard.main_menu_title")),console.log(a.gray(" "+d.t("wizard.config_plan")+": ")+(t.plan?a.green(d.t("wizard.plan_global")):a.red(d.t("wizard.not_set")))),console.log(a.gray(" "+d.t("wizard.config_api_key")+": ")+(t.api_key?a.gray(d.t("wizard.api_key_set")+" ("+t.api_key.slice(0,4)+"****)"):a.red(d.t("wizard.not_set")))),console.log("");const i=[{name:"> "+d.t("wizard.menu_config_language"),value:"lang"},{name:"> "+d.t("wizard.menu_select_plan"),value:"plan"},{name:"> "+d.t("wizard.menu_config_api_key"),value:"apikey"},{name:"> "+d.t("wizard.menu_config_tool"),value:"tool"},new e.Separator,{name:"x "+d.t("wizard.menu_exit"),value:"exit"}],{action:n}=await this.promptWithHints([{type:"list",name:"action",message:d.t("wizard.select_operation"),choices:i}]);"exit"===n?(console.log(a.green("\n👋 "+d.t("wizard.goodbye_message"))),process.exit(0)):"lang"===n?await this.configLanguage():"plan"===n?await this.configPlan():"apikey"===n?await this.configApiKey():"tool"===n&&await this.selectAndConfigureTool()}}async showToolMenu(t){for(;;){this.resetScreen();const i=`${n[t].displayName} ${d.t("wizard.menu_title")}`;this.createBox(i),"claude-code"!==t&&"opencode"!==t&&"crush"!==t&&"factory-droid"!==t||(console.log(a.yellow.bold(d.t("wizard.global_config_warning",{tool:n[t].displayName}))),console.log(""),"factory-droid"===t&&(console.log(a.yellow("ℹ️ "+d.t("wizard.factory_droid_login_hint"))),console.log("")));const g="opencode"===t?s.detectCurrentConfig():"crush"===t?l.detectCurrentConfig():"factory-droid"===t?c.detectCurrentConfig():r.detectCurrentConfig(),_=o.getConfig();console.log(a.cyan.bold(d.t("wizard.chelper_config_title")+":")),_.plan?console.log(a.gray(" "+d.t("wizard.config_plan")+": ")+a.green(d.t("wizard.plan_global"))):console.log(a.gray(" "+d.t("wizard.config_plan")+": ")+a.red(d.t("wizard.not_set"))),_.api_key?console.log(a.gray(" "+d.t("wizard.config_api_key")+": ")+a.gray(d.t("wizard.api_key_set")+" ("+_.api_key.slice(0,4)+"****)")):console.log(a.gray(" "+d.t("wizard.config_api_key")+": ")+a.red(d.t("wizard.not_set"))),console.log(""),console.log(a.yellow.bold(n[t].displayName+" "+d.t("wizard.config_title")+":")),g.plan?console.log(a.gray(" "+d.t("wizard.config_plan")+": ")+a.green(d.t("wizard.plan_global"))):console.log(a.gray(" "+d.t("wizard.config_plan")+": ")+a.red(d.t("wizard.not_set"))),g.apiKey?console.log(a.gray(" "+d.t("wizard.config_api_key")+": ")+a.gray(d.t("wizard.api_key_set")+" ("+g.apiKey.slice(0,4)+"****)")):console.log(a.gray(" "+d.t("wizard.config_api_key")+": ")+a.red(d.t("wizard.not_set"))),console.log("");const p=g.plan===_.plan&&g.apiKey===_.api_key;let f="";g.plan&&g.apiKey&&p?(console.log(a.green("✅ "+d.t("wizard.config_synced"))),f=d.t("wizard.action_refresh_gff",{tool:n[t].displayName})):g.plan||g.apiKey?(console.log(a.yellow("⚠️ "+d.t("wizard.config_out_of_sync"))),f=d.t("wizard.action_refresh_gff",{tool:n[t].displayName})):(console.log(a.blue("ℹ️ "+d.t("wizard.config_not_loaded",{tool:n[t].displayName}))),f=d.t("wizard.action_load_gff",{tool:n[t].displayName})),console.log("");const m=[{name:"> "+f,value:"load_gff"}];g.plan&&g.apiKey&&m.push({name:"> "+d.t("wizard.action_unload_gff",{tool:n[t].displayName}),value:"unload_gff"}),g.plan&&g.apiKey&&m.push({name:"> "+d.t("wizard.start_tool",{tool:n[t].displayName,shell:n[t].command}),value:"start_tool",disabled:"opencode"===t&&"win32"===process.platform}),m.push(new e.Separator,{name:"<- "+d.t("wizard.nav_return"),value:"back"},{name:"x "+d.t("wizard.nav_exit"),value:"exit"});const{action:w}=await this.promptWithHints([{type:"list",name:"action",message:d.t("wizard.select_action"),loop:!1,choices:m}]);if("exit"===w)console.log(a.green("\n👋 "+d.t("wizard.goodbye_message"))),process.exit(0);else{if("back"===w)return;"load_gff"===w?await this.loadGFFConfig(t):"unload_gff"===w?await this.unloadGFFConfig(t):"start_tool"===w&&await this.startTool(t)}}}async startTool(e){const o=n[e];if(!o)throw Error("Unknown tool: "+e);if("factory-droid"===e&&!process.env.FACTORY_API_KEY){let e;e="win32"===process.platform?void 0!==process.env.PSModulePath?'$env:FACTORY_API_KEY="fk-demo"':"set FACTORY_API_KEY=fk-demo":"export FACTORY_API_KEY=fk-demo",console.log(a.gray("$ ")+a.white(e)),process.env.FACTORY_API_KEY="fk-demo"}console.log(a.gray("$ ")+a.white(o.command));const r=t({text:d.t("wizard.starting_tool"),spinner:"star2"}).start();try{if("factory-droid"!==e||i.isToolInstalled(e))p(o.command,{stdio:"inherit"});else if("win32"===process.platform)void 0!==process.env.PSModulePath?p("$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User'); "+o.command,{stdio:"inherit",shell:"powershell.exe"}):p(o.command,{stdio:"inherit",shell:"cmd.exe"});else{const e=process.env.SHELL||"/bin/bash",a=e.includes("zsh")?"~/.zshrc":"~/.bashrc";p(`source ${a} 2>/dev/null || true; ${o.command}`,{stdio:"inherit",shell:e})}r.succeed(d.t("wizard.tool_started"))}catch(e){throw m.logError("Wizard.startTool",e),r.fail(d.t("wizard.tool_start_failed")),e}}async loadGFFConfig(e){const r=t({text:d.t("wizard.loading_gff_config"),spinner:"star2"}).start();try{const t=o.getConfig();if(!t.plan||!t.api_key)return r.fail(d.t("wizard.missing_config")),void await new Promise(e=>setTimeout(e,800));i.loadGFFConfig(e,t.plan,t.api_key),await new Promise(e=>setTimeout(e,800)),r.succeed(a.green(d.t("wizard.gff_config_loaded",{tool:n[e].displayName}))),await new Promise(e=>setTimeout(e,2e3))}catch(e){m.logError("Wizard.loadGFFConfig",e),r.fail(d.t("wizard.gff_config_failed")),await new Promise(e=>setTimeout(e,800)),console.error(e)}}async unloadGFFConfig(e){const{confirm:o}=await this.promptWithHints([{type:"confirm",name:"confirm",message:d.t("wizard.confirm_unload_gff",{tool:n[e].displayName}),default:!1}]);if(!o)return;const i=t({text:d.t("wizard.unloading_gff_config"),spinner:"star2"}).start();try{if(await new Promise(e=>setTimeout(e,300)),"claude-code"===e)r.unloadGFFConfig();else if("opencode"===e)s.unloadGFFConfig();else if("crush"===e)l.unloadGFFConfig();else{if("factory-droid"!==e)return i.fail(d.t("wizard.tool_not_supported")),void await new Promise(e=>setTimeout(e,800));c.unloadGFFConfig()}await new Promise(e=>setTimeout(e,500)),i.succeed(a.green(d.t("wizard.gff_config_unloaded"))),await new Promise(e=>setTimeout(e,800))}catch(e){m.logError("Wizard.unloadGFFConfig",e),i.fail(d.t("wizard.gff_config_unload_failed")),await new Promise(e=>setTimeout(e,800)),console.error(e)}}}export const wizard=Wizard.getInstance();
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli": {
|
|
3
|
+
"title": "Coding Tool Helper (chelper)",
|
|
4
|
+
"usage": "Verwendung: chelper <Befehl> [Optionen]",
|
|
5
|
+
"commands": "Befehle:",
|
|
6
|
+
"examples": "Beispiele:",
|
|
7
|
+
"error_unknown": "Unbekannter Befehl: {{command}}",
|
|
8
|
+
"error_general": "Fehler:"
|
|
9
|
+
},
|
|
10
|
+
"commands": {
|
|
11
|
+
"help": "Hilfeinformationen anzeigen",
|
|
12
|
+
"version": "Versionsinformationen anzeigen",
|
|
13
|
+
"init": "Interaktiven Initialisierungsassistenten ausführen",
|
|
14
|
+
"lang": "Sprachverwaltung",
|
|
15
|
+
"auth": "API-Schlüsselverwaltung",
|
|
16
|
+
"doctor": "Systemprüfung",
|
|
17
|
+
"enter": "Konfigurationsverwaltung",
|
|
18
|
+
"tool": "Toolverwaltung"
|
|
19
|
+
},
|
|
20
|
+
"lang": {
|
|
21
|
+
"current": "Aktuelle Sprache",
|
|
22
|
+
"show_usage": "chelper lang show - Aktuelle Sprache anzeigen",
|
|
23
|
+
"set_usage": "chelper lang set <en_US> - Sprache einstellen",
|
|
24
|
+
"changed": "Sprache von {{from}} nach {{to}} geändert",
|
|
25
|
+
"invalid": "Nicht unterstützte Sprache: {{lang}}",
|
|
26
|
+
"available": "Verfügbare Sprachen"
|
|
27
|
+
},
|
|
28
|
+
"auth": {
|
|
29
|
+
"set_usage": "chelper auth <Dienst> <Token> - API-Schlüssel festlegen",
|
|
30
|
+
"service_prompt": "Diensttyp auswählen",
|
|
31
|
+
"token_prompt": "API-Schlüssel eingeben",
|
|
32
|
+
"token_required": "API-Schlüssel darf nicht leer sein",
|
|
33
|
+
"saved": "API-Schlüssel gespeichert",
|
|
34
|
+
"revoke_confirm": "Möchten Sie den API-Schlüssel wirklich widerrufen?",
|
|
35
|
+
"revoked": "API-Schlüssel widerrufen",
|
|
36
|
+
"not_revoked": "Widerruf abgebrochen",
|
|
37
|
+
"get_api_key_hint": "Um einen API-Schlüssel zu erhalten, besuchen Sie bitte: {{url}}",
|
|
38
|
+
"reload_usage": "chelper auth reload <Tool> - Goffy-Konfiguration im Tool neu laden",
|
|
39
|
+
"reloading": "Goffy-Konfiguration wird in {{tool}} neu geladen...",
|
|
40
|
+
"reloaded": "Goffy-Konfiguration erfolgreich in {{tool}} neu geladen",
|
|
41
|
+
"reload_failed": "Neuladen der Goffy-Konfiguration fehlgeschlagen",
|
|
42
|
+
"reload_missing_config": "Fehlende Plan- oder API-Schlüsselkonfiguration. Bitte führen Sie zuerst 'chelper auth' aus.",
|
|
43
|
+
"reload_tool_not_supported": "Tool '{{tool}}' wird für das Neuladen nicht unterstützt",
|
|
44
|
+
"service_not_supported": "Dienst '{{service}}' wird nicht unterstützt, goffy_coding und goffy_coding werden unterstützt."
|
|
45
|
+
},
|
|
46
|
+
"wizard": {
|
|
47
|
+
"banner_subtitle": "Verwalten Sie Ihre Claude Code und anderen Coding-Tools",
|
|
48
|
+
"welcome": "Willkommen beim Coding Tool Helper!",
|
|
49
|
+
"privacy_note": "Wir schätzen Ihre Privatsphäre. Alle Konfigurationen werden nur lokal gespeichert.",
|
|
50
|
+
"select_language": "Oberflächensprache auswählen",
|
|
51
|
+
"select_plan": "Plan auswählen",
|
|
52
|
+
"plan_global": "Goffy Coding Plan",
|
|
53
|
+
"plan_china": "Goffy Coding Plan",
|
|
54
|
+
"input_api_key": "API-Schlüssel eingeben",
|
|
55
|
+
"input_your_api_key": "Bitte geben Sie hier Ihren API-Schlüssel ein und drücken Sie Enter zum Bestätigen:",
|
|
56
|
+
"update_api_key": "API-Schlüssel aktualisieren",
|
|
57
|
+
"api_key_required": "API-Schlüssel darf nicht leer sein",
|
|
58
|
+
"select_tool": "Coding-Tool zur Konfiguration auswählen",
|
|
59
|
+
"tool_not_installed": "{{tool}} ist nicht installiert",
|
|
60
|
+
"install_tool_confirm": "Jetzt installieren?",
|
|
61
|
+
"installing_tool": "Tool wird installiert...",
|
|
62
|
+
"tool_installed": "Tool erfolgreich installiert",
|
|
63
|
+
"install_failed": "Installation fehlgeschlagen",
|
|
64
|
+
"install_skipped": "Installation übersprungen. Sie können es später manuell installieren.",
|
|
65
|
+
"menu_title": "Verwaltungsmenü",
|
|
66
|
+
"select_action": "Aktion auswählen",
|
|
67
|
+
"action_load_gff": "Konfiguration anwenden - (Den von Ihnen eingerichteten Goffy Coding Plan auf {{tool}} anwenden)",
|
|
68
|
+
"action_unload_gff": "Konfiguration entfernen - (Goffy-Konfiguration aus {{tool}} entfernen)",
|
|
69
|
+
"action_mcp_config": "MCP-Konfiguration",
|
|
70
|
+
"action_view_config": "Aktuelle Konfiguration",
|
|
71
|
+
"action_back": "Zurück",
|
|
72
|
+
"action_exit": "Beenden",
|
|
73
|
+
"goodbye": "Auf Wiedersehen!",
|
|
74
|
+
"loading_gff_config": "Goffy-Konfiguration wird geladen...",
|
|
75
|
+
"gff_config_loaded": "Goffy-Konfiguration erfolgreich geladen, Sie können jetzt {{tool}} für das Programmieren verwenden oder MCP-Dienste konfigurieren, um die Fähigkeiten zu erweitern.",
|
|
76
|
+
"gff_config_failed": "Laden der Goffy-Konfiguration fehlgeschlagen",
|
|
77
|
+
"confirm_unload_gff": "Möchten Sie die Goffy-Konfiguration wirklich aus {{tool}} entfernen?",
|
|
78
|
+
"unloading_gff_config": "Goffy-Konfiguration wird entfernt...",
|
|
79
|
+
"gff_config_unloaded": "Goffy-Konfiguration erfolgreich entfernt",
|
|
80
|
+
"gff_config_unload_failed": "Entfernen der Goffy-Konfiguration fehlgeschlagen",
|
|
81
|
+
"tool_not_supported": "Dieser Vorgang wird für dieses Tool nicht unterstützt",
|
|
82
|
+
"missing_config": "Erforderliche Konfiguration fehlt (Plan oder API-Schlüssel)",
|
|
83
|
+
"mcp_menu_title": "MCP-Diensteverwaltung",
|
|
84
|
+
"no_preset_mcp_installed": "Noch keine im Plan integrierten MCP-Dienste installiert",
|
|
85
|
+
"action_install_all_mcp": "Alle im Plan integrierten MCP-Dienste installieren",
|
|
86
|
+
"install_all_mcp_confirm": "Möchten Sie wirklich alle im Plan integrierten MCP-Dienste installieren? Dies werden folgende Dienste installiert:",
|
|
87
|
+
"installing_all_mcp": "Alle im Plan integrierten MCP-Dienste werden stapelweise installiert...",
|
|
88
|
+
"all_mcp_installed": "Alle im Plan integrierten MCP-Dienste erfolgreich installiert",
|
|
89
|
+
"all_mcp_install_failed": "Einige im Plan integrierte MCP-Dienste konnten nicht installiert werden",
|
|
90
|
+
"builtin_mcp_services": "Im Plan integrierte MCP-Dienste",
|
|
91
|
+
"other_mcp_services": "Andere MCP-Dienste",
|
|
92
|
+
"other_mcp": "Andere",
|
|
93
|
+
"other_mcp_info": "Dies ist ein nicht integrierter MCP-Dienst",
|
|
94
|
+
"confirm_uninstall_other_mcp": "Möchten Sie diesen MCP-Dienst wirklich entfernen?",
|
|
95
|
+
"select_mcp": "MCP-Dienst auswählen",
|
|
96
|
+
"installed": "Installiert",
|
|
97
|
+
"not_installed": "Nicht installiert",
|
|
98
|
+
"mcp_protocol": "Protokoll",
|
|
99
|
+
"mcp_type": "Typ",
|
|
100
|
+
"mcp_plan_type": "Plan",
|
|
101
|
+
"mcp_description": "Beschreibung",
|
|
102
|
+
"mcp_status": "Status",
|
|
103
|
+
"action_install": "Installieren",
|
|
104
|
+
"action_uninstall": "Deinstallieren",
|
|
105
|
+
"installing_mcp": "MCP-Dienst wird installiert...",
|
|
106
|
+
"mcp_installed": "MCP-Dienst erfolgreich installiert",
|
|
107
|
+
"mcp_install_failed": "Installation des MCP-Dienstes fehlgeschlagen",
|
|
108
|
+
"uninstalling_mcp": "MCP-Dienst wird deinstalliert...",
|
|
109
|
+
"mcp_uninstalled": "MCP-Dienst erfolgreich deinstalliert",
|
|
110
|
+
"mcp_uninstall_failed": "Deinstallation des MCP-Dienstes fehlgeschlagen",
|
|
111
|
+
"current_config": "Aktuelle Konfiguration",
|
|
112
|
+
"config_language": "Oberflächensprache",
|
|
113
|
+
"config_plan": "Coding Plan",
|
|
114
|
+
"config_api_key": "API-Schlüssel",
|
|
115
|
+
"config_title": "Konfiguration",
|
|
116
|
+
"not_set": "Nicht festgelegt",
|
|
117
|
+
"press_enter": "Drücken Sie Enter um fortzufahren",
|
|
118
|
+
"main_menu_title": "Hauptmenü",
|
|
119
|
+
"current_config_status": "Aktueller Konfigurationsstatus:",
|
|
120
|
+
"api_key_set": "Festgelegt",
|
|
121
|
+
"api_key_get_hint": "Um einen API-Schlüssel zu erhalten, besuchen Sie bitte: {{url}}",
|
|
122
|
+
"menu_config_language": "Oberflächensprache",
|
|
123
|
+
"menu_select_plan": "Coding Plan",
|
|
124
|
+
"menu_config_api_key": "API-Schlüssel",
|
|
125
|
+
"menu_config_tool": "Coding-Tool",
|
|
126
|
+
"menu_exit": "Beenden",
|
|
127
|
+
"select_operation": "Vorgang auswählen:",
|
|
128
|
+
"goodbye_message": "Auf Wiedersehen!",
|
|
129
|
+
"nav_return": "Zurück",
|
|
130
|
+
"nav_exit": "Beenden",
|
|
131
|
+
"current_active": "Aktuell",
|
|
132
|
+
"operation_hint": "Vorgangshinweis",
|
|
133
|
+
"hint_navigate": "↑↓ Navigieren",
|
|
134
|
+
"hint_confirm": "Enter Bestätigen",
|
|
135
|
+
"detected_config_title": "Claude Code Aktuelle Konfiguration",
|
|
136
|
+
"chelper_config_title": "Chelper-Konfiguration",
|
|
137
|
+
"claude_code_config_title": "Claude Code Aktuelle Globale Konfiguration",
|
|
138
|
+
"config_synced": "Konfiguration synchronisiert",
|
|
139
|
+
"config_out_of_sync": "Konfiguration nicht synchronisiert, Aktualisierung empfohlen",
|
|
140
|
+
"config_not_loaded": "{{tool}}-Konfiguration noch nicht geladen",
|
|
141
|
+
"action_refresh_gff": "Konfiguration aktualisieren - (Goffy-Konfiguration von {{tool}} aktualisieren)",
|
|
142
|
+
"global_config_warning": "⚠️ Warnung: Sie ändern die globale Konfiguration von {{tool}}. Änderungen wirken sich auf alle Arbeitsbereiche aus",
|
|
143
|
+
"global_label": "GLOBAL",
|
|
144
|
+
"set_success": "Erfolgreich festgelegt",
|
|
145
|
+
"start_tool": "{{tool}} starten - (Empfehlung: Öffnen Sie ein neues Terminal in Ihrem Arbeitsbereich und führen Sie {{shell}} aus, um es zu starten)",
|
|
146
|
+
"starting_tool": "Wird gestartet...",
|
|
147
|
+
"factory_droid_login_hint": "Achtung: Sie müssen sich in Ihrem Factory Droid-Konto im Factory Droid-Client anmelden, bevor Sie es verwenden können.",
|
|
148
|
+
"validating_api_key": "API-Schlüssel wird überprüft...",
|
|
149
|
+
"api_key_valid": "API-Schlüssel ist gültig",
|
|
150
|
+
"api_key_invalid": "API-Schlüssel ist ungültig oder abgelaufen",
|
|
151
|
+
"api_key_network_error": "Netwerkfehler, bitte überprüfen Sie Ihre Verbindung"
|
|
152
|
+
},
|
|
153
|
+
"doctor": {
|
|
154
|
+
"checking": "Systemprüfung wird ausgeführt...",
|
|
155
|
+
"check_path": "PATH-Umgebungsvariable wird überprüft",
|
|
156
|
+
"check_api_key_network": "API-Schlüssel & Netzwerk werden überprüft",
|
|
157
|
+
"check_api_key": "API-Schlüssel wird überprüft",
|
|
158
|
+
"check_tools": "Installierte Tools werden überprüft",
|
|
159
|
+
"check_gff_plan": "Goffy-Konfiguration wird überprüft",
|
|
160
|
+
"all_good": "Alle Prüfungen bestanden!",
|
|
161
|
+
"tool_installed": "Installiert",
|
|
162
|
+
"tool_not_found": "Tool nicht gefunden: {{tool}}",
|
|
163
|
+
"network_error": "Netzwerkverbindung fehlgeschlagen",
|
|
164
|
+
"api_key_invalid": "API-Schlüssel ist ungültig oder abgelaufen",
|
|
165
|
+
"api_key_network_ok": "API-Schlüssel & Netzwerk überprüft",
|
|
166
|
+
"api_key_missing": "API-Schlüssel nicht konfiguriert",
|
|
167
|
+
"claude_code_installed": "Claude Code ist installiert",
|
|
168
|
+
"claude_code_not_installed": "Claude Code ist nicht installiert. Bitte installieren Sie es von https://claude.ai/download",
|
|
169
|
+
"gff_plan_configured": "Goffy konfiguriert: {{plan}}",
|
|
170
|
+
"gff_plan_not_configured": "Goffy nicht konfiguriert. Führen Sie 'chelper init' aus zum Konfigurieren.",
|
|
171
|
+
"suggestions": "Vorschläge",
|
|
172
|
+
"check_git_env": "Git-Umgebung überprüfen",
|
|
173
|
+
"git_not_installed": "Git ist nicht installiert. Bitte installieren Sie Git von https://git-scm.com/downloads"
|
|
174
|
+
},
|
|
175
|
+
"messages": {
|
|
176
|
+
"initializing": "Wird initialisiert...",
|
|
177
|
+
"args": "Argumente:",
|
|
178
|
+
"first_run": "Erste Ausführung erkannt. Initialisierungsassistent wird gestartet...",
|
|
179
|
+
"config_saved": "Konfiguration gespeichert"
|
|
180
|
+
},
|
|
181
|
+
"install": {
|
|
182
|
+
"permission_detected": "Berechtigungsproblem erkannt, wird versucht zu beheben...",
|
|
183
|
+
"trying_solution": "Lösung {{num}} wird versucht: {{desc}}...",
|
|
184
|
+
"permission_fixed": "Berechtigungsproblem behoben!",
|
|
185
|
+
"auto_fix_failed": "Automatische Behebung fehlgeschlagen.",
|
|
186
|
+
"windows_solutions": "Windows-Benutzer, versuchen Sie bitte eine der folgenden Lösungen:",
|
|
187
|
+
"windows_solution_1_title": "Lösung 1 (Empfohlen): Als Administrator ausführen",
|
|
188
|
+
"windows_solution_1_step1": "1. Rechtsklick auf Eingabeaufforderung oder PowerShell",
|
|
189
|
+
"windows_solution_1_step2": "2. \"Als Administrator ausführen\" auswählen",
|
|
190
|
+
"windows_solution_1_step3": "3. Installationsbefehl erneut ausführen",
|
|
191
|
+
"windows_solution_2_title": "Lösung 2: Benutzerebene Installation verwenden (kein Admin erforderlich)",
|
|
192
|
+
"windows_solution_2_command": "In Befehlszeile ausführen:",
|
|
193
|
+
"unix_solutions": "Bitte versuchen Sie manuell eine der folgenden Lösungen:",
|
|
194
|
+
"unix_solution_1_title": "Lösung 1: Mit sudo installieren",
|
|
195
|
+
"unix_solution_1_desc": "Führen Sie den folgenden Befehl im Terminal aus (erfordert Admin-Passwort):",
|
|
196
|
+
"unix_solution_2_title": "Lösung 2 (Empfohlen): nvm verwenden um Node.js zu verwalten",
|
|
197
|
+
"unix_solution_2_desc": "Die Verwendung von nvm vermeidet Berechtigungsprobleme und wird von npm offiziell empfohlen",
|
|
198
|
+
"path_warning": "Bitte fügen Sie den folgenden Pfad zu Ihrer PATH-Umgebungsvariablen hinzu:",
|
|
199
|
+
"path_add_method": "Wie man hinzufügt:",
|
|
200
|
+
"all_solutions_failed": "Alle automatischen Lösungen sind fehlgeschlagen.",
|
|
201
|
+
"using_sudo": "Installation mit sudo-Rechten",
|
|
202
|
+
"using_user_install": "Benutzerebene Installation wird verwendet (kein sudo erforderlich)",
|
|
203
|
+
"using_force": "Wiederholung mit --force-Flag",
|
|
204
|
+
"npm_docs_link": "npm Offizielle Dokumentation",
|
|
205
|
+
"what_next": "Was möchten Sie als nächstes tun?",
|
|
206
|
+
"installed_continue": "✅ Ich habe die Installation abgeschlossen, weiter",
|
|
207
|
+
"cancel_install": "❌ Installation abbrechen",
|
|
208
|
+
"user_cancelled": "Benutzer hat die Installation abgebrochen",
|
|
209
|
+
"still_not_installed": "{{tool}} scheint immer noch nicht installiert zu sein",
|
|
210
|
+
"install_failed_detail": "Installation fehlgeschlagen mit Fehler:",
|
|
211
|
+
"skip_install_confirm": "Die Installation überspringen und manuell installieren?",
|
|
212
|
+
"skip_install_yes": "Überspringen, ich werde manuell installieren",
|
|
213
|
+
"skip_install_no": "Zurück",
|
|
214
|
+
"retry_check": "Möchten Sie den Installationsstatus erneut überprüfen?",
|
|
215
|
+
"verified_success": "{{tool}}-Installation erfolgreich überprüft!",
|
|
216
|
+
"manual_action_detected": "Die Installation erfordert möglicherweise zusätzliche manuelle Schritte (siehe Ausgabe oben)",
|
|
217
|
+
"confirm_manual_action_done": "Haben Sie die erforderlichen manuellen Schritte ausgeführt?",
|
|
218
|
+
"complete_manual_action_hint": "Bitte führen Sie die oben gezeigten erforderlichen Schritte aus und führen Sie dann die Installation erneut aus",
|
|
219
|
+
"factory_irm_install_tip": "Dies liegt möglicherweise daran, dass Ihre Umgebung den irm-Installationsbefehl nicht unterstützt. Es wird empfohlen, den obigen Befehl in einem PowerShell-Fenster auszuführen, um es selbst zu installieren, oder die offizielle Website für die Installation zu konsultieren https://docs.factory.ai/cli/getting-started/quickstart \n Nach der Eigeninstallation müssen Sie ein neues Befehlszeilenfenster erstellen und den Assistenten erneut ausführen"
|
|
220
|
+
}
|
|
221
|
+
}
|