ai-summon 0.0.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/.claude/commands/speckit.analyze.md +184 -0
- package/.claude/commands/speckit.checklist.md +294 -0
- package/.claude/commands/speckit.clarify.md +177 -0
- package/.claude/commands/speckit.constitution.md +78 -0
- package/.claude/commands/speckit.implement.md +121 -0
- package/.claude/commands/speckit.plan.md +81 -0
- package/.claude/commands/speckit.specify.md +204 -0
- package/.claude/commands/speckit.tasks.md +108 -0
- package/.claude/settings.local.json +23 -0
- package/.prettierignore +5 -0
- package/.prettierrc.json +10 -0
- package/.specify/memory/constitution.md +72 -0
- package/.specify/scripts/bash/check-prerequisites.sh +166 -0
- package/.specify/scripts/bash/common.sh +113 -0
- package/.specify/scripts/bash/create-new-feature.sh +97 -0
- package/.specify/scripts/bash/setup-plan.sh +60 -0
- package/.specify/scripts/bash/update-agent-context.sh +738 -0
- package/.specify/templates/agent-file-template.md +28 -0
- package/.specify/templates/checklist-template.md +40 -0
- package/.specify/templates/plan-template.md +111 -0
- package/.specify/templates/spec-template.md +115 -0
- package/.specify/templates/tasks-template.md +250 -0
- package/CLAUDE.md +199 -0
- package/PRD.md +268 -0
- package/README.md +171 -0
- package/dist/ai-summon.d.ts +2 -0
- package/dist/ai-summon.js +73 -0
- package/dist/commands/ide/index.d.ts +3 -0
- package/dist/commands/ide/index.js +253 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +55 -0
- package/dist/commands/url.d.ts +4 -0
- package/dist/commands/url.js +223 -0
- package/dist/types/index.d.ts +40 -0
- package/dist/types/index.js +1 -0
- package/dist/util.d.ts +16 -0
- package/dist/util.js +109 -0
- package/eslint.config.js +47 -0
- package/package.json +47 -0
- package/specs/001-cloud-login-feature/contracts/cloud-command.ts +82 -0
- package/specs/001-cloud-login-feature/contracts/config-service.ts +170 -0
- package/specs/001-cloud-login-feature/data-model.md +269 -0
- package/specs/001-cloud-login-feature/plan.md +91 -0
- package/specs/001-cloud-login-feature/quickstart.md +366 -0
- package/specs/001-cloud-login-feature/research.md +290 -0
- package/specs/001-cloud-login-feature/spec.md +195 -0
- package/specs/001-cloud-login-feature/tasks.md +235 -0
- package/specs/001-cloud-scp-command/contracts/cloud-scp-api.ts +402 -0
- package/specs/001-cloud-scp-command/data-model.md +424 -0
- package/specs/001-cloud-scp-command/plan.md +124 -0
- package/specs/001-cloud-scp-command/quickstart.md +536 -0
- package/specs/001-cloud-scp-command/research.md +345 -0
- package/specs/001-cloud-scp-command/spec.md +248 -0
- package/specs/001-cloud-scp-command/tasks.md +434 -0
- package/src/ai-summon.ts +88 -0
- package/src/commands/ide/index.ts +322 -0
- package/src/commands/init.ts +64 -0
- package/src/commands/url.ts +262 -0
- package/src/types/index.ts +49 -0
- package/src/util.ts +146 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# Quickstart: Cloud Login Implementation
|
|
2
|
+
|
|
3
|
+
**Feature**: Cloud Login Command | **Generated**: 2025-10-11 | **Phase**: 1
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Quick implementation guide for the `hsh cloud login` feature, providing step-by-step development instructions and validation checkpoints.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- TypeScript 5.0+ development environment
|
|
12
|
+
- Existing hsh CLI project structure
|
|
13
|
+
- Access to cloud instances with SSH keys
|
|
14
|
+
- yarn package manager
|
|
15
|
+
|
|
16
|
+
## Implementation Steps
|
|
17
|
+
|
|
18
|
+
### Step 1: Update Type Definitions
|
|
19
|
+
|
|
20
|
+
**File**: `src/types/index.ts`
|
|
21
|
+
|
|
22
|
+
Add new TypeScript interfaces for cloud configuration:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// Add to existing types
|
|
26
|
+
export interface CloudConfig {
|
|
27
|
+
ip: string;
|
|
28
|
+
privateKeyFile: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ServiceConfig {
|
|
32
|
+
dev: CloudConfig;
|
|
33
|
+
staging: CloudConfig;
|
|
34
|
+
prod: CloudConfig;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface YirenConfig {
|
|
38
|
+
[serviceName: string]: ServiceConfig;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface HshConfig {
|
|
42
|
+
repos: {
|
|
43
|
+
[groupName: string]: {
|
|
44
|
+
[repoName: string]: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
yiren: YirenConfig;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type Environment = 'dev' | 'staging' | 'prod';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Validation**: TypeScript compilation succeeds with no type errors.
|
|
54
|
+
|
|
55
|
+
### Step 2: Update Configuration Utility
|
|
56
|
+
|
|
57
|
+
**File**: `src/util.ts`
|
|
58
|
+
|
|
59
|
+
Update the configuration reading function to support new structure:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { HshConfig } from './types/index.js';
|
|
63
|
+
|
|
64
|
+
export async function readConfig(): Promise<HshConfig> {
|
|
65
|
+
const configPath = path.join(os.homedir(), '.hsh', 'config.json');
|
|
66
|
+
|
|
67
|
+
if (!existsSync(configPath)) {
|
|
68
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const configContent = await readFile(configPath, 'utf-8');
|
|
72
|
+
const config = JSON.parse(configContent);
|
|
73
|
+
|
|
74
|
+
// Auto-migrate legacy configuration
|
|
75
|
+
if (!config.repos && !config.yiren) {
|
|
76
|
+
return {
|
|
77
|
+
repos: config,
|
|
78
|
+
yiren: {},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return config as HshConfig;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Validation**: Configuration reading works with both old and new formats.
|
|
87
|
+
|
|
88
|
+
### Step 3: Create Cloud Command Module
|
|
89
|
+
|
|
90
|
+
**File**: `src/commands/cloud.ts`
|
|
91
|
+
|
|
92
|
+
Implement the cloud login functionality:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { $ } from 'zx';
|
|
96
|
+
import chalk from 'chalk';
|
|
97
|
+
import inquirer from 'inquirer';
|
|
98
|
+
import { readConfig } from '../util.js';
|
|
99
|
+
import { Environment, CloudConfig } from '../types/index.js';
|
|
100
|
+
|
|
101
|
+
export async function cloudLogin(env?: Environment, service?: string) {
|
|
102
|
+
try {
|
|
103
|
+
const config = await readConfig();
|
|
104
|
+
|
|
105
|
+
// Interactive prompts for missing parameters
|
|
106
|
+
if (!service) {
|
|
107
|
+
const availableServices = Object.keys(config.yiren);
|
|
108
|
+
if (availableServices.length === 0) {
|
|
109
|
+
console.error(chalk.red('❌ No cloud services configured'));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { selectedService } = await inquirer.prompt([
|
|
114
|
+
{
|
|
115
|
+
type: 'list',
|
|
116
|
+
name: 'selectedService',
|
|
117
|
+
message: 'Select service:',
|
|
118
|
+
choices: availableServices,
|
|
119
|
+
},
|
|
120
|
+
]);
|
|
121
|
+
service = selectedService;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!env) {
|
|
125
|
+
const { selectedEnv } = await inquirer.prompt([
|
|
126
|
+
{
|
|
127
|
+
type: 'list',
|
|
128
|
+
name: 'selectedEnv',
|
|
129
|
+
message: 'Select environment:',
|
|
130
|
+
choices: ['dev', 'staging', 'prod'],
|
|
131
|
+
},
|
|
132
|
+
]);
|
|
133
|
+
env = selectedEnv;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validate configuration exists
|
|
137
|
+
const serviceConfig = config.yiren[service];
|
|
138
|
+
if (!serviceConfig) {
|
|
139
|
+
console.error(chalk.red(`❌ Service '${service}' not found in configuration`));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const cloudConfig = serviceConfig[env];
|
|
144
|
+
if (!cloudConfig) {
|
|
145
|
+
console.error(chalk.red(`❌ Environment '${env}' not configured for service '${service}'`));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Validate private key file
|
|
150
|
+
await validatePrivateKey(cloudConfig.privateKeyFile);
|
|
151
|
+
|
|
152
|
+
// Execute SSH connection
|
|
153
|
+
console.log(chalk.blue(`🔗 Connecting to ${service} (${env}): ${cloudConfig.ip}`));
|
|
154
|
+
await $`ssh -i ${cloudConfig.privateKeyFile} -o ConnectTimeout=10 -o StrictHostKeyChecking=no root@${cloudConfig.ip}`;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
handleSSHError(error);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function validatePrivateKey(privateKeyFile: string): Promise<void> {
|
|
162
|
+
// Implementation from research.md
|
|
163
|
+
// Check file existence and permissions
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function handleSSHError(error: any): void {
|
|
167
|
+
// Implementation from research.md
|
|
168
|
+
// Provide user-friendly error messages
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Validation**: SSH connections work with valid configuration.
|
|
173
|
+
|
|
174
|
+
### Step 4: Update Main CLI Entry
|
|
175
|
+
|
|
176
|
+
**File**: `src/hsh.ts`
|
|
177
|
+
|
|
178
|
+
Add the cloud command structure to the CLI:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { cloudLogin } from './commands/cloud.js';
|
|
182
|
+
|
|
183
|
+
// Add after existing commands
|
|
184
|
+
const cloudCommand = program
|
|
185
|
+
.command('cloud')
|
|
186
|
+
.description('Cloud infrastructure management commands');
|
|
187
|
+
|
|
188
|
+
// Add login subcommand
|
|
189
|
+
cloudCommand
|
|
190
|
+
.command('login')
|
|
191
|
+
.description('SSH into cloud instances')
|
|
192
|
+
.option('--env <environment>', 'Environment: dev, staging, or prod')
|
|
193
|
+
.option('--service <service>', 'Service name (e.g., todo-mini, wuhan-mall)')
|
|
194
|
+
.action(async (options) => {
|
|
195
|
+
await cloudLogin(options.env, options.service);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Future subcommands can be added like:
|
|
199
|
+
// cloudCommand
|
|
200
|
+
// .command('scp')
|
|
201
|
+
// .description('Copy files to/from cloud instances')
|
|
202
|
+
// .action(async (options) => { ... });
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Validation**: Command appears in `hsh --help` output with proper subcommand structure.
|
|
206
|
+
|
|
207
|
+
### Step 5: Update IDE Commands for New Config
|
|
208
|
+
|
|
209
|
+
**File**: `src/commands/ide.ts`
|
|
210
|
+
|
|
211
|
+
Update existing IDE commands to use `config.repos`:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Update existing functions to use config.repos instead of direct config access
|
|
215
|
+
export async function openCursor() {
|
|
216
|
+
const config = await readConfig();
|
|
217
|
+
const projects = config.repos; // Updated from direct config access
|
|
218
|
+
// Rest of implementation unchanged
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function openSurf() {
|
|
222
|
+
const config = await readConfig();
|
|
223
|
+
const projects = config.repos; // Updated from direct config access
|
|
224
|
+
// Rest of implementation unchanged
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Validation**: Existing `cursor` and `surf` commands continue to work.
|
|
229
|
+
|
|
230
|
+
## Configuration Setup
|
|
231
|
+
|
|
232
|
+
### Sample Configuration File
|
|
233
|
+
|
|
234
|
+
Create or update `~/.ai/config.json`:
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"repos": {
|
|
239
|
+
"personal": {
|
|
240
|
+
"hsh-tool": "/Users/username/projects/hsh"
|
|
241
|
+
},
|
|
242
|
+
"work": {
|
|
243
|
+
"api-service": "/Users/username/work/api-service"
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"yiren": {
|
|
247
|
+
"todo-mini": {
|
|
248
|
+
"dev": {
|
|
249
|
+
"ip": "192.168.1.10",
|
|
250
|
+
"privateKeyFile": "/Users/username/.ssh/todo-mini-dev.pem"
|
|
251
|
+
},
|
|
252
|
+
"staging": {
|
|
253
|
+
"ip": "192.168.1.20",
|
|
254
|
+
"privateKeyFile": "/Users/username/.ssh/todo-mini-staging.pem"
|
|
255
|
+
},
|
|
256
|
+
"prod": {
|
|
257
|
+
"ip": "192.168.1.30",
|
|
258
|
+
"privateKeyFile": "/Users/username/.ssh/todo-mini-prod.pem"
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"wuhan-mall": {
|
|
262
|
+
"dev": {
|
|
263
|
+
"ip": "192.168.2.10",
|
|
264
|
+
"privateKeyFile": "/Users/username/.ssh/wuhan-mall-dev.pem"
|
|
265
|
+
},
|
|
266
|
+
"staging": {
|
|
267
|
+
"ip": "192.168.2.20",
|
|
268
|
+
"privateKeyFile": "/Users/username/.ssh/wuhan-mall-staging.pem"
|
|
269
|
+
},
|
|
270
|
+
"prod": {
|
|
271
|
+
"ip": "192.168.2.30",
|
|
272
|
+
"privateKeyFile": "/Users/username/.ssh/wuhan-mall-prod.pem"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### SSH Key Setup
|
|
280
|
+
|
|
281
|
+
Ensure SSH private key files have correct permissions:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
chmod 600 /path/to/private-key-file.pem
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Testing
|
|
288
|
+
|
|
289
|
+
### Manual Testing Checklist
|
|
290
|
+
|
|
291
|
+
- [ ] `hsh cloud --help` shows available subcommands
|
|
292
|
+
- [ ] `hsh cloud login --help` shows login-specific options
|
|
293
|
+
- [ ] `hsh cloud login --env dev --service todo-mini` connects successfully
|
|
294
|
+
- [ ] `hsh cloud login` prompts for missing parameters
|
|
295
|
+
- [ ] Invalid service name shows helpful error message
|
|
296
|
+
- [ ] Invalid environment shows helpful error message
|
|
297
|
+
- [ ] Missing private key file shows helpful error message
|
|
298
|
+
- [ ] Wrong private key permissions shows warning with fix command
|
|
299
|
+
- [ ] Existing `hsh cursor` command still works with new config structure
|
|
300
|
+
- [ ] Existing `hsh surf` command still works with new config structure
|
|
301
|
+
- [ ] Legacy configuration automatically migrates to new structure
|
|
302
|
+
|
|
303
|
+
### Error Scenarios
|
|
304
|
+
|
|
305
|
+
Test error handling for:
|
|
306
|
+
|
|
307
|
+
- Configuration file not found
|
|
308
|
+
- Invalid JSON in configuration
|
|
309
|
+
- Network connectivity issues
|
|
310
|
+
- SSH authentication failures
|
|
311
|
+
- Missing private key files
|
|
312
|
+
|
|
313
|
+
## Build and Deploy
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Build TypeScript
|
|
317
|
+
yarn build
|
|
318
|
+
|
|
319
|
+
# Install globally for testing
|
|
320
|
+
yarn build:install
|
|
321
|
+
|
|
322
|
+
# Test commands
|
|
323
|
+
hsh cloud --help
|
|
324
|
+
hsh cloud login --help
|
|
325
|
+
hsh cloud login --env dev --service todo-mini
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Success Criteria
|
|
329
|
+
|
|
330
|
+
✅ **Functional Requirements**:
|
|
331
|
+
|
|
332
|
+
- Cloud login command executes SSH connections
|
|
333
|
+
- Configuration structure supports both repos and cloud configs
|
|
334
|
+
- Backward compatibility maintained for existing commands
|
|
335
|
+
|
|
336
|
+
✅ **Technical Requirements**:
|
|
337
|
+
|
|
338
|
+
- TypeScript strict mode compliance
|
|
339
|
+
- zx library usage for shell commands
|
|
340
|
+
- inquirer prompts for user interaction
|
|
341
|
+
- Chalk colored output for feedback
|
|
342
|
+
|
|
343
|
+
✅ **Quality Requirements**:
|
|
344
|
+
|
|
345
|
+
- Comprehensive error handling
|
|
346
|
+
- Input validation and helpful messages
|
|
347
|
+
- Security best practices for SSH keys
|
|
348
|
+
- Performance targets met (<2 seconds command execution)
|
|
349
|
+
|
|
350
|
+
## Next Steps
|
|
351
|
+
|
|
352
|
+
After implementation:
|
|
353
|
+
|
|
354
|
+
1. Run comprehensive testing with various configuration scenarios
|
|
355
|
+
2. Update documentation for new command usage
|
|
356
|
+
3. Consider adding bash completion for service and environment names
|
|
357
|
+
4. Plan additional cloud management features (status, logs, etc.)
|
|
358
|
+
|
|
359
|
+
## Troubleshooting
|
|
360
|
+
|
|
361
|
+
Common issues and solutions:
|
|
362
|
+
|
|
363
|
+
- **TypeScript errors**: Ensure all imports use `.js` extensions
|
|
364
|
+
- **Configuration not found**: Check file path and permissions
|
|
365
|
+
- **SSH connection fails**: Verify network connectivity and key permissions
|
|
366
|
+
- **Command not found**: Rebuild and reinstall globally with `yarn build:install`
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Research: Cloud Login Implementation
|
|
2
|
+
|
|
3
|
+
**Generated**: 2025-10-11 | **Phase**: 0 | **Feature**: Cloud Login Command
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Research for implementing `hsh cloud login` command with SSH connections, configuration migration, and error handling best practices.
|
|
8
|
+
|
|
9
|
+
## 1. SSH Connection Management with zx
|
|
10
|
+
|
|
11
|
+
### Decision: Use zx with proper SSH execution patterns
|
|
12
|
+
|
|
13
|
+
**Context**: Need to execute SSH commands using zx library following project constitution.
|
|
14
|
+
|
|
15
|
+
**Research Findings**:
|
|
16
|
+
|
|
17
|
+
- zx provides `$` template literal syntax for shell commands
|
|
18
|
+
- SSH connections require handling of interactive sessions and proper error handling
|
|
19
|
+
- Best practice: Use `$` with proper argument escaping and timeout handling
|
|
20
|
+
|
|
21
|
+
**Implementation Pattern**:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { $ } from 'zx';
|
|
25
|
+
|
|
26
|
+
// SSH execution with zx
|
|
27
|
+
await $`ssh -i ${privateKeyFile} -o ConnectTimeout=10 -o StrictHostKeyChecking=no root@${ip}`;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Rationale**: zx provides consistent cross-platform shell execution with TypeScript integration. The `-o ConnectTimeout=10` prevents hanging connections, and `-o StrictHostKeyChecking=no` avoids interactive prompts.
|
|
31
|
+
|
|
32
|
+
**Alternatives Considered**:
|
|
33
|
+
|
|
34
|
+
- Direct `child_process.spawn()`: Rejected due to constitution requirement for zx usage
|
|
35
|
+
- SSH libraries (ssh2): Rejected to avoid additional dependencies and complexity
|
|
36
|
+
|
|
37
|
+
## 2. Configuration File Migration Strategy
|
|
38
|
+
|
|
39
|
+
### Decision: Backward-compatible configuration reader with automatic migration
|
|
40
|
+
|
|
41
|
+
**Context**: Need to support both old flat structure and new nested structure for IDE commands.
|
|
42
|
+
|
|
43
|
+
**Research Findings**:
|
|
44
|
+
|
|
45
|
+
- Configuration migration should be transparent to users
|
|
46
|
+
- Support both structures during transition period
|
|
47
|
+
- Provide clear migration path and warnings
|
|
48
|
+
|
|
49
|
+
**Implementation Strategy**:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
interface LegacyConfig {
|
|
53
|
+
[category: string]: {
|
|
54
|
+
[project: string]: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface NewConfig {
|
|
59
|
+
repos: {
|
|
60
|
+
[category: string]: {
|
|
61
|
+
[project: string]: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
yiren: {
|
|
65
|
+
[service: string]: {
|
|
66
|
+
[env: string]: {
|
|
67
|
+
ip: string;
|
|
68
|
+
privateKeyFile: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function migrateConfig(config: any): NewConfig {
|
|
75
|
+
// Detect old format and migrate automatically
|
|
76
|
+
if (!config.repos && !config.yiren) {
|
|
77
|
+
return {
|
|
78
|
+
repos: config,
|
|
79
|
+
yiren: {},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return config as NewConfig;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Rationale**: Automatic migration maintains backward compatibility while enabling new features. Users don't need to manually update configurations.
|
|
87
|
+
|
|
88
|
+
**Alternatives Considered**:
|
|
89
|
+
|
|
90
|
+
- Breaking change requiring manual migration: Rejected to avoid user friction
|
|
91
|
+
- Supporting both formats permanently: Rejected due to complexity maintenance
|
|
92
|
+
|
|
93
|
+
## 3. Error Handling Patterns for SSH Connections
|
|
94
|
+
|
|
95
|
+
### Decision: Comprehensive error handling with user-friendly messages
|
|
96
|
+
|
|
97
|
+
**Context**: SSH connections can fail for various reasons (network, authentication, host unreachable).
|
|
98
|
+
|
|
99
|
+
**Research Findings**:
|
|
100
|
+
|
|
101
|
+
- SSH failures should provide actionable error messages
|
|
102
|
+
- Common failure modes: connection timeout, authentication failure, host unreachable
|
|
103
|
+
- Use chalk for colored error output following project patterns
|
|
104
|
+
|
|
105
|
+
**Error Handling Strategy**:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import chalk from 'chalk';
|
|
109
|
+
|
|
110
|
+
async function executeSSH(ip: string, privateKeyFile: string) {
|
|
111
|
+
try {
|
|
112
|
+
await $`ssh -i ${privateKeyFile} -o ConnectTimeout=10 -o StrictHostKeyChecking=no root@${ip}`;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error.message.includes('Connection timed out')) {
|
|
115
|
+
console.error(chalk.red('❌ Connection timeout - check network connectivity and IP address'));
|
|
116
|
+
} else if (error.message.includes('Permission denied')) {
|
|
117
|
+
console.error(
|
|
118
|
+
chalk.red('❌ Authentication failed - check private key file permissions and path')
|
|
119
|
+
);
|
|
120
|
+
} else if (error.message.includes('No route to host')) {
|
|
121
|
+
console.error(chalk.red('❌ Host unreachable - verify IP address and network access'));
|
|
122
|
+
} else {
|
|
123
|
+
console.error(chalk.red(`❌ SSH connection failed: ${error.message}`));
|
|
124
|
+
}
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Rationale**: Specific error messages help users diagnose and fix connection issues quickly. Using chalk for colored output follows project conventions.
|
|
131
|
+
|
|
132
|
+
**Alternatives Considered**:
|
|
133
|
+
|
|
134
|
+
- Generic error messages: Rejected as unhelpful for debugging
|
|
135
|
+
- Silent failures: Rejected as users need feedback on connection status
|
|
136
|
+
|
|
137
|
+
## 4. Command Parameter Validation
|
|
138
|
+
|
|
139
|
+
### Decision: Use inquirer for interactive prompts with validation
|
|
140
|
+
|
|
141
|
+
**Context**: Need to validate environment and service parameters, provide user guidance.
|
|
142
|
+
|
|
143
|
+
**Research Findings**:
|
|
144
|
+
|
|
145
|
+
- inquirer provides excellent autocomplete and validation capabilities
|
|
146
|
+
- Should offer interactive selection when parameters are missing
|
|
147
|
+
- Validate against available options in configuration
|
|
148
|
+
|
|
149
|
+
**Implementation Pattern**:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import inquirer from 'inquirer';
|
|
153
|
+
|
|
154
|
+
async function promptForMissingParams(config: NewConfig, env?: string, service?: string) {
|
|
155
|
+
const questions = [];
|
|
156
|
+
|
|
157
|
+
if (!service) {
|
|
158
|
+
questions.push({
|
|
159
|
+
type: 'list',
|
|
160
|
+
name: 'service',
|
|
161
|
+
message: 'Select service:',
|
|
162
|
+
choices: Object.keys(config.yiren),
|
|
163
|
+
validate: (input: string) => Object.keys(config.yiren).includes(input),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!env) {
|
|
168
|
+
questions.push({
|
|
169
|
+
type: 'list',
|
|
170
|
+
name: 'env',
|
|
171
|
+
message: 'Select environment:',
|
|
172
|
+
choices: ['dev', 'staging', 'prod'],
|
|
173
|
+
validate: (input: string) => ['dev', 'staging', 'prod'].includes(input),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return await inquirer.prompt(questions);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Rationale**: Interactive prompts reduce user errors and provide guidance. Validation ensures only valid options are accepted.
|
|
182
|
+
|
|
183
|
+
**Alternatives Considered**:
|
|
184
|
+
|
|
185
|
+
- Command-line flags only: Rejected as less user-friendly for discovery
|
|
186
|
+
- No validation: Rejected as it leads to runtime errors
|
|
187
|
+
|
|
188
|
+
## 5. Private Key File Security
|
|
189
|
+
|
|
190
|
+
### Decision: Validate private key file permissions and existence
|
|
191
|
+
|
|
192
|
+
**Context**: SSH private keys must have correct permissions and exist for security.
|
|
193
|
+
|
|
194
|
+
**Research Findings**:
|
|
195
|
+
|
|
196
|
+
- SSH requires private key files to have 600 permissions (readable by owner only)
|
|
197
|
+
- Files should exist and be readable before attempting SSH connection
|
|
198
|
+
- Provide clear guidance on fixing permission issues
|
|
199
|
+
|
|
200
|
+
**Security Validation**:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { access, stat } from 'fs/promises';
|
|
204
|
+
import { constants } from 'fs';
|
|
205
|
+
|
|
206
|
+
async function validatePrivateKey(privateKeyFile: string): Promise<void> {
|
|
207
|
+
try {
|
|
208
|
+
// Check if file exists
|
|
209
|
+
await access(privateKeyFile, constants.F_OK);
|
|
210
|
+
|
|
211
|
+
// Check permissions
|
|
212
|
+
const stats = await stat(privateKeyFile);
|
|
213
|
+
const permissions = stats.mode & parseInt('777', 8);
|
|
214
|
+
|
|
215
|
+
if (permissions !== parseInt('600', 8)) {
|
|
216
|
+
console.warn(
|
|
217
|
+
chalk.yellow(
|
|
218
|
+
`⚠️ Private key file permissions should be 600. Run: chmod 600 ${privateKeyFile}`
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
throw new Error(`Private key file not found or not accessible: ${privateKeyFile}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Rationale**: Proactive validation prevents common SSH authentication failures and provides actionable guidance.
|
|
229
|
+
|
|
230
|
+
**Alternatives Considered**:
|
|
231
|
+
|
|
232
|
+
- No permission checking: Rejected as it leads to confusing SSH failures
|
|
233
|
+
- Automatic permission fixing: Rejected as it modifies user files without permission
|
|
234
|
+
|
|
235
|
+
## 6. TypeScript Type Definitions
|
|
236
|
+
|
|
237
|
+
### Decision: Comprehensive type definitions for configuration and parameters
|
|
238
|
+
|
|
239
|
+
**Context**: Project requires strict TypeScript typing following constitution.
|
|
240
|
+
|
|
241
|
+
**Type Definitions**:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
export interface CloudConfig {
|
|
245
|
+
ip: string;
|
|
246
|
+
privateKeyFile: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface ServiceConfig {
|
|
250
|
+
dev: CloudConfig;
|
|
251
|
+
staging: CloudConfig;
|
|
252
|
+
prod: CloudConfig;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface YirenConfig {
|
|
256
|
+
[serviceName: string]: ServiceConfig;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface HshConfig {
|
|
260
|
+
repos: {
|
|
261
|
+
[groupName: string]: {
|
|
262
|
+
[repoName: string]: string;
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
yiren: YirenConfig;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export type Environment = 'dev' | 'staging' | 'prod';
|
|
269
|
+
export type ServiceName = string;
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Rationale**: Strong typing prevents runtime errors and enables better IDE support and refactoring capabilities.
|
|
273
|
+
|
|
274
|
+
**Alternatives Considered**:
|
|
275
|
+
|
|
276
|
+
- Loose typing with any: Rejected due to constitution requirements
|
|
277
|
+
- Inline types: Rejected for maintainability and reusability
|
|
278
|
+
|
|
279
|
+
## Implementation Roadmap
|
|
280
|
+
|
|
281
|
+
1. **Configuration Management**: Update util.ts with new config structure support
|
|
282
|
+
2. **Type Definitions**: Add new types to types/index.ts
|
|
283
|
+
3. **Cloud Command Module**: Create src/commands/cloud.ts with SSH functionality
|
|
284
|
+
4. **CLI Integration**: Update src/hsh.ts to register cloud commands
|
|
285
|
+
5. **IDE Command Updates**: Modify src/commands/ide.ts for new config structure
|
|
286
|
+
6. **Testing**: Manual testing with various configuration scenarios
|
|
287
|
+
|
|
288
|
+
## Conclusion
|
|
289
|
+
|
|
290
|
+
All technical questions have been resolved with specific implementation patterns. The approach maintains backward compatibility, follows project constitution requirements, and provides excellent user experience through proper error handling and validation.
|