claude-autopm 1.31.0 → 2.1.1
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 +57 -5
- package/autopm/.claude/mcp/test-server.md +10 -0
- package/bin/autopm-poc.js +176 -44
- package/bin/autopm.js +97 -179
- package/lib/ai-providers/AbstractAIProvider.js +524 -0
- package/lib/ai-providers/ClaudeProvider.js +359 -48
- package/lib/ai-providers/TemplateProvider.js +432 -0
- package/lib/cli/commands/agent.js +206 -0
- package/lib/cli/commands/config.js +488 -0
- package/lib/cli/commands/prd.js +345 -0
- package/lib/cli/commands/task.js +206 -0
- package/lib/config/ConfigManager.js +531 -0
- package/lib/errors/AIProviderError.js +164 -0
- package/lib/services/AgentService.js +557 -0
- package/lib/services/EpicService.js +609 -0
- package/lib/services/PRDService.js +928 -103
- package/lib/services/TaskService.js +760 -0
- package/lib/services/interfaces.js +753 -0
- package/lib/utils/CircuitBreaker.js +165 -0
- package/lib/utils/Encryption.js +201 -0
- package/lib/utils/RateLimiter.js +241 -0
- package/lib/utils/ServiceFactory.js +165 -0
- package/package.json +6 -5
- package/scripts/config/get.js +108 -0
- package/scripts/config/init.js +100 -0
- package/scripts/config/list-providers.js +93 -0
- package/scripts/config/set-api-key.js +107 -0
- package/scripts/config/set-provider.js +201 -0
- package/scripts/config/set.js +139 -0
- package/scripts/config/show.js +181 -0
- package/autopm/.claude/.env +0 -158
- package/autopm/.claude/settings.local.json +0 -9
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ServiceFactory - Creates services with ConfigManager integration
|
|
3
|
+
*
|
|
4
|
+
* Provides factory methods for creating AutoPM services with:
|
|
5
|
+
* - Automatic provider creation from ConfigManager
|
|
6
|
+
* - Centralized configuration management
|
|
7
|
+
* - Simplified service instantiation
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const factory = new ServiceFactory(configManager);
|
|
11
|
+
* const prdService = factory.createPRDService();
|
|
12
|
+
* const epicService = factory.createEpicService();
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const ConfigManager = require('../config/ConfigManager');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* ServiceFactory - Creates services with ConfigManager integration
|
|
19
|
+
*/
|
|
20
|
+
class ServiceFactory {
|
|
21
|
+
/**
|
|
22
|
+
* Create a ServiceFactory instance
|
|
23
|
+
*
|
|
24
|
+
* @param {ConfigManager} configManager - ConfigManager instance
|
|
25
|
+
* @throws {Error} If configManager is not provided or invalid
|
|
26
|
+
*/
|
|
27
|
+
constructor(configManager) {
|
|
28
|
+
if (!configManager) {
|
|
29
|
+
throw new Error('ConfigManager instance is required');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!(configManager instanceof ConfigManager)) {
|
|
33
|
+
throw new Error('configManager must be an instance of ConfigManager');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.configManager = configManager;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create AI provider from ConfigManager
|
|
41
|
+
*
|
|
42
|
+
* @param {string} [providerName] - Provider name (defaults to defaultProvider)
|
|
43
|
+
* @returns {Object} Provider instance
|
|
44
|
+
* @throws {Error} If provider creation fails
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const provider = factory.createProvider(); // Uses default provider
|
|
48
|
+
* const provider = factory.createProvider('openai'); // Specific provider
|
|
49
|
+
*/
|
|
50
|
+
createProvider(providerName = null) {
|
|
51
|
+
// Check master password
|
|
52
|
+
if (!this.configManager.hasMasterPassword()) {
|
|
53
|
+
throw new Error('Master password not set. Call configManager.setMasterPassword() first.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Get provider name (default if not specified)
|
|
57
|
+
const name = providerName || this.configManager.getDefaultProvider();
|
|
58
|
+
|
|
59
|
+
// Get provider config first to validate provider exists
|
|
60
|
+
const config = this.configManager.getProvider(name);
|
|
61
|
+
if (!config) {
|
|
62
|
+
throw new Error(`Provider configuration not found: ${name}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Validate configuration before attempting to get API key
|
|
66
|
+
try {
|
|
67
|
+
this.configManager.validateProvider(config);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new Error(`Invalid configuration for provider '${name}': ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Get API key
|
|
73
|
+
const apiKey = this.configManager.getApiKey(name);
|
|
74
|
+
if (!apiKey) {
|
|
75
|
+
throw new Error(`API key not found for provider: ${name}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Create provider based on name
|
|
79
|
+
if (name === 'claude') {
|
|
80
|
+
const ClaudeProvider = require('../ai-providers/ClaudeProvider');
|
|
81
|
+
return new ClaudeProvider({ apiKey, ...config });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throw new Error(`Unknown provider: ${name}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create PRDService instance
|
|
89
|
+
*
|
|
90
|
+
* @param {Object} [options] - Service options
|
|
91
|
+
* @param {Object} [options.provider] - Custom provider (overrides ConfigManager)
|
|
92
|
+
* @param {number} [options.defaultEffortHours] - Default effort hours
|
|
93
|
+
* @param {number} [options.hoursPerDay] - Hours per day
|
|
94
|
+
* @param {number} [options.hoursPerWeek] - Hours per week
|
|
95
|
+
* @returns {PRDService} PRDService instance
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* const service = factory.createPRDService();
|
|
99
|
+
* const service = factory.createPRDService({ defaultEffortHours: 10 });
|
|
100
|
+
*/
|
|
101
|
+
createPRDService(options = {}) {
|
|
102
|
+
const PRDService = require('../services/PRDService');
|
|
103
|
+
|
|
104
|
+
// Create provider if not provided
|
|
105
|
+
const provider = options.provider || this.createProvider();
|
|
106
|
+
|
|
107
|
+
return new PRDService({
|
|
108
|
+
provider,
|
|
109
|
+
configManager: this.configManager,
|
|
110
|
+
...options
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create EpicService instance
|
|
116
|
+
*
|
|
117
|
+
* @param {Object} [options] - Service options
|
|
118
|
+
* @param {PRDService} [options.prdService] - Custom PRDService (overrides auto-creation)
|
|
119
|
+
* @returns {EpicService} EpicService instance
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* const service = factory.createEpicService();
|
|
123
|
+
* const service = factory.createEpicService({ prdService: customPrdService });
|
|
124
|
+
*/
|
|
125
|
+
createEpicService(options = {}) {
|
|
126
|
+
const EpicService = require('../services/EpicService');
|
|
127
|
+
|
|
128
|
+
// Create PRDService if not provided
|
|
129
|
+
const prdService = options.prdService || this.createPRDService();
|
|
130
|
+
|
|
131
|
+
return new EpicService({
|
|
132
|
+
prdService,
|
|
133
|
+
configManager: this.configManager,
|
|
134
|
+
...options
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Create TaskService instance
|
|
140
|
+
*
|
|
141
|
+
* @param {Object} [options] - Service options
|
|
142
|
+
* @param {PRDService} [options.prdService] - Custom PRDService (overrides auto-creation)
|
|
143
|
+
* @param {string} [options.defaultTaskType] - Default task type
|
|
144
|
+
* @param {string} [options.defaultEffort] - Default effort
|
|
145
|
+
* @returns {TaskService} TaskService instance
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* const service = factory.createTaskService();
|
|
149
|
+
* const service = factory.createTaskService({ defaultTaskType: 'testing' });
|
|
150
|
+
*/
|
|
151
|
+
createTaskService(options = {}) {
|
|
152
|
+
const TaskService = require('../services/TaskService');
|
|
153
|
+
|
|
154
|
+
// Create PRDService if not provided
|
|
155
|
+
const prdService = options.prdService || this.createPRDService();
|
|
156
|
+
|
|
157
|
+
return new TaskService({
|
|
158
|
+
prdService,
|
|
159
|
+
configManager: this.configManager,
|
|
160
|
+
...options
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = ServiceFactory;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-autopm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
|
|
5
5
|
"main": "bin/autopm.js",
|
|
6
6
|
"bin": {
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
"README.md"
|
|
117
117
|
],
|
|
118
118
|
"dependencies": {
|
|
119
|
-
"@anthropic-ai/sdk": "^0.
|
|
119
|
+
"@anthropic-ai/sdk": "^0.65.0",
|
|
120
120
|
"@octokit/rest": "^22.0.0",
|
|
121
121
|
"azure-devops-node-api": "^15.1.1",
|
|
122
|
-
"chalk": "
|
|
123
|
-
"dotenv": "^
|
|
122
|
+
"chalk": "4.1.2",
|
|
123
|
+
"dotenv": "^17.2.3",
|
|
124
124
|
"execa": "^9.6.0",
|
|
125
125
|
"fast-glob": "^3.3.2",
|
|
126
126
|
"fs-extra": "^11.3.2",
|
|
@@ -128,6 +128,7 @@
|
|
|
128
128
|
"inquirer": "^12.9.6",
|
|
129
129
|
"js-yaml": "^4.1.0",
|
|
130
130
|
"markdown-it": "^14.1.0",
|
|
131
|
+
"marked": "^16.4.0",
|
|
131
132
|
"moment": "^2.29.4",
|
|
132
133
|
"ora": "^5.4.1",
|
|
133
134
|
"simple-git": "^3.20.0",
|
|
@@ -155,7 +156,7 @@
|
|
|
155
156
|
"sinon": "^21.0.0"
|
|
156
157
|
},
|
|
157
158
|
"optionalDependencies": {
|
|
158
|
-
"@playwright/mcp": "^0.0.
|
|
159
|
+
"@playwright/mcp": "^0.0.42",
|
|
159
160
|
"@upstash/context7-mcp": "^1.0.0"
|
|
160
161
|
},
|
|
161
162
|
"publishConfig": {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Config command: get
|
|
4
|
+
* Get configuration value with dot notation support
|
|
5
|
+
*
|
|
6
|
+
* Usage: autopm config:get <key>
|
|
7
|
+
* Example: autopm config:get providers.claude.model
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const ConfigManager = require('../../lib/config/ConfigManager');
|
|
13
|
+
|
|
14
|
+
// Default config path
|
|
15
|
+
const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Mask sensitive API key data
|
|
19
|
+
* @param {*} value - Value to potentially mask
|
|
20
|
+
* @returns {*} Masked value or original value
|
|
21
|
+
*/
|
|
22
|
+
function maskSensitiveData(value) {
|
|
23
|
+
if (value && typeof value === 'object') {
|
|
24
|
+
if ('iv' in value && 'encryptedData' in value) {
|
|
25
|
+
// This is an encrypted API key
|
|
26
|
+
return '***encrypted***';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Recursively mask object properties
|
|
30
|
+
const masked = {};
|
|
31
|
+
for (const [key, val] of Object.entries(value)) {
|
|
32
|
+
masked[key] = maskSensitiveData(val);
|
|
33
|
+
}
|
|
34
|
+
return masked;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Format output value
|
|
42
|
+
* @param {*} value - Value to format
|
|
43
|
+
* @returns {string} Formatted output
|
|
44
|
+
*/
|
|
45
|
+
function formatOutput(value) {
|
|
46
|
+
if (value === null || value === undefined) {
|
|
47
|
+
return 'null';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (typeof value === 'object') {
|
|
51
|
+
// Mask sensitive data before printing
|
|
52
|
+
const masked = maskSensitiveData(value);
|
|
53
|
+
return JSON.stringify(masked, null, 2);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return String(value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Main function
|
|
61
|
+
*/
|
|
62
|
+
async function main() {
|
|
63
|
+
try {
|
|
64
|
+
const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
|
|
65
|
+
|
|
66
|
+
// Get key from command line arguments
|
|
67
|
+
const key = process.argv[2];
|
|
68
|
+
|
|
69
|
+
if (!key) {
|
|
70
|
+
console.error('Error: Key is required');
|
|
71
|
+
console.error('Usage: autopm config:get <key>');
|
|
72
|
+
console.error('Example: autopm config:get providers.claude.model');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if config exists
|
|
77
|
+
const fs = require('fs');
|
|
78
|
+
if (!fs.existsSync(configPath)) {
|
|
79
|
+
console.error('Error: Configuration not found');
|
|
80
|
+
console.error('Run: autopm config:init');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Load configuration
|
|
85
|
+
const manager = new ConfigManager(configPath);
|
|
86
|
+
|
|
87
|
+
// Get value
|
|
88
|
+
const value = manager.getConfig(key);
|
|
89
|
+
|
|
90
|
+
// Output formatted value
|
|
91
|
+
console.log(formatOutput(value));
|
|
92
|
+
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error.message.includes('Unexpected token') || error.message.includes('JSON')) {
|
|
95
|
+
console.error('Error reading config: Invalid JSON format');
|
|
96
|
+
} else {
|
|
97
|
+
console.error('Error:', error.message);
|
|
98
|
+
}
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Run if called directly
|
|
104
|
+
if (require.main === module) {
|
|
105
|
+
main();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = main;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Config command: init
|
|
4
|
+
* Initialize configuration with interactive prompts for master password
|
|
5
|
+
*
|
|
6
|
+
* Usage: autopm config:init
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const { password } = require('@inquirer/prompts');
|
|
12
|
+
const ConfigManager = require('../../lib/config/ConfigManager');
|
|
13
|
+
|
|
14
|
+
// Default config path
|
|
15
|
+
const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Validate password meets minimum requirements
|
|
19
|
+
* @param {string} pwd - Password to validate
|
|
20
|
+
* @returns {boolean|string} True if valid, error message otherwise
|
|
21
|
+
*/
|
|
22
|
+
function validatePassword(pwd) {
|
|
23
|
+
if (!pwd || pwd.length < 8) {
|
|
24
|
+
return 'Password must be at least 8 characters long';
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Main initialization function
|
|
31
|
+
*/
|
|
32
|
+
async function main() {
|
|
33
|
+
try {
|
|
34
|
+
const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
|
|
35
|
+
|
|
36
|
+
console.log('\n🔧 AutoPM Configuration Setup');
|
|
37
|
+
console.log('=============================\n');
|
|
38
|
+
|
|
39
|
+
// Check if config already exists
|
|
40
|
+
const fs = require('fs');
|
|
41
|
+
if (fs.existsSync(configPath)) {
|
|
42
|
+
console.log('⚠️ Configuration already exists at:', configPath);
|
|
43
|
+
console.log('To reconfigure, delete the existing file first.\n');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Prompt for master password
|
|
48
|
+
const masterPassword = await password({
|
|
49
|
+
message: 'Enter master password (for API key encryption):',
|
|
50
|
+
mask: '*'
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Validate password
|
|
54
|
+
const validationResult = validatePassword(masterPassword);
|
|
55
|
+
if (validationResult !== true) {
|
|
56
|
+
console.error(`\nError: ${validationResult}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Confirm password
|
|
61
|
+
const confirmPassword = await password({
|
|
62
|
+
message: 'Confirm master password:',
|
|
63
|
+
mask: '*'
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Check passwords match
|
|
67
|
+
if (masterPassword !== confirmPassword) {
|
|
68
|
+
console.error('\nError: Passwords do not match');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Create ConfigManager instance
|
|
73
|
+
const manager = new ConfigManager(configPath);
|
|
74
|
+
manager.setMasterPassword(masterPassword);
|
|
75
|
+
|
|
76
|
+
// Save configuration
|
|
77
|
+
manager.save();
|
|
78
|
+
|
|
79
|
+
console.log('\n✓ Configuration initialized at:', configPath);
|
|
80
|
+
console.log('✓ Master password set (not stored, remember it!)');
|
|
81
|
+
console.log('\nNext steps:');
|
|
82
|
+
console.log(' - Set API key: autopm config:set-api-key');
|
|
83
|
+
console.log(' - Configure provider: autopm config:set-provider\n');
|
|
84
|
+
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (error.message.includes('cancelled') || error.message.includes('User force closed')) {
|
|
87
|
+
console.error('\nError: Setup cancelled by user');
|
|
88
|
+
} else {
|
|
89
|
+
console.error('\nError:', error.message);
|
|
90
|
+
}
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Run if called directly
|
|
96
|
+
if (require.main === module) {
|
|
97
|
+
main();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = main;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Config command: list-providers
|
|
4
|
+
* List all configured providers with API key status
|
|
5
|
+
*
|
|
6
|
+
* Usage: autopm config:list-providers
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const ConfigManager = require('../../lib/config/ConfigManager');
|
|
12
|
+
|
|
13
|
+
// Default config path
|
|
14
|
+
const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Main function
|
|
18
|
+
*/
|
|
19
|
+
async function main() {
|
|
20
|
+
try {
|
|
21
|
+
const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
|
|
22
|
+
|
|
23
|
+
// Check if config exists
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
if (!fs.existsSync(configPath)) {
|
|
26
|
+
console.error('\nError: Configuration not found');
|
|
27
|
+
console.error('Run: autopm config:init\n');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Load configuration
|
|
32
|
+
const manager = new ConfigManager(configPath);
|
|
33
|
+
|
|
34
|
+
// Get providers
|
|
35
|
+
const providers = manager.listProviders();
|
|
36
|
+
const defaultProvider = manager.getDefaultProvider();
|
|
37
|
+
|
|
38
|
+
if (providers.length === 0) {
|
|
39
|
+
console.log('\nNo providers configured');
|
|
40
|
+
console.log('Run: autopm config:set-provider <name>\n');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Display header
|
|
45
|
+
console.log('\nConfigured Providers:');
|
|
46
|
+
console.log('=====================\n');
|
|
47
|
+
|
|
48
|
+
// Sort providers alphabetically
|
|
49
|
+
const sortedProviders = providers.sort();
|
|
50
|
+
|
|
51
|
+
// Count providers with API keys
|
|
52
|
+
let apiKeyCount = 0;
|
|
53
|
+
|
|
54
|
+
// Display each provider
|
|
55
|
+
for (const providerName of sortedProviders) {
|
|
56
|
+
const provider = manager.getProvider(providerName);
|
|
57
|
+
const hasApiKey = manager.hasApiKey(providerName);
|
|
58
|
+
const isDefault = providerName === defaultProvider;
|
|
59
|
+
|
|
60
|
+
if (hasApiKey) {
|
|
61
|
+
apiKeyCount++;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Format line
|
|
65
|
+
const defaultMarker = isDefault ? '* ' : ' ';
|
|
66
|
+
const apiKeyStatus = hasApiKey ? '✓ API key set' : '✗ No API key';
|
|
67
|
+
const modelInfo = provider.model || 'not configured';
|
|
68
|
+
|
|
69
|
+
console.log(`${defaultMarker}${providerName.padEnd(18)} ${apiKeyStatus.padEnd(15)} ${modelInfo}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log('\n(* = default provider)\n');
|
|
73
|
+
|
|
74
|
+
// Summary
|
|
75
|
+
console.log(`Total: ${providers.length} provider${providers.length !== 1 ? 's' : ''} configured`);
|
|
76
|
+
console.log(`API keys: ${apiKeyCount} with API keys configured\n`);
|
|
77
|
+
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error.message.includes('Unexpected token') || error.message.includes('JSON')) {
|
|
80
|
+
console.error('\nError reading config: Invalid JSON format\n');
|
|
81
|
+
} else {
|
|
82
|
+
console.error('\nError:', error.message, '\n');
|
|
83
|
+
}
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Run if called directly
|
|
89
|
+
if (require.main === module) {
|
|
90
|
+
main();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = main;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Config command: set-api-key
|
|
4
|
+
* Set encrypted API key for a provider
|
|
5
|
+
*
|
|
6
|
+
* Usage: autopm config:set-api-key
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const { select, password } = require('@inquirer/prompts');
|
|
12
|
+
const ConfigManager = require('../../lib/config/ConfigManager');
|
|
13
|
+
|
|
14
|
+
// Default config path
|
|
15
|
+
const DEFAULT_CONFIG_PATH = path.join(os.homedir(), '.autopm', 'config.json');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Main function
|
|
19
|
+
*/
|
|
20
|
+
async function main() {
|
|
21
|
+
try {
|
|
22
|
+
const configPath = process.env.AUTOPM_CONFIG_PATH || DEFAULT_CONFIG_PATH;
|
|
23
|
+
|
|
24
|
+
// Check if config exists
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
if (!fs.existsSync(configPath)) {
|
|
27
|
+
console.error('\nError: Configuration not initialized');
|
|
28
|
+
console.error('Run: autopm config:init\n');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Load configuration
|
|
33
|
+
const manager = new ConfigManager(configPath);
|
|
34
|
+
|
|
35
|
+
// Get list of providers
|
|
36
|
+
const providers = manager.listProviders();
|
|
37
|
+
|
|
38
|
+
if (providers.length === 0) {
|
|
39
|
+
console.error('\nError: No providers configured');
|
|
40
|
+
console.error('Run: autopm config:set-provider <name>\n');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Build choices with current API key status
|
|
45
|
+
const choices = providers.map(provider => {
|
|
46
|
+
const hasKey = manager.hasApiKey(provider);
|
|
47
|
+
const status = hasKey ? '✓ API key set' : '✗ No API key';
|
|
48
|
+
return {
|
|
49
|
+
name: `${provider.padEnd(20)} ${status}`,
|
|
50
|
+
value: provider
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Prompt for provider selection
|
|
55
|
+
const selectedProvider = await select({
|
|
56
|
+
message: 'Select provider:',
|
|
57
|
+
choices
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Get master password (from env or prompt)
|
|
61
|
+
let masterPassword = process.env.AUTOPM_MASTER_PASSWORD;
|
|
62
|
+
|
|
63
|
+
if (!masterPassword) {
|
|
64
|
+
masterPassword = await password({
|
|
65
|
+
message: 'Enter master password:',
|
|
66
|
+
mask: '*'
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
manager.setMasterPassword(masterPassword);
|
|
71
|
+
|
|
72
|
+
// Prompt for API key
|
|
73
|
+
const apiKey = await password({
|
|
74
|
+
message: `Enter API key for ${selectedProvider}:`,
|
|
75
|
+
mask: '*'
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Validate API key not empty
|
|
79
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
80
|
+
console.error('\nError: API key cannot be empty');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Set and save API key
|
|
85
|
+
manager.setApiKey(selectedProvider, apiKey);
|
|
86
|
+
manager.save();
|
|
87
|
+
|
|
88
|
+
console.log(`\n✓ API key encrypted and stored for '${selectedProvider}'\n`);
|
|
89
|
+
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error.message.includes('cancelled') || error.message.includes('User force closed')) {
|
|
92
|
+
console.error('\nError: Operation cancelled by user');
|
|
93
|
+
} else if (error.message.includes('incorrect')) {
|
|
94
|
+
console.error('\nError: Incorrect master password');
|
|
95
|
+
} else {
|
|
96
|
+
console.error('\nError:', error.message);
|
|
97
|
+
}
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Run if called directly
|
|
103
|
+
if (require.main === module) {
|
|
104
|
+
main();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = main;
|