ai-account-switch 1.9.0 โ 1.12.0
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/.playwright-mcp/grid-view-before.png +0 -0
- package/.playwright-mcp/list-view.png +0 -0
- package/CLAUDE.md +338 -0
- package/README.md +3 -1
- package/package.json +45 -45
- package/src/accounts/base-account.js +39 -0
- package/src/accounts/ccr-account.js +118 -0
- package/src/accounts/claude-account.js +62 -0
- package/src/accounts/codex-account.js +192 -0
- package/src/accounts/droids-account.js +80 -0
- package/src/accounts/index.js +29 -0
- package/src/commands/account.js +68 -0
- package/src/commands/env.js +728 -0
- package/src/commands/helpers.js +32 -0
- package/src/commands/index.js +22 -1
- package/src/commands/mcp.js +71 -13
- package/src/config/global-config.js +266 -0
- package/src/config/project-config.js +255 -0
- package/src/config.js +129 -1300
- package/src/config.js.bak +1593 -0
- package/src/constants.js +86 -0
- package/src/generators/base-generator.js +124 -0
- package/src/generators/ccr-generator.js +113 -0
- package/src/generators/claude-generator.js +124 -0
- package/src/generators/codex-generator.js +207 -0
- package/src/generators/droids-generator.js +49 -0
- package/src/generators/index.js +29 -0
- package/src/index.js +63 -1
- package/src/mcp/mcp-manager.js +309 -0
- package/src/ui-server.js +1093 -9
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const ConfigManager = require('../config');
|
|
7
|
+
const { maskEnvValue } = require('./helpers');
|
|
8
|
+
|
|
9
|
+
const config = new ConfigManager();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get Claude user config path (using ConfigManager's method for consistency)
|
|
13
|
+
*/
|
|
14
|
+
function getClaudeUserConfigPath() {
|
|
15
|
+
const claudeConfigPath = config.getClaudeUserConfigPath();
|
|
16
|
+
|
|
17
|
+
// If config exists, return it; otherwise, use default path
|
|
18
|
+
if (claudeConfigPath) {
|
|
19
|
+
return claudeConfigPath;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Fallback to default location
|
|
23
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
24
|
+
if (!home) return null;
|
|
25
|
+
|
|
26
|
+
return path.join(home, '.claude', 'settings.json');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Read Claude project config (.claude/settings.local.json)
|
|
31
|
+
*/
|
|
32
|
+
function readClaudeProjectConfig(projectRoot = process.cwd()) {
|
|
33
|
+
const claudeConfigFile = path.join(projectRoot, '.claude', 'settings.local.json');
|
|
34
|
+
|
|
35
|
+
if (!fs.existsSync(claudeConfigFile)) {
|
|
36
|
+
return { env: {} };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const data = fs.readFileSync(claudeConfigFile, 'utf8');
|
|
41
|
+
const config = JSON.parse(data);
|
|
42
|
+
// Ensure env property exists
|
|
43
|
+
if (!config.env) {
|
|
44
|
+
config.env = {};
|
|
45
|
+
}
|
|
46
|
+
return config;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return { env: {} };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Write Claude project config
|
|
54
|
+
*/
|
|
55
|
+
function writeClaudeProjectConfig(claudeConfig, projectRoot = process.cwd()) {
|
|
56
|
+
const claudeDir = path.join(projectRoot, '.claude');
|
|
57
|
+
const claudeConfigFile = path.join(claudeDir, 'settings.local.json');
|
|
58
|
+
|
|
59
|
+
// Create .claude directory if it doesn't exist
|
|
60
|
+
if (!fs.existsSync(claudeDir)) {
|
|
61
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Read existing config and merge with new env
|
|
65
|
+
let existingConfig = {};
|
|
66
|
+
if (fs.existsSync(claudeConfigFile)) {
|
|
67
|
+
try {
|
|
68
|
+
const data = fs.readFileSync(claudeConfigFile, 'utf8');
|
|
69
|
+
existingConfig = JSON.parse(data);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// If parsing fails, start fresh
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Merge env property
|
|
76
|
+
existingConfig.env = claudeConfig.env || {};
|
|
77
|
+
|
|
78
|
+
fs.writeFileSync(claudeConfigFile, JSON.stringify(existingConfig, null, 2), 'utf8');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Read Claude user config (~/.claude.json or ~/.config/claude/config.json)
|
|
83
|
+
*/
|
|
84
|
+
function readClaudeUserConfig() {
|
|
85
|
+
const claudeConfigPath = getClaudeUserConfigPath();
|
|
86
|
+
|
|
87
|
+
if (!claudeConfigPath || !fs.existsSync(claudeConfigPath)) {
|
|
88
|
+
return { env: {} };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const data = fs.readFileSync(claudeConfigPath, 'utf8');
|
|
93
|
+
const config = JSON.parse(data);
|
|
94
|
+
// Ensure env property exists
|
|
95
|
+
if (!config.env) {
|
|
96
|
+
config.env = {};
|
|
97
|
+
}
|
|
98
|
+
return config;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return { env: {} };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Write Claude user config
|
|
106
|
+
*/
|
|
107
|
+
function writeClaudeUserConfig(claudeConfig) {
|
|
108
|
+
const claudeConfigPath = getClaudeUserConfigPath();
|
|
109
|
+
|
|
110
|
+
if (!claudeConfigPath) {
|
|
111
|
+
throw new Error('Could not determine Claude config path');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Create directory if it doesn't exist
|
|
115
|
+
const claudeConfigDir = path.dirname(claudeConfigPath);
|
|
116
|
+
if (!fs.existsSync(claudeConfigDir)) {
|
|
117
|
+
fs.mkdirSync(claudeConfigDir, { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Read existing config and merge with new env
|
|
121
|
+
let existingConfig = {};
|
|
122
|
+
if (fs.existsSync(claudeConfigPath)) {
|
|
123
|
+
try {
|
|
124
|
+
const data = fs.readFileSync(claudeConfigPath, 'utf8');
|
|
125
|
+
existingConfig = JSON.parse(data);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// If parsing fails, start fresh
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Merge env property
|
|
132
|
+
existingConfig.env = claudeConfig.env || {};
|
|
133
|
+
|
|
134
|
+
fs.writeFileSync(claudeConfigPath, JSON.stringify(existingConfig, null, 2), 'utf8');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* List environment variables from both project and user configs
|
|
139
|
+
*/
|
|
140
|
+
async function listEnv() {
|
|
141
|
+
try {
|
|
142
|
+
const projectRoot = config.findProjectRoot();
|
|
143
|
+
const projectConfig = projectRoot ? readClaudeProjectConfig(projectRoot) : null;
|
|
144
|
+
const userConfig = readClaudeUserConfig();
|
|
145
|
+
|
|
146
|
+
console.log(chalk.bold.cyan('\n๐ Environment Variables (็ฏๅขๅ้)\n'));
|
|
147
|
+
|
|
148
|
+
// Project-level environment variables
|
|
149
|
+
if (projectRoot) {
|
|
150
|
+
console.log(chalk.bold('Project Level (้กน็ฎ็บงๅซ):'));
|
|
151
|
+
console.log(` ${chalk.cyan('Path:')} ${projectRoot}`);
|
|
152
|
+
console.log(` ${chalk.cyan('Config:')} ${path.join(projectRoot, '.claude', 'settings.local.json')}\n`);
|
|
153
|
+
|
|
154
|
+
const projectEnv = projectConfig.env || {};
|
|
155
|
+
if (Object.keys(projectEnv).length > 0) {
|
|
156
|
+
console.log(chalk.bold(' Variables (ๅ้):'));
|
|
157
|
+
Object.entries(projectEnv).forEach(([key, value]) => {
|
|
158
|
+
const maskedValue = maskEnvValue(key, value);
|
|
159
|
+
console.log(` ${chalk.cyan(key)} = ${chalk.yellow(maskedValue)}`);
|
|
160
|
+
});
|
|
161
|
+
} else {
|
|
162
|
+
console.log(chalk.yellow(' No environment variables configured (ๆช้
็ฝฎ็ฏๅขๅ้)'));
|
|
163
|
+
}
|
|
164
|
+
console.log('');
|
|
165
|
+
} else {
|
|
166
|
+
console.log(chalk.yellow('Not in a project directory (ๆชๅจ้กน็ฎ็ฎๅฝไธญ)\n'));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// User-level environment variables
|
|
170
|
+
console.log(chalk.bold('User Level (็จๆท็บงๅซ):'));
|
|
171
|
+
const claudeConfigPath = getClaudeUserConfigPath();
|
|
172
|
+
console.log(` ${chalk.cyan('Config:')} ${claudeConfigPath}\n`);
|
|
173
|
+
|
|
174
|
+
const userEnv = userConfig.env || {};
|
|
175
|
+
if (Object.keys(userEnv).length > 0) {
|
|
176
|
+
console.log(chalk.bold(' Variables (ๅ้):'));
|
|
177
|
+
Object.entries(userEnv).forEach(([key, value]) => {
|
|
178
|
+
const maskedValue = maskEnvValue(key, value);
|
|
179
|
+
console.log(` ${chalk.cyan(key)} = ${chalk.yellow(maskedValue)}`);
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
console.log(chalk.yellow(' No environment variables configured (ๆช้
็ฝฎ็ฏๅขๅ้)'));
|
|
183
|
+
}
|
|
184
|
+
console.log('');
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error(chalk.red('โ Error listing environment variables:'), error.message);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Add or update environment variable
|
|
192
|
+
*/
|
|
193
|
+
async function addEnv() {
|
|
194
|
+
try {
|
|
195
|
+
const projectRoot = config.findProjectRoot();
|
|
196
|
+
|
|
197
|
+
const { level } = await inquirer.prompt([
|
|
198
|
+
{
|
|
199
|
+
type: 'list',
|
|
200
|
+
name: 'level',
|
|
201
|
+
message: 'Select configuration level (่ฏท้ๆฉ้
็ฝฎ็บงๅซ):',
|
|
202
|
+
choices: projectRoot
|
|
203
|
+
? [
|
|
204
|
+
{ name: 'Project (้กน็ฎ) - Only for current project (ไป
ๅฝๅ้กน็ฎ)', value: 'project' },
|
|
205
|
+
{ name: 'User (็จๆท) - For all projects (ๆๆ้กน็ฎ)', value: 'user' }
|
|
206
|
+
]
|
|
207
|
+
: [
|
|
208
|
+
{ name: 'User (็จๆท) - For all projects (ๆๆ้กน็ฎ)', value: 'user' }
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
let existingEnv;
|
|
214
|
+
let configPath;
|
|
215
|
+
let isUserLevel = level === 'user';
|
|
216
|
+
|
|
217
|
+
if (isUserLevel) {
|
|
218
|
+
const userConfig = readClaudeUserConfig();
|
|
219
|
+
existingEnv = userConfig.env || {};
|
|
220
|
+
configPath = getClaudeUserConfigPath();
|
|
221
|
+
} else {
|
|
222
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
223
|
+
existingEnv = projectConfig.env || {};
|
|
224
|
+
configPath = path.join(projectRoot, '.claude', 'settings.local.json');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Show existing variables
|
|
228
|
+
if (Object.keys(existingEnv).length > 0) {
|
|
229
|
+
console.log(chalk.cyan('\n๐ Existing environment variables (็ฐๆ็ฏๅขๅ้):\n'));
|
|
230
|
+
Object.entries(existingEnv).forEach(([key, value]) => {
|
|
231
|
+
const maskedValue = maskEnvValue(key, value);
|
|
232
|
+
console.log(` ${chalk.gray('โข')} ${chalk.cyan(key)} = ${chalk.yellow(maskedValue)}`);
|
|
233
|
+
});
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const { key, value } = await inquirer.prompt([
|
|
238
|
+
{
|
|
239
|
+
type: 'input',
|
|
240
|
+
name: 'key',
|
|
241
|
+
message: 'Enter environment variable name (่ฏท่พๅ
ฅ็ฏๅขๅ้ๅ็งฐ):',
|
|
242
|
+
validate: (input) => {
|
|
243
|
+
if (!input.trim()) {
|
|
244
|
+
return 'Environment variable name is required (็ฏๅขๅ้ๅ็งฐไธ่ฝไธบ็ฉบ)';
|
|
245
|
+
}
|
|
246
|
+
if (!/^[A-Z_][A-Z0-9_]*$/.test(input.trim())) {
|
|
247
|
+
return 'Invalid variable name. Use uppercase letters, numbers, and underscores (e.g., MY_VAR) (ๅ้ๅๆ ๆใ่ฏทไฝฟ็จๅคงๅๅญๆฏใๆฐๅญๅไธๅ็บฟ,ไพๅฆ: MY_VAR)';
|
|
248
|
+
}
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
type: 'input',
|
|
254
|
+
name: 'value',
|
|
255
|
+
message: 'Enter environment variable value (่ฏท่พๅ
ฅ็ฏๅขๅ้ๅผ):',
|
|
256
|
+
validate: (input) => input.trim() !== '' || 'Environment variable value is required (็ฏๅขๅ้ๅผไธ่ฝไธบ็ฉบ)'
|
|
257
|
+
}
|
|
258
|
+
]);
|
|
259
|
+
|
|
260
|
+
const envKey = key.trim();
|
|
261
|
+
const envValue = value.trim();
|
|
262
|
+
|
|
263
|
+
// Check if variable already exists
|
|
264
|
+
if (existingEnv[envKey]) {
|
|
265
|
+
const { overwrite } = await inquirer.prompt([
|
|
266
|
+
{
|
|
267
|
+
type: 'confirm',
|
|
268
|
+
name: 'overwrite',
|
|
269
|
+
message: `Variable '${envKey}' already exists with value '${existingEnv[envKey]}'. Overwrite? (ๅ้ '${envKey}' ๅทฒๅญๅจ,ๅผไธบ '${existingEnv[envKey]}'ใๆฏๅฆ่ฆ็?)`,
|
|
270
|
+
default: false
|
|
271
|
+
}
|
|
272
|
+
]);
|
|
273
|
+
|
|
274
|
+
if (!overwrite) {
|
|
275
|
+
console.log(chalk.yellow('Operation cancelled. (ๆไฝๅทฒๅๆถใ)'));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Update the config
|
|
281
|
+
if (isUserLevel) {
|
|
282
|
+
const userConfig = readClaudeUserConfig();
|
|
283
|
+
userConfig.env = userConfig.env || {};
|
|
284
|
+
userConfig.env[envKey] = envValue;
|
|
285
|
+
writeClaudeUserConfig(userConfig);
|
|
286
|
+
} else {
|
|
287
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
288
|
+
projectConfig.env = projectConfig.env || {};
|
|
289
|
+
projectConfig.env[envKey] = envValue;
|
|
290
|
+
writeClaudeProjectConfig(projectConfig, projectRoot);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
console.log(chalk.green(`\nโ Environment variable '${envKey}' added successfully at ${level} level! (็ฏๅขๅ้ '${envKey}' ๅจ${level === 'project' ? '้กน็ฎ' : '็จๆท'}็บงๅซๆทปๅ ๆๅ!)`));
|
|
294
|
+
console.log(` ${chalk.cyan('Config file (้
็ฝฎๆไปถ):')} ${configPath}\n`);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error(chalk.red('โ Error adding environment variable:'), error.message);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Set environment variable (non-interactive, for scripts)
|
|
302
|
+
*/
|
|
303
|
+
async function setEnv(key, value, options = {}) {
|
|
304
|
+
try {
|
|
305
|
+
const level = options.level || 'user';
|
|
306
|
+
const isUserLevel = level === 'user';
|
|
307
|
+
|
|
308
|
+
if (!key || !value) {
|
|
309
|
+
console.log(chalk.red('โ Key and value are required (้ฎๅๅผ้ฝๆฏๅฟ
้็)'));
|
|
310
|
+
console.log(chalk.cyan('Usage: ais env set <key> <value> [--level=project|user]'));
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Validate key format
|
|
315
|
+
if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) {
|
|
316
|
+
console.log(chalk.red(`โ Invalid variable name '${key}'. Use uppercase letters, numbers, and underscores (e.g., MY_VAR)`));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let configPath;
|
|
321
|
+
|
|
322
|
+
if (isUserLevel) {
|
|
323
|
+
const userConfig = readClaudeUserConfig();
|
|
324
|
+
userConfig.env = userConfig.env || {};
|
|
325
|
+
userConfig.env[key] = value;
|
|
326
|
+
writeClaudeUserConfig(userConfig);
|
|
327
|
+
configPath = getClaudeUserConfigPath();
|
|
328
|
+
} else {
|
|
329
|
+
const projectRoot = config.findProjectRoot();
|
|
330
|
+
if (!projectRoot) {
|
|
331
|
+
console.log(chalk.red('โ Not in a project directory. Use --level=user or run from a project directory'));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
335
|
+
projectConfig.env = projectConfig.env || {};
|
|
336
|
+
projectConfig.env[key] = value;
|
|
337
|
+
writeClaudeProjectConfig(projectConfig, projectRoot);
|
|
338
|
+
configPath = path.join(projectRoot, '.claude', 'settings.local.json');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
console.log(chalk.green(`โ Environment variable '${key}' set successfully at ${level} level!`));
|
|
342
|
+
console.log(` ${chalk.cyan('Config file:')} ${configPath}`);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error(chalk.red('โ Error setting environment variable:'), error.message);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Remove environment variable
|
|
350
|
+
*/
|
|
351
|
+
async function removeEnv() {
|
|
352
|
+
try {
|
|
353
|
+
const projectRoot = config.findProjectRoot();
|
|
354
|
+
|
|
355
|
+
const { level } = await inquirer.prompt([
|
|
356
|
+
{
|
|
357
|
+
type: 'list',
|
|
358
|
+
name: 'level',
|
|
359
|
+
message: 'Select configuration level (่ฏท้ๆฉ้
็ฝฎ็บงๅซ):',
|
|
360
|
+
choices: projectRoot
|
|
361
|
+
? [
|
|
362
|
+
{ name: 'Project (้กน็ฎ) - Only for current project (ไป
ๅฝๅ้กน็ฎ)', value: 'project' },
|
|
363
|
+
{ name: 'User (็จๆท) - For all projects (ๆๆ้กน็ฎ)', value: 'user' }
|
|
364
|
+
]
|
|
365
|
+
: [
|
|
366
|
+
{ name: 'User (็จๆท) - For all projects (ๆๆ้กน็ฎ)', value: 'user' }
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
]);
|
|
370
|
+
|
|
371
|
+
let existingEnv;
|
|
372
|
+
let isUserLevel = level === 'user';
|
|
373
|
+
|
|
374
|
+
if (isUserLevel) {
|
|
375
|
+
const userConfig = readClaudeUserConfig();
|
|
376
|
+
existingEnv = userConfig.env || {};
|
|
377
|
+
} else {
|
|
378
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
379
|
+
existingEnv = projectConfig.env || {};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (Object.keys(existingEnv).length === 0) {
|
|
383
|
+
console.log(chalk.yellow(`No environment variables configured at ${level} level (${level === 'project' ? '้กน็ฎ' : '็จๆท'}็บงๅซๆช้
็ฝฎ็ฏๅขๅ้)`));
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const { key } = await inquirer.prompt([
|
|
388
|
+
{
|
|
389
|
+
type: 'list',
|
|
390
|
+
name: 'key',
|
|
391
|
+
message: 'Select environment variable to remove (่ฏท้ๆฉ่ฆๅ ้ค็็ฏๅขๅ้):',
|
|
392
|
+
choices: Object.keys(existingEnv)
|
|
393
|
+
}
|
|
394
|
+
]);
|
|
395
|
+
|
|
396
|
+
const { confirm } = await inquirer.prompt([
|
|
397
|
+
{
|
|
398
|
+
type: 'confirm',
|
|
399
|
+
name: 'confirm',
|
|
400
|
+
message: `Remove environment variable '${key}'? (็กฎๅฎ่ฆๅ ้ค็ฏๅขๅ้ '${key}' ๅ?)`,
|
|
401
|
+
default: false
|
|
402
|
+
}
|
|
403
|
+
]);
|
|
404
|
+
|
|
405
|
+
if (!confirm) {
|
|
406
|
+
console.log(chalk.yellow('Operation cancelled. (ๆไฝๅทฒๅๆถใ)'));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Remove the variable
|
|
411
|
+
if (isUserLevel) {
|
|
412
|
+
const userConfig = readClaudeUserConfig();
|
|
413
|
+
delete userConfig.env[key];
|
|
414
|
+
writeClaudeUserConfig(userConfig);
|
|
415
|
+
} else {
|
|
416
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
417
|
+
delete projectConfig.env[key];
|
|
418
|
+
writeClaudeProjectConfig(projectConfig, projectRoot);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
console.log(chalk.green(`โ Environment variable '${key}' removed successfully from ${level} level! (็ฏๅขๅ้ '${key}' ไป${level === 'project' ? '้กน็ฎ' : '็จๆท'}็บงๅซๅ ้คๆๅ!)\n`));
|
|
422
|
+
} catch (error) {
|
|
423
|
+
console.error(chalk.red('โ Error removing environment variable:'), error.message);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Remove environment variable by key (non-interactive)
|
|
429
|
+
*/
|
|
430
|
+
async function unsetEnv(key, options = {}) {
|
|
431
|
+
try {
|
|
432
|
+
const level = options.level || 'user';
|
|
433
|
+
const isUserLevel = level === 'user';
|
|
434
|
+
|
|
435
|
+
if (!key) {
|
|
436
|
+
console.log(chalk.red('โ Key is required (้ฎๆฏๅฟ
้็)'));
|
|
437
|
+
console.log(chalk.cyan('Usage: ais env unset <key> [--level=project|user]'));
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
let configPath;
|
|
442
|
+
|
|
443
|
+
if (isUserLevel) {
|
|
444
|
+
const userConfig = readClaudeUserConfig();
|
|
445
|
+
if (!userConfig.env || !userConfig.env[key]) {
|
|
446
|
+
console.log(chalk.yellow(`Environment variable '${key}' not found at user level`));
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
delete userConfig.env[key];
|
|
450
|
+
writeClaudeUserConfig(userConfig);
|
|
451
|
+
configPath = getClaudeUserConfigPath();
|
|
452
|
+
} else {
|
|
453
|
+
const projectRoot = config.findProjectRoot();
|
|
454
|
+
if (!projectRoot) {
|
|
455
|
+
console.log(chalk.red('โ Not in a project directory. Use --level=user or run from a project directory'));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
459
|
+
if (!projectConfig.env || !projectConfig.env[key]) {
|
|
460
|
+
console.log(chalk.yellow(`Environment variable '${key}' not found at project level`));
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
delete projectConfig.env[key];
|
|
464
|
+
writeClaudeProjectConfig(projectConfig, projectRoot);
|
|
465
|
+
configPath = path.join(projectRoot, '.claude', 'settings.local.json');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
console.log(chalk.green(`โ Environment variable '${key}' removed successfully from ${level} level!`));
|
|
469
|
+
console.log(` ${chalk.cyan('Config file:')} ${configPath}`);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
console.error(chalk.red('โ Error unsetting environment variable:'), error.message);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Show environment variable value
|
|
477
|
+
*/
|
|
478
|
+
async function showEnv(key, options = {}) {
|
|
479
|
+
try {
|
|
480
|
+
const level = options.level;
|
|
481
|
+
const projectRoot = config.findProjectRoot();
|
|
482
|
+
|
|
483
|
+
let found = false;
|
|
484
|
+
let foundLevel = '';
|
|
485
|
+
|
|
486
|
+
// Search in project config first if not specified or if project level requested
|
|
487
|
+
if (!level || level === 'project') {
|
|
488
|
+
if (projectRoot) {
|
|
489
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
490
|
+
if (projectConfig.env && projectConfig.env[key]) {
|
|
491
|
+
console.log(chalk.cyan(`\n๐ Environment Variable: ${key}`));
|
|
492
|
+
console.log(`${chalk.cyan('Level:')} Project (้กน็ฎ)`);
|
|
493
|
+
console.log(`${chalk.cyan('Value:')} ${maskEnvValue(key, projectConfig.env[key])}`);
|
|
494
|
+
console.log(`${chalk.cyan('Config:')} ${path.join(projectRoot, '.claude', 'settings.local.json')}`);
|
|
495
|
+
console.log('');
|
|
496
|
+
found = true;
|
|
497
|
+
foundLevel = 'project';
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Search in user config if not found in project or if user level requested
|
|
503
|
+
if (!found && (!level || level === 'user')) {
|
|
504
|
+
const userConfig = readClaudeUserConfig();
|
|
505
|
+
if (userConfig.env && userConfig.env[key]) {
|
|
506
|
+
console.log(chalk.cyan(`\n๐ Environment Variable: ${key}`));
|
|
507
|
+
console.log(`${chalk.cyan('Level:')} User (็จๆท)`);
|
|
508
|
+
console.log(`${chalk.cyan('Value:')} ${maskEnvValue(key, userConfig.env[key])}`);
|
|
509
|
+
console.log(`${chalk.cyan('Config:')} ${getClaudeUserConfigPath()}`);
|
|
510
|
+
console.log('');
|
|
511
|
+
found = true;
|
|
512
|
+
foundLevel = 'user';
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (!found) {
|
|
517
|
+
console.log(chalk.yellow(`Environment variable '${key}' not found`));
|
|
518
|
+
if (level) {
|
|
519
|
+
console.log(` ${chalk.cyan('Level:')} ${level}`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.error(chalk.red('โ Error showing environment variable:'), error.message);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Clear all environment variables at a level
|
|
529
|
+
*/
|
|
530
|
+
async function clearEnv(options = {}) {
|
|
531
|
+
try {
|
|
532
|
+
const level = options.level || 'user';
|
|
533
|
+
const isUserLevel = level === 'user';
|
|
534
|
+
|
|
535
|
+
const { confirm } = await inquirer.prompt([
|
|
536
|
+
{
|
|
537
|
+
type: 'confirm',
|
|
538
|
+
name: 'confirm',
|
|
539
|
+
message: `Clear all environment variables at ${level} level? This cannot be undone. (็กฎๅฎ่ฆๆธ
็ฉบ${level === 'project' ? '้กน็ฎ' : '็จๆท'}็บงๅซ็ๆๆ็ฏๅขๅ้ๅ? ๆญคๆไฝๆ ๆณๆค้ใ)`,
|
|
540
|
+
default: false
|
|
541
|
+
}
|
|
542
|
+
]);
|
|
543
|
+
|
|
544
|
+
if (!confirm) {
|
|
545
|
+
console.log(chalk.yellow('Operation cancelled. (ๆไฝๅทฒๅๆถใ)'));
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (isUserLevel) {
|
|
550
|
+
const userConfig = readClaudeUserConfig();
|
|
551
|
+
userConfig.env = {};
|
|
552
|
+
writeClaudeUserConfig(userConfig);
|
|
553
|
+
console.log(chalk.green('โ All user-level environment variables cleared successfully! (ๆๆ็จๆท็บงๅซ็็ฏๅขๅ้ๅทฒๆธ
็ฉบ!)'));
|
|
554
|
+
} else {
|
|
555
|
+
const projectRoot = config.findProjectRoot();
|
|
556
|
+
if (!projectRoot) {
|
|
557
|
+
console.log(chalk.red('โ Not in a project directory'));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
561
|
+
projectConfig.env = {};
|
|
562
|
+
writeClaudeProjectConfig(projectConfig, projectRoot);
|
|
563
|
+
console.log(chalk.green('โ All project-level environment variables cleared successfully! (ๆๆ้กน็ฎ็บงๅซ็็ฏๅขๅ้ๅทฒๆธ
็ฉบ!)'));
|
|
564
|
+
}
|
|
565
|
+
console.log('');
|
|
566
|
+
} catch (error) {
|
|
567
|
+
console.error(chalk.red('โ Error clearing environment variables:'), error.message);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Edit environment variables interactively
|
|
573
|
+
*/
|
|
574
|
+
async function editEnv(options = {}) {
|
|
575
|
+
try {
|
|
576
|
+
const level = options.level || 'user';
|
|
577
|
+
const projectRoot = config.findProjectRoot();
|
|
578
|
+
|
|
579
|
+
if (level === 'project' && !projectRoot) {
|
|
580
|
+
console.log(chalk.red('โ Not in a project directory'));
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
let envVars;
|
|
585
|
+
let isUserLevel = level === 'user';
|
|
586
|
+
|
|
587
|
+
if (isUserLevel) {
|
|
588
|
+
const userConfig = readClaudeUserConfig();
|
|
589
|
+
envVars = { ...userConfig.env } || {};
|
|
590
|
+
} else {
|
|
591
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
592
|
+
envVars = { ...projectConfig.env } || {};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
console.log(chalk.cyan(`\n๐ Editing ${level}-level environment variables (็ผ่พ${level === 'project' ? '้กน็ฎ' : '็จๆท'}็บงๅซ็ฏๅขๅ้)\n`));
|
|
596
|
+
|
|
597
|
+
while (true) {
|
|
598
|
+
console.log(chalk.bold('Current environment variables (ๅฝๅ็ฏๅขๅ้):'));
|
|
599
|
+
const keys = Object.keys(envVars);
|
|
600
|
+
if (keys.length === 0) {
|
|
601
|
+
console.log(chalk.gray(' (None)'));
|
|
602
|
+
} else {
|
|
603
|
+
keys.forEach((key, index) => {
|
|
604
|
+
console.log(` ${index + 1}. ${chalk.cyan(key)} = ${chalk.yellow(maskEnvValue(key, envVars[key]))}`);
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
console.log('');
|
|
608
|
+
|
|
609
|
+
const { action } = await inquirer.prompt([
|
|
610
|
+
{
|
|
611
|
+
type: 'list',
|
|
612
|
+
name: 'action',
|
|
613
|
+
message: 'Select action (่ฏท้ๆฉๆไฝ):',
|
|
614
|
+
choices: [
|
|
615
|
+
{ name: 'Add new variable (ๆทปๅ ๆฐๅ้)', value: 'add' },
|
|
616
|
+
{ name: 'Edit variable (็ผ่พๅ้)', value: 'edit' },
|
|
617
|
+
{ name: 'Remove variable (ๅ ้คๅ้)', value: 'remove' },
|
|
618
|
+
{ name: 'Save and exit (ไฟๅญๅนถ้ๅบ)', value: 'save' },
|
|
619
|
+
{ name: 'Cancel (ๅๆถ)', value: 'cancel' }
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
]);
|
|
623
|
+
|
|
624
|
+
if (action === 'save') {
|
|
625
|
+
if (isUserLevel) {
|
|
626
|
+
const userConfig = readClaudeUserConfig();
|
|
627
|
+
userConfig.env = envVars;
|
|
628
|
+
writeClaudeUserConfig(userConfig);
|
|
629
|
+
} else {
|
|
630
|
+
const projectConfig = readClaudeProjectConfig(projectRoot);
|
|
631
|
+
projectConfig.env = envVars;
|
|
632
|
+
writeClaudeProjectConfig(projectConfig, projectRoot);
|
|
633
|
+
}
|
|
634
|
+
console.log(chalk.green('โ Environment variables saved successfully! (็ฏๅขๅ้ไฟๅญๆๅ!)\n'));
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (action === 'cancel') {
|
|
639
|
+
console.log(chalk.yellow('Operation cancelled. Changes not saved. (ๆไฝๅทฒๅๆถใๆดๆนๆชไฟๅญใ)\n'));
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (action === 'add') {
|
|
644
|
+
const { key, value } = await inquirer.prompt([
|
|
645
|
+
{
|
|
646
|
+
type: 'input',
|
|
647
|
+
name: 'key',
|
|
648
|
+
message: 'Enter variable name (่ฏท่พๅ
ฅๅ้ๅ):',
|
|
649
|
+
validate: (input) => {
|
|
650
|
+
if (!input.trim()) return 'Variable name is required';
|
|
651
|
+
if (!/^[A-Z_][A-Z0-9_]*$/.test(input.trim())) {
|
|
652
|
+
return 'Invalid format. Use uppercase letters, numbers, and underscores';
|
|
653
|
+
}
|
|
654
|
+
if (envVars[input.trim()]) return 'Variable already exists';
|
|
655
|
+
return true;
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
type: 'input',
|
|
660
|
+
name: 'value',
|
|
661
|
+
message: 'Enter variable value (่ฏท่พๅ
ฅๅ้ๅผ):',
|
|
662
|
+
validate: (input) => input.trim() !== '' || 'Value is required'
|
|
663
|
+
}
|
|
664
|
+
]);
|
|
665
|
+
envVars[key.trim()] = value.trim();
|
|
666
|
+
} else if (action === 'edit') {
|
|
667
|
+
if (keys.length === 0) {
|
|
668
|
+
console.log(chalk.yellow('No variables to edit.\n'));
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
const { key } = await inquirer.prompt([
|
|
672
|
+
{
|
|
673
|
+
type: 'list',
|
|
674
|
+
name: 'key',
|
|
675
|
+
message: 'Select variable to edit (่ฏท้ๆฉ่ฆ็ผ่พ็ๅ้):',
|
|
676
|
+
choices: keys
|
|
677
|
+
}
|
|
678
|
+
]);
|
|
679
|
+
const { value } = await inquirer.prompt([
|
|
680
|
+
{
|
|
681
|
+
type: 'input',
|
|
682
|
+
name: 'value',
|
|
683
|
+
message: `Enter new value for ${key}:`,
|
|
684
|
+
default: envVars[key]
|
|
685
|
+
}
|
|
686
|
+
]);
|
|
687
|
+
envVars[key] = value.trim();
|
|
688
|
+
} else if (action === 'remove') {
|
|
689
|
+
if (keys.length === 0) {
|
|
690
|
+
console.log(chalk.yellow('No variables to remove.\n'));
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
const { key } = await inquirer.prompt([
|
|
694
|
+
{
|
|
695
|
+
type: 'list',
|
|
696
|
+
name: 'key',
|
|
697
|
+
message: 'Select variable to remove (่ฏท้ๆฉ่ฆๅ ้ค็ๅ้):',
|
|
698
|
+
choices: keys
|
|
699
|
+
}
|
|
700
|
+
]);
|
|
701
|
+
const { confirm } = await inquirer.prompt([
|
|
702
|
+
{
|
|
703
|
+
type: 'confirm',
|
|
704
|
+
name: 'confirm',
|
|
705
|
+
message: `Remove variable '${key}'?`,
|
|
706
|
+
default: false
|
|
707
|
+
}
|
|
708
|
+
]);
|
|
709
|
+
if (confirm) {
|
|
710
|
+
delete envVars[key];
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
} catch (error) {
|
|
715
|
+
console.error(chalk.red('โ Error editing environment variables:'), error.message);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
module.exports = {
|
|
720
|
+
listEnv,
|
|
721
|
+
addEnv,
|
|
722
|
+
setEnv,
|
|
723
|
+
removeEnv,
|
|
724
|
+
unsetEnv,
|
|
725
|
+
showEnv,
|
|
726
|
+
clearEnv,
|
|
727
|
+
editEnv
|
|
728
|
+
};
|