@mexty/cli 1.3.0 → 1.4.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.
@@ -1,49 +1,54 @@
1
- import chalk from 'chalk';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { apiClient } from '../utils/api';
5
- import { GitManager } from '../utils/git';
6
- import { createInterface } from 'readline';
7
- import { syncCommand } from './sync';
8
- import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
1
+ import chalk from "chalk";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { apiClient } from "../utils/api";
5
+ import { GitManager } from "../utils/git";
6
+ import { createInterface } from "readline";
7
+ // Removed sync command import as it's been deleted
8
+ import { requireAuthentication, getAuthenticatedUser } from "../utils/auth";
9
9
 
10
10
  // Simple confirmation function
11
11
  async function confirm(question: string): Promise<boolean> {
12
12
  return new Promise((resolve) => {
13
13
  const rl = createInterface({
14
14
  input: process.stdin,
15
- output: process.stdout
15
+ output: process.stdout,
16
16
  });
17
17
 
18
18
  rl.question(`${question} (y/N): `, (answer) => {
19
19
  rl.close();
20
- resolve(answer.toLowerCase().trim() === 'y' || answer.toLowerCase().trim() === 'yes');
20
+ resolve(
21
+ answer.toLowerCase().trim() === "y" ||
22
+ answer.toLowerCase().trim() === "yes"
23
+ );
21
24
  });
22
25
  });
23
26
  }
24
27
 
25
28
  // Extract block ID from package.json or git URL
26
29
  async function findBlockId(): Promise<string | null> {
27
- const packageJsonPath = path.join(process.cwd(), 'package.json');
28
-
30
+ const packageJsonPath = path.join(process.cwd(), "package.json");
31
+
29
32
  if (fs.existsSync(packageJsonPath)) {
30
33
  try {
31
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
32
-
34
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
35
+
33
36
  // Look for block ID in package.json name or description
34
- if (packageJson.name && packageJson.name.startsWith('block-')) {
35
- return packageJson.name.replace('block-', '');
37
+ if (packageJson.name && packageJson.name.startsWith("block-")) {
38
+ return packageJson.name.replace("block-", "");
36
39
  }
37
-
40
+
38
41
  // Look in description for block ID pattern
39
42
  if (packageJson.description) {
40
- const match = packageJson.description.match(/block[:\s]+([a-f0-9]{24})/i);
43
+ const match = packageJson.description.match(
44
+ /block[:\s]+([a-f0-9]{24})/i
45
+ );
41
46
  if (match) {
42
47
  return match[1];
43
48
  }
44
49
  }
45
50
  } catch (error) {
46
- console.warn(chalk.yellow('⚠️ Could not parse package.json'));
51
+ console.warn(chalk.yellow("⚠️ Could not parse package.json"));
47
52
  }
48
53
  }
49
54
 
@@ -51,7 +56,7 @@ async function findBlockId(): Promise<string | null> {
51
56
  try {
52
57
  const gitManager = new GitManager();
53
58
  const remoteUrl = await gitManager.getRemoteUrl();
54
-
59
+
55
60
  if (remoteUrl) {
56
61
  const match = remoteUrl.match(/block-([a-f0-9]{24})/);
57
62
  if (match) {
@@ -65,21 +70,36 @@ async function findBlockId(): Promise<string | null> {
65
70
  return null;
66
71
  }
67
72
 
68
- export async function publishCommand(): Promise<void> {
73
+ interface PublishOptions {
74
+ agent?: boolean;
75
+ }
76
+
77
+ export async function publishCommand(
78
+ options: PublishOptions = {}
79
+ ): Promise<void> {
69
80
  try {
70
81
  // Check authentication first
71
82
  requireAuthentication();
72
-
83
+
73
84
  const user = getAuthenticatedUser();
74
- console.log(chalk.blue('🚀 Publishing block...'));
75
- console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
85
+ console.log(chalk.blue("🚀 Publishing block to marketplace..."));
86
+ console.log(
87
+ chalk.gray(` User: ${user?.fullName || user?.email || "Unknown"}`)
88
+ );
89
+ console.log(
90
+ chalk.gray(` Agent insertable: ${options.agent ? "Yes" : "No"}`)
91
+ );
76
92
 
77
93
  // Check if we're in a git repository
78
94
  const gitManager = new GitManager();
79
95
  const isGitRepo = await gitManager.isGitRepository();
80
-
96
+
81
97
  if (!isGitRepo) {
82
- console.error(chalk.red('❌ Not a git repository. Please run this command from a block repository.'));
98
+ console.error(
99
+ chalk.red(
100
+ "❌ Not a git repository. Please run this command from a block repository."
101
+ )
102
+ );
83
103
  process.exit(1);
84
104
  }
85
105
 
@@ -91,8 +111,14 @@ export async function publishCommand(): Promise<void> {
91
111
  // Find block ID
92
112
  const blockId = await findBlockId();
93
113
  if (!blockId) {
94
- console.error(chalk.red('❌ Could not determine block ID from repository.'));
95
- console.error(chalk.yellow(' Make sure you are in a block repository created with mexty'));
114
+ console.error(
115
+ chalk.red("❌ Could not determine block ID from repository.")
116
+ );
117
+ console.error(
118
+ chalk.yellow(
119
+ " Make sure you are in a block repository created with mexty"
120
+ )
121
+ );
96
122
  process.exit(1);
97
123
  }
98
124
 
@@ -100,60 +126,125 @@ export async function publishCommand(): Promise<void> {
100
126
 
101
127
  // Check for uncommitted changes
102
128
  if (repoInfo.hasChanges) {
103
- console.log(chalk.yellow('⚠️ You have uncommitted changes.'));
104
- console.log(chalk.gray(' Please commit your changes before publishing:'));
105
- console.log(chalk.gray(' git add . && git commit -m "Your commit message"'));
106
-
107
- const proceed = await confirm('Do you want to continue anyway?');
129
+ console.log(chalk.yellow("⚠️ You have uncommitted changes."));
130
+ console.log(
131
+ chalk.gray(" Please commit your changes before publishing:")
132
+ );
133
+ console.log(
134
+ chalk.gray(' git add . && git commit -m "Your commit message"')
135
+ );
136
+
137
+ const proceed = await confirm("Do you want to continue anyway?");
108
138
  if (!proceed) {
109
- console.log(chalk.yellow('🚫 Publishing cancelled.'));
139
+ console.log(chalk.yellow("🚫 Publishing cancelled."));
110
140
  return;
111
141
  }
112
142
  }
113
143
 
114
144
  // Ask user to push changes
115
- console.log(chalk.blue('\n📤 Push your changes to GitHub:'));
145
+ console.log(chalk.blue("\n📤 Push your changes to GitHub:"));
116
146
  console.log(chalk.gray(` git push origin ${repoInfo.branch}`));
117
-
118
- const pushed = await confirm('Have you pushed your changes to GitHub?');
147
+
148
+ const pushed = await confirm("Have you pushed your changes to GitHub?");
119
149
  if (!pushed) {
120
- console.log(chalk.yellow('🚫 Please push your changes first and then run publish again.'));
150
+ console.log(
151
+ chalk.yellow(
152
+ "🚫 Please push your changes first and then run publish again."
153
+ )
154
+ );
121
155
  return;
122
156
  }
123
157
 
124
158
  // Trigger save and bundle
125
- console.log(chalk.yellow('📡 Triggering build and bundle process...'));
126
-
159
+ console.log(chalk.yellow("📡 Triggering build and bundle process..."));
160
+
127
161
  try {
128
162
  const result = await apiClient.saveAndBundle({ blockId });
129
-
130
- console.log(chalk.green('✅ Block published successfully!'));
163
+
164
+ console.log(chalk.green("✅ Block bundled successfully!"));
131
165
  console.log(chalk.gray(` Bundle Path: ${result.bundlePath}`));
132
166
  console.log(chalk.gray(` Federation URL: ${result.federationUrl}`));
133
-
167
+
134
168
  if (result.message) {
135
169
  console.log(chalk.blue(` ${result.message}`));
136
170
  }
137
171
 
138
- // Automatically sync registry after successful publish
139
- console.log(chalk.blue('\n🔄 Auto-syncing registry...'));
172
+ // Update sharing scope to publish to marketplace (free only)
173
+ console.log(chalk.blue("\n🌐 Publishing to marketplace..."));
174
+
140
175
  try {
141
- await syncCommand();
142
- console.log(chalk.green('✅ Registry synced! Your block is now available as a named component.'));
143
- } catch (syncError: any) {
144
- console.warn(chalk.yellow(`⚠️ Registry sync failed: ${syncError.message}`));
145
- console.log(chalk.gray(' You can manually sync later with: mexty sync'));
146
- // Don't fail the publish if sync fails - it's not critical
176
+ // Get current block to check existing scope
177
+ const block = await apiClient.getBlock(blockId);
178
+ const currentScope = block?.scope || ["user-store"];
179
+
180
+ // Add published-store to scope if not already present
181
+ const newScope = [
182
+ ...new Set([...currentScope, "user-store", "published-store"]),
183
+ ];
184
+
185
+ await apiClient.updateBlockSharing(blockId, newScope);
186
+ console.log(chalk.green("✅ Block published to marketplace (free)"));
187
+
188
+ // Update agent insertability if requested
189
+ if (options.agent) {
190
+ console.log(chalk.blue("🤖 Making block insertable by AI agents..."));
191
+ try {
192
+ await apiClient.updateBlockAgentInsertable(blockId, true);
193
+ console.log(chalk.green("✅ Block is now insertable by AI agents"));
194
+ } catch (agentError: any) {
195
+ // This might fail if user is not Mext staff, but that's okay
196
+ console.log(
197
+ chalk.yellow(
198
+ "⚠️ Could not set agent insertability (requires Mext staff permissions)"
199
+ )
200
+ );
201
+ console.log(
202
+ chalk.gray(
203
+ ` Error: ${agentError.message || "Permission denied"}`
204
+ )
205
+ );
206
+ }
207
+ }
208
+ } catch (sharingError: any) {
209
+ console.error(
210
+ chalk.red("❌ Failed to publish to marketplace:"),
211
+ sharingError.message
212
+ );
213
+ console.log(
214
+ chalk.yellow(
215
+ " Block was built successfully but not published to marketplace."
216
+ )
217
+ );
218
+ console.log(
219
+ chalk.gray(" You can try publishing again or contact support.")
220
+ );
221
+ return;
222
+ }
223
+
224
+ // Success message
225
+ console.log(
226
+ chalk.green("\n🎉 Block published to marketplace successfully!")
227
+ );
228
+ console.log(chalk.blue("Your block is now:"));
229
+ console.log(chalk.gray(" ✓ Available for free download by anyone"));
230
+ console.log(chalk.gray(" ✓ Discoverable in the marketplace"));
231
+ if (options.agent) {
232
+ console.log(
233
+ chalk.gray(" ✓ Insertable by AI agents (if you have permissions)")
234
+ );
147
235
  }
148
-
236
+ console.log(
237
+ chalk.gray(
238
+ ` ✓ Viewable at: https://mexty.ai/preview.html?blockId=${blockId}`
239
+ )
240
+ );
149
241
  } catch (buildError: any) {
150
242
  console.error(chalk.red(`❌ Build failed: ${buildError.message}`));
151
- console.log(chalk.yellow(' Check the server logs for more details.'));
243
+ console.log(chalk.yellow(" Check the server logs for more details."));
152
244
  process.exit(1);
153
245
  }
154
-
155
246
  } catch (error: any) {
156
247
  console.error(chalk.red(`❌ Failed to publish block: ${error.message}`));
157
248
  process.exit(1);
158
249
  }
159
- }
250
+ }
package/src/index.ts CHANGED
@@ -4,10 +4,8 @@ import { Command } from "commander";
4
4
  import chalk from "chalk";
5
5
  import { loginCommand } from "./commands/login";
6
6
  import { createCommand } from "./commands/create";
7
- import { forkCommand } from "./commands/fork";
8
7
  import { deleteCommand } from "./commands/delete";
9
8
  import { publishCommand } from "./commands/publish";
10
- import { syncCommand } from "./commands/sync";
11
9
  import { saveCommand } from "./commands/save";
12
10
  import { apiClient } from "./utils/api";
13
11
 
@@ -57,11 +55,6 @@ program
57
55
  )
58
56
  .action(createCommand);
59
57
 
60
- program
61
- .command("fork <blockId>")
62
- .description("Fork an existing block and clone its repository")
63
- .action(forkCommand);
64
-
65
58
  program
66
59
  .command("delete <blockId>")
67
60
  .description("Delete a block (requires ownership)")
@@ -69,14 +62,10 @@ program
69
62
 
70
63
  program
71
64
  .command("publish")
72
- .description("Publish current block with automatic bundling")
65
+ .description("Publish current block to marketplace (free only)")
66
+ .option("--agent", "Make block insertable by AI agents", false)
73
67
  .action(publishCommand);
74
68
 
75
- program
76
- .command("sync")
77
- .description("Sync block registry and update typed exports")
78
- .action(syncCommand);
79
-
80
69
  program
81
70
  .command("save")
82
71
  .description("Save current block (git add, commit, push, and trigger build)")
package/src/utils/api.ts CHANGED
@@ -6,7 +6,16 @@ import os from "os";
6
6
 
7
7
  export interface Block {
8
8
  _id: string;
9
+ id?: string;
9
10
  blockType: string;
11
+ _doc?: {
12
+ _id: string;
13
+ blockType: string;
14
+ title: string;
15
+ description: string;
16
+ gitUrl?: string;
17
+ [key: string]: any;
18
+ };
10
19
  title: string;
11
20
  description: string;
12
21
  gitUrl?: string;
@@ -225,6 +234,24 @@ class ApiClient {
225
234
  return response.data;
226
235
  }
227
236
 
237
+ async updateBlockSharing(blockId: string, scope: string[]): Promise<any> {
238
+ const response = await this.client.patch(`/api/blocks/${blockId}/sharing`, {
239
+ scope,
240
+ });
241
+ return response.data;
242
+ }
243
+
244
+ async updateBlockAgentInsertable(
245
+ blockId: string,
246
+ isInsertableByAgent: boolean
247
+ ): Promise<any> {
248
+ const response = await this.client.patch(
249
+ `/api/blocks/${blockId}/agent-insertable`,
250
+ { isInsertableByAgent }
251
+ );
252
+ return response.data;
253
+ }
254
+
228
255
  async healthCheck(): Promise<boolean> {
229
256
  try {
230
257
  await this.client.get("/api/health");
@@ -1,58 +0,0 @@
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
- }