@mexty/cli 1.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.
Files changed (49) hide show
  1. package/README.md +427 -0
  2. package/dist/commands/create.d.ts +7 -0
  3. package/dist/commands/create.d.ts.map +1 -0
  4. package/dist/commands/create.js +80 -0
  5. package/dist/commands/create.js.map +1 -0
  6. package/dist/commands/delete.d.ts +2 -0
  7. package/dist/commands/delete.d.ts.map +1 -0
  8. package/dist/commands/delete.js +54 -0
  9. package/dist/commands/delete.js.map +1 -0
  10. package/dist/commands/fork.d.ts +2 -0
  11. package/dist/commands/fork.d.ts.map +1 -0
  12. package/dist/commands/fork.js +52 -0
  13. package/dist/commands/fork.js.map +1 -0
  14. package/dist/commands/login.d.ts +2 -0
  15. package/dist/commands/login.d.ts.map +1 -0
  16. package/dist/commands/login.js +12 -0
  17. package/dist/commands/login.js.map +1 -0
  18. package/dist/commands/publish.d.ts +2 -0
  19. package/dist/commands/publish.d.ts.map +1 -0
  20. package/dist/commands/publish.js +139 -0
  21. package/dist/commands/publish.js.map +1 -0
  22. package/dist/commands/sync.d.ts +2 -0
  23. package/dist/commands/sync.d.ts.map +1 -0
  24. package/dist/commands/sync.js +140 -0
  25. package/dist/commands/sync.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +60 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/utils/api.d.ts +55 -0
  31. package/dist/utils/api.d.ts.map +1 -0
  32. package/dist/utils/api.js +68 -0
  33. package/dist/utils/api.js.map +1 -0
  34. package/dist/utils/git.d.ts +41 -0
  35. package/dist/utils/git.d.ts.map +1 -0
  36. package/dist/utils/git.js +171 -0
  37. package/dist/utils/git.js.map +1 -0
  38. package/package.json +39 -0
  39. package/src/commands/create.ts +97 -0
  40. package/src/commands/delete.ts +63 -0
  41. package/src/commands/fork.ts +58 -0
  42. package/src/commands/login.ts +104 -0
  43. package/src/commands/publish.ts +159 -0
  44. package/src/commands/sync.ts +284 -0
  45. package/src/index.ts +84 -0
  46. package/src/utils/api.ts +240 -0
  47. package/src/utils/auth.ts +21 -0
  48. package/src/utils/git.ts +194 -0
  49. package/tsconfig.json +24 -0
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GitManager = void 0;
7
+ const simple_git_1 = __importDefault(require("simple-git"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ // Simple spinner implementation since ora v5 has import issues
11
+ class SimpleSpinner {
12
+ constructor(message) {
13
+ this.interval = null;
14
+ this.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
15
+ this.currentFrame = 0;
16
+ this.message = message;
17
+ }
18
+ start() {
19
+ process.stdout.write(this.message);
20
+ this.interval = setInterval(() => {
21
+ process.stdout.write(`\r${this.frames[this.currentFrame]} ${this.message}`);
22
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length;
23
+ }, 80);
24
+ return this;
25
+ }
26
+ succeed(message) {
27
+ this.stop();
28
+ console.log(`\r✅ ${message}`);
29
+ }
30
+ fail(message) {
31
+ this.stop();
32
+ console.log(`\r❌ ${message}`);
33
+ }
34
+ stop() {
35
+ if (this.interval) {
36
+ clearInterval(this.interval);
37
+ this.interval = null;
38
+ }
39
+ process.stdout.write('\r');
40
+ }
41
+ }
42
+ function ora(message) {
43
+ return new SimpleSpinner(message);
44
+ }
45
+ class GitManager {
46
+ constructor(cwd) {
47
+ this.git = (0, simple_git_1.default)(cwd);
48
+ }
49
+ /**
50
+ * Clone a repository to a local directory
51
+ */
52
+ async cloneRepository(repoUrl, targetDir) {
53
+ const spinner = ora(`Cloning repository from ${repoUrl}...`).start();
54
+ try {
55
+ // Ensure target directory doesn't exist
56
+ if (fs_1.default.existsSync(targetDir)) {
57
+ spinner.fail(chalk_1.default.red(`Directory ${targetDir} already exists`));
58
+ throw new Error(`Directory ${targetDir} already exists`);
59
+ }
60
+ // Clone the repository
61
+ await this.git.clone(repoUrl, targetDir);
62
+ spinner.succeed(chalk_1.default.green(`Repository cloned to ${targetDir}`));
63
+ }
64
+ catch (error) {
65
+ spinner.fail(chalk_1.default.red(`Failed to clone repository: ${error.message}`));
66
+ throw error;
67
+ }
68
+ }
69
+ /**
70
+ * Check if current directory is a Git repository
71
+ */
72
+ async isGitRepository(dir) {
73
+ try {
74
+ const git = dir ? (0, simple_git_1.default)(dir) : this.git;
75
+ await git.status();
76
+ return true;
77
+ }
78
+ catch (error) {
79
+ return false;
80
+ }
81
+ }
82
+ /**
83
+ * Get the current repository's remote URL
84
+ */
85
+ async getRemoteUrl(dir) {
86
+ try {
87
+ const git = dir ? (0, simple_git_1.default)(dir) : this.git;
88
+ const remotes = await git.getRemotes(true);
89
+ const origin = remotes.find(remote => remote.name === 'origin');
90
+ return origin?.refs?.fetch || null;
91
+ }
92
+ catch (error) {
93
+ return null;
94
+ }
95
+ }
96
+ /**
97
+ * Check if there are uncommitted changes
98
+ */
99
+ async hasUncommittedChanges(dir) {
100
+ try {
101
+ const git = dir ? (0, simple_git_1.default)(dir) : this.git;
102
+ const status = await git.status();
103
+ return !status.isClean();
104
+ }
105
+ catch (error) {
106
+ return false;
107
+ }
108
+ }
109
+ /**
110
+ * Push current branch to remote
111
+ */
112
+ async pushToRemote(dir) {
113
+ const spinner = ora('Pushing changes to remote repository...').start();
114
+ try {
115
+ const git = dir ? (0, simple_git_1.default)(dir) : this.git;
116
+ // Get current branch
117
+ const status = await git.status();
118
+ const currentBranch = status.current;
119
+ if (!currentBranch) {
120
+ throw new Error('No current branch found');
121
+ }
122
+ // Push to remote
123
+ await git.push('origin', currentBranch);
124
+ spinner.succeed(chalk_1.default.green('Changes pushed to remote repository'));
125
+ }
126
+ catch (error) {
127
+ spinner.fail(chalk_1.default.red(`Failed to push changes: ${error.message}`));
128
+ throw error;
129
+ }
130
+ }
131
+ /**
132
+ * Get repository information
133
+ */
134
+ async getRepositoryInfo(dir) {
135
+ const git = dir ? (0, simple_git_1.default)(dir) : this.git;
136
+ const status = await git.status();
137
+ const remoteUrl = await this.getRemoteUrl(dir);
138
+ return {
139
+ branch: status.current || 'unknown',
140
+ remoteUrl,
141
+ hasChanges: !status.isClean()
142
+ };
143
+ }
144
+ /**
145
+ * Extract repository name from URL
146
+ */
147
+ static extractRepoName(gitUrl) {
148
+ // Handle both SSH and HTTPS URLs
149
+ const match = gitUrl.match(/\/([^\/]+?)(?:\.git)?$/);
150
+ if (match) {
151
+ return match[1];
152
+ }
153
+ // Fallback: use the last part of the URL
154
+ const parts = gitUrl.split('/');
155
+ return parts[parts.length - 1].replace('.git', '');
156
+ }
157
+ /**
158
+ * Validate Git URL format
159
+ */
160
+ static isValidGitUrl(url) {
161
+ const patterns = [
162
+ /^https:\/\/github\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
163
+ /^git@github\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
164
+ /^https:\/\/gitlab\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
165
+ /^git@gitlab\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/
166
+ ];
167
+ return patterns.some(pattern => pattern.test(url));
168
+ }
169
+ }
170
+ exports.GitManager = GitManager;
171
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@mexty/cli",
3
+ "version": "1.0.1",
4
+ "description": "MEXT CLI for managing blocks and repositories",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "mexty": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "dependencies": {
15
+ "commander": "^11.1.0",
16
+ "axios": "^1.6.0",
17
+ "chalk": "^4.1.2",
18
+ "simple-git": "^3.20.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.0.0",
22
+ "typescript": "^5.0.0"
23
+ },
24
+ "engines": {
25
+ "node": ">=16.0.0"
26
+ },
27
+ "keywords": [
28
+ "mext",
29
+ "cli",
30
+ "blocks",
31
+ "federation",
32
+ "webpack"
33
+ ],
34
+ "author": "MEXT Team",
35
+ "license": "MIT",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }
@@ -0,0 +1,97 @@
1
+ import chalk from 'chalk';
2
+ import path from 'path';
3
+ import { apiClient, CreateBlockRequest } from '../utils/api';
4
+ import { GitManager } from '../utils/git';
5
+ import { createInterface } from 'readline';
6
+ import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
7
+
8
+ interface CreateOptions {
9
+ description?: string;
10
+ type?: string;
11
+ }
12
+
13
+ // Simple prompt function to replace inquirer
14
+ async function prompt(question: string, defaultValue?: string): Promise<string> {
15
+ return new Promise((resolve) => {
16
+ const rl = createInterface({
17
+ input: process.stdin,
18
+ output: process.stdout
19
+ });
20
+
21
+ const promptText = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
22
+
23
+ rl.question(promptText, (answer) => {
24
+ rl.close();
25
+ resolve(answer.trim() || defaultValue || '');
26
+ });
27
+ });
28
+ }
29
+
30
+ export async function createCommand(name: string, options: CreateOptions): Promise<void> {
31
+ try {
32
+ // Check authentication first
33
+ requireAuthentication();
34
+
35
+ const user = getAuthenticatedUser();
36
+ console.log(chalk.blue(`🚀 Creating new block: ${name}`));
37
+ console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
38
+
39
+ // Get description if not provided
40
+ let description = options.description;
41
+ if (!description) {
42
+ description = await prompt('Enter a description for the block', `Custom block: ${name}`);
43
+ }
44
+
45
+ // Prepare block data
46
+ const blockData: CreateBlockRequest = {
47
+ blockType: options.type || 'custom',
48
+ title: name,
49
+ description: description,
50
+ allowedBrickTypes: ['text', 'image', 'video', 'code', 'quiz'], // Default allowed types
51
+ scope: ['user-store'], // Default scope for CLI-created blocks
52
+ content: []
53
+ };
54
+
55
+ console.log(chalk.yellow('📡 Creating block on server...'));
56
+
57
+ // Create the block
58
+ const block = await apiClient.createBlock(blockData);
59
+
60
+ console.log(chalk.green(`✅ Block created successfully!`));
61
+ console.log(chalk.gray(` Block ID: ${block._id}`));
62
+ console.log(chalk.gray(` Block Type: ${block.blockType}`));
63
+
64
+ if (block.gitUrl) {
65
+ console.log(chalk.gray(` GitHub URL: ${block.gitUrl}`));
66
+
67
+ // Clone the repository
68
+ const repoName = GitManager.extractRepoName(block.gitUrl);
69
+ const targetDir = path.join(process.cwd(), repoName);
70
+
71
+ console.log(chalk.yellow(`📦 Cloning repository to ./${repoName}...`));
72
+
73
+ try {
74
+ const gitManager = new GitManager();
75
+ await gitManager.cloneRepository(block.gitUrl, targetDir);
76
+
77
+ console.log(chalk.green(`🎉 Block created and repository cloned successfully!`));
78
+ console.log(chalk.blue(`\nNext steps:`));
79
+ console.log(chalk.gray(` 1. cd ${repoName}`));
80
+ console.log(chalk.gray(` 2. Make your changes`));
81
+ console.log(chalk.gray(` 3. git add . && git commit -m "Your changes"`));
82
+ console.log(chalk.gray(` 4. mexty publish`));
83
+
84
+ } catch (cloneError: any) {
85
+ console.error(chalk.red(`❌ Failed to clone repository: ${cloneError.message}`));
86
+ console.log(chalk.yellow(`You can manually clone it later:`));
87
+ console.log(chalk.gray(` git clone ${block.gitUrl}`));
88
+ }
89
+ } else {
90
+ console.log(chalk.yellow('⚠️ No GitHub repository was created (GitHub not configured)'));
91
+ }
92
+
93
+ } catch (error: any) {
94
+ console.error(chalk.red(`❌ Failed to create block: ${error.message}`));
95
+ process.exit(1);
96
+ }
97
+ }
@@ -0,0 +1,63 @@
1
+ import chalk from 'chalk';
2
+ import { apiClient } from '../utils/api';
3
+ import { createInterface } from 'readline';
4
+ import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
5
+
6
+ // Simple confirmation function
7
+ async function confirm(question: string): Promise<boolean> {
8
+ return new Promise((resolve) => {
9
+ const rl = createInterface({
10
+ input: process.stdin,
11
+ output: process.stdout
12
+ });
13
+
14
+ rl.question(`${question} (y/N): `, (answer) => {
15
+ rl.close();
16
+ resolve(answer.toLowerCase().trim() === 'y' || answer.toLowerCase().trim() === 'yes');
17
+ });
18
+ });
19
+ }
20
+
21
+ export async function deleteCommand(blockId: string): Promise<void> {
22
+ try {
23
+ // Check authentication first
24
+ requireAuthentication();
25
+
26
+ const user = getAuthenticatedUser();
27
+ console.log(chalk.blue(`🗑️ Deleting block: ${blockId}`));
28
+ console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
29
+
30
+ // Get block info first
31
+ console.log(chalk.yellow('📡 Fetching block information...'));
32
+ const block = await apiClient.getBlock(blockId);
33
+
34
+ console.log(chalk.gray(` Title: ${block.title}`));
35
+ console.log(chalk.gray(` Description: ${block.description}`));
36
+ if (block.gitUrl) {
37
+ console.log(chalk.gray(` GitHub URL: ${block.gitUrl}`));
38
+ }
39
+
40
+ // Confirm deletion
41
+ const confirmed = await confirm(chalk.red('Are you sure you want to delete this block? This action cannot be undone.'));
42
+
43
+ if (!confirmed) {
44
+ console.log(chalk.yellow('🚫 Deletion cancelled.'));
45
+ return;
46
+ }
47
+
48
+ // Delete the block
49
+ console.log(chalk.yellow('📡 Deleting block on server...'));
50
+ await apiClient.deleteBlock(blockId);
51
+
52
+ console.log(chalk.green(`✅ Block deleted successfully!`));
53
+
54
+ if (block.gitUrl) {
55
+ console.log(chalk.yellow('⚠️ Note: The GitHub repository still exists and needs to be deleted manually if desired.'));
56
+ console.log(chalk.gray(` Repository: ${block.gitUrl}`));
57
+ }
58
+
59
+ } catch (error: any) {
60
+ console.error(chalk.red(`❌ Failed to delete block: ${error.message}`));
61
+ process.exit(1);
62
+ }
63
+ }
@@ -0,0 +1,58 @@
1
+ import chalk from 'chalk';
2
+ import path from 'path';
3
+ import { apiClient } from '../utils/api';
4
+ import { GitManager } from '../utils/git';
5
+ import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
6
+
7
+ export async function forkCommand(blockId: string): Promise<void> {
8
+ try {
9
+ // Check authentication first
10
+ requireAuthentication();
11
+
12
+ const user = getAuthenticatedUser();
13
+ console.log(chalk.blue(`🍴 Forking block: ${blockId}`));
14
+ console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
15
+
16
+ // Fork the block
17
+ console.log(chalk.yellow('📡 Forking block on server...'));
18
+ const forkedBlock = await apiClient.forkBlock({ blockId });
19
+
20
+ console.log(chalk.green(`✅ Block forked successfully!`));
21
+ console.log(chalk.gray(` New Block ID: ${forkedBlock._id}`));
22
+ console.log(chalk.gray(` Title: ${forkedBlock.title}`));
23
+ console.log(chalk.gray(` Description: ${forkedBlock.description}`));
24
+
25
+ if (forkedBlock.gitUrl) {
26
+ console.log(chalk.gray(` GitHub URL: ${forkedBlock.gitUrl}`));
27
+
28
+ // Clone the forked repository
29
+ const repoName = GitManager.extractRepoName(forkedBlock.gitUrl);
30
+ const targetDir = path.join(process.cwd(), repoName);
31
+
32
+ console.log(chalk.yellow(`📦 Cloning forked repository to ./${repoName}...`));
33
+
34
+ try {
35
+ const gitManager = new GitManager();
36
+ await gitManager.cloneRepository(forkedBlock.gitUrl, targetDir);
37
+
38
+ console.log(chalk.green(`🎉 Block forked and repository cloned successfully!`));
39
+ console.log(chalk.blue(`\nNext steps:`));
40
+ console.log(chalk.gray(` 1. cd ${repoName}`));
41
+ console.log(chalk.gray(` 2. Make your changes`));
42
+ console.log(chalk.gray(` 3. git add . && git commit -m "Your changes"`));
43
+ console.log(chalk.gray(` 4. mexty publish`));
44
+
45
+ } catch (cloneError: any) {
46
+ console.error(chalk.red(`❌ Failed to clone repository: ${cloneError.message}`));
47
+ console.log(chalk.yellow(`You can manually clone it later:`));
48
+ console.log(chalk.gray(` git clone ${forkedBlock.gitUrl}`));
49
+ }
50
+ } else {
51
+ console.log(chalk.yellow('⚠️ No GitHub repository available for this block'));
52
+ }
53
+
54
+ } catch (error: any) {
55
+ console.error(chalk.red(`❌ Failed to fork block: ${error.message}`));
56
+ process.exit(1);
57
+ }
58
+ }
@@ -0,0 +1,104 @@
1
+ import chalk from 'chalk';
2
+ import { createInterface } from 'readline';
3
+ import { apiClient } from '../utils/api';
4
+
5
+ // Simple prompt function
6
+ async function prompt(question: string): Promise<string> {
7
+ return new Promise((resolve) => {
8
+ const rl = createInterface({
9
+ input: process.stdin,
10
+ output: process.stdout
11
+ });
12
+
13
+ rl.question(question, (answer) => {
14
+ rl.close();
15
+ resolve(answer.trim());
16
+ });
17
+ });
18
+ }
19
+
20
+ // Wait function for countdown
21
+ async function wait(seconds: number): Promise<void> {
22
+ return new Promise(resolve => setTimeout(resolve, seconds * 1000));
23
+ }
24
+
25
+ export async function loginCommand(): Promise<void> {
26
+ try {
27
+ console.log(chalk.blue('🔐 Login to MEXT'));
28
+
29
+ // Check if already authenticated
30
+ if (apiClient.isAuthenticated()) {
31
+ const user = apiClient.getStoredUser();
32
+ console.log(chalk.green('✅ You are already logged in!'));
33
+ console.log(chalk.gray(` Email: ${user?.email || 'Unknown'}`));
34
+ console.log(chalk.gray(` Name: ${user?.fullName || 'Not set'}`));
35
+
36
+ const logout = await prompt('Do you want to logout and login as a different user? (y/N): ');
37
+ if (logout.toLowerCase() !== 'y' && logout.toLowerCase() !== 'yes') {
38
+ return;
39
+ }
40
+
41
+ await apiClient.logout();
42
+ console.log(chalk.yellow('📤 Logged out successfully'));
43
+ }
44
+
45
+ // Request email
46
+ const email = await prompt('Enter your email address: ');
47
+
48
+ if (!email || !email.includes('@')) {
49
+ console.error(chalk.red('❌ Please provide a valid email address'));
50
+ process.exit(1);
51
+ }
52
+
53
+ console.log(chalk.yellow('📧 Requesting verification code...'));
54
+
55
+ // Request OTP
56
+ try {
57
+ const otpResponse = await apiClient.requestOTP(email);
58
+
59
+ if (!otpResponse.success) {
60
+ console.error(chalk.red(`❌ ${otpResponse.message}`));
61
+ process.exit(1);
62
+ }
63
+
64
+ console.log(chalk.green('✅ Verification code sent to your email'));
65
+ console.log(chalk.gray(' Please check your inbox (and spam folder)'));
66
+
67
+ // Wait a moment for the user to check email
68
+ await wait(2);
69
+
70
+ // Request OTP code
71
+ const otp = await prompt('Enter the 6-digit verification code: ');
72
+
73
+ if (!otp || otp.length !== 6 || !/^\d{6}$/.test(otp)) {
74
+ console.error(chalk.red('❌ Please provide a valid 6-digit code'));
75
+ process.exit(1);
76
+ }
77
+
78
+ console.log(chalk.yellow('🔓 Verifying code...'));
79
+
80
+ // Verify OTP
81
+ const verifyResponse = await apiClient.verifyOTP(email, otp);
82
+
83
+ if (!verifyResponse.success) {
84
+ console.error(chalk.red(`❌ ${verifyResponse.message}`));
85
+ process.exit(1);
86
+ }
87
+
88
+ console.log(chalk.green('🎉 Login successful!'));
89
+ console.log(chalk.gray(` Welcome, ${verifyResponse.user?.fullName || verifyResponse.user?.email || 'User'}!`));
90
+
91
+ if (!verifyResponse.user?.isProfileComplete) {
92
+ console.log(chalk.yellow('⚠️ Your profile is incomplete. Please complete it in the web interface.'));
93
+ }
94
+
95
+ } catch (error: any) {
96
+ console.error(chalk.red(`❌ Login failed: ${error.message}`));
97
+ process.exit(1);
98
+ }
99
+
100
+ } catch (error: any) {
101
+ console.error(chalk.red(`❌ Login error: ${error.message}`));
102
+ process.exit(1);
103
+ }
104
+ }