@yoamigo.com/cli 0.1.24 → 0.1.26

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 CHANGED
@@ -1,11 +1,11 @@
1
- # @yoamigo/cli
1
+ # @yoamigo.com/cli
2
2
 
3
3
  CLI for creating and managing YoAmigo templates.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install -g @yoamigo/cli
8
+ npm install -g @yoamigo.com/cli
9
9
  ```
10
10
 
11
11
  ## Quick Start
@@ -103,7 +103,7 @@ my-template/
103
103
 
104
104
  ## Documentation
105
105
 
106
- For full documentation, visit [yoamigo.com/developers](https://yoamigo.com/developers).
106
+ For full documentation, visit [https://www.yoamigo.com/dev/docs](https://www.yoamigo.com/dev/docs).
107
107
 
108
108
  ## License
109
109
 
package/dist/index.js CHANGED
@@ -11,6 +11,8 @@ import chalk2 from "chalk";
11
11
  import ora2 from "ora";
12
12
  import fs2 from "fs-extra";
13
13
  import path2 from "path";
14
+ import os2 from "os";
15
+ import { execSync } from "child_process";
14
16
 
15
17
  // src/commands/login.ts
16
18
  import { Command } from "commander";
@@ -116,19 +118,10 @@ var loginCommand = new Command("login").description("Authenticate with your API
116
118
  // src/commands/deploy.ts
117
119
  var API_BASE_URL2 = getApiBaseUrl();
118
120
  var APP_BASE_URL = getAppBaseUrl();
119
- var EXCLUDE_PATTERNS = ["node_modules", "dist", ".git", "*.log"];
120
- var ROOT_FILES = [
121
- "index.html",
122
- "package.json",
123
- "package-lock.json",
124
- "pnpm-lock.yaml",
125
- "vite.config.ts",
126
- "tsconfig.json",
127
- "postcss.config.cjs",
128
- "tailwind.config.ts"
129
- ];
130
- var deployCommand = new Command2("deploy").description("Upload template to YoAmigo").argument("<version>", "Template version (e.g., 1.0.0)").option("-v, --verbose", "Show detailed logging for debugging").action(async (version, options) => {
121
+ var EXCLUDE_PATTERNS = ["node_modules", "dist", ".git", ".vite", ".DS_Store"];
122
+ var deployCommand = new Command2("deploy").description("Upload template to YoAmigo").argument("[version]", "Template version (e.g., 1.0.0). Defaults to version from package.json").option("-v, --verbose", "Show detailed logging for debugging").action(async (versionArg, options) => {
131
123
  const verbose = options.verbose;
124
+ let tempDir = null;
132
125
  if (verbose) {
133
126
  console.log(chalk2.dim(`API URL: ${API_BASE_URL2}`));
134
127
  console.log(chalk2.dim(`App URL: ${APP_BASE_URL}`));
@@ -139,11 +132,6 @@ var deployCommand = new Command2("deploy").description("Upload template to YoAmi
139
132
  console.error(chalk2.red("Error: Not logged in. Run `yoamigo login` first."));
140
133
  process.exit(1);
141
134
  }
142
- const versionRegex = /^\d+\.\d+\.\d+$/;
143
- if (!versionRegex.test(version)) {
144
- console.error(chalk2.red("Error: Version must be in semver format (e.g., 1.0.0)"));
145
- process.exit(1);
146
- }
147
135
  const packageJsonPath = path2.join(process.cwd(), "package.json");
148
136
  if (!await fs2.pathExists(packageJsonPath)) {
149
137
  console.error(chalk2.red("Error: No package.json found. Are you in a template project?"));
@@ -151,14 +139,22 @@ var deployCommand = new Command2("deploy").description("Upload template to YoAmi
151
139
  }
152
140
  const packageJson = await fs2.readJson(packageJsonPath);
153
141
  const templateName = packageJson.name || "unnamed-template";
142
+ const version = versionArg || packageJson.version;
143
+ if (!version) {
144
+ console.error(chalk2.red("Error: No version specified and package.json has no version field"));
145
+ process.exit(1);
146
+ }
147
+ const versionRegex = /^\d+\.\d+\.\d+$/;
148
+ if (!versionRegex.test(version)) {
149
+ console.error(chalk2.red(`Error: Version "${version}" must be in semver format (e.g., 1.0.0)`));
150
+ process.exit(1);
151
+ }
152
+ if (!versionArg) {
153
+ console.log(chalk2.dim(`Using version from package.json: ${version}`));
154
+ }
154
155
  const spinner = ora2("Deploying template...").start();
155
156
  try {
156
- spinner.text = "Collecting files...";
157
- const files = await collectFiles(process.cwd());
158
- if (files.length === 0) {
159
- throw new Error("No files found to upload");
160
- }
161
- spinner.text = `Uploading ${files.length} files...`;
157
+ spinner.text = "Creating template version...";
162
158
  const templateUrl = `${API_BASE_URL2}/api/trpc/developer.createOrUpdateTemplate`;
163
159
  const templateBody = {
164
160
  name: templateName,
@@ -171,30 +167,14 @@ var deployCommand = new Command2("deploy").description("Upload template to YoAmi
171
167
  console.log(chalk2.dim(`Body: ${JSON.stringify(templateBody)}`));
172
168
  spinner.start();
173
169
  }
174
- let templateResponse;
175
- try {
176
- templateResponse = await fetch(templateUrl, {
177
- method: "POST",
178
- headers: {
179
- "Content-Type": "application/json",
180
- "X-API-Key": credentials.apiKey
181
- },
182
- body: JSON.stringify(templateBody)
183
- });
184
- } catch (fetchError) {
185
- if (verbose) {
186
- spinner.stop();
187
- console.error(chalk2.red(`
188
- Fetch error for ${templateUrl}:`));
189
- console.error(chalk2.red(fetchError instanceof Error ? fetchError.stack || fetchError.message : String(fetchError)));
190
- }
191
- throw new Error(`Network error: ${fetchError instanceof Error ? fetchError.message : "fetch failed"}`);
192
- }
193
- if (verbose) {
194
- spinner.stop();
195
- console.log(chalk2.dim(`Response status: ${templateResponse.status}`));
196
- spinner.start();
197
- }
170
+ const templateResponse = await fetch(templateUrl, {
171
+ method: "POST",
172
+ headers: {
173
+ "Content-Type": "application/json",
174
+ "X-API-Key": credentials.apiKey
175
+ },
176
+ body: JSON.stringify(templateBody)
177
+ });
198
178
  if (!templateResponse.ok) {
199
179
  const errorText = await templateResponse.text();
200
180
  if (verbose) {
@@ -214,65 +194,90 @@ Response body: ${errorText}`));
214
194
  const templateData = await templateResponse.json();
215
195
  const templateId = templateData.result?.data?.id;
216
196
  const versionId = templateData.result?.data?.versionId;
217
- if (!templateId) {
218
- throw new Error("Failed to get template ID");
197
+ if (!templateId || !versionId) {
198
+ throw new Error("Failed to get template ID or version ID");
219
199
  }
220
- if (verbose && versionId) {
200
+ if (verbose) {
221
201
  spinner.stop();
202
+ console.log(chalk2.dim(`Template ID: ${templateId}`));
222
203
  console.log(chalk2.dim(`Version ID: ${versionId}`));
223
204
  spinner.start();
224
205
  }
225
- const uploadUrl = `${API_BASE_URL2}/api/trpc/developer.uploadTemplateFile`;
226
- for (const file of files) {
227
- const relativePath = path2.relative(process.cwd(), file);
228
- const content = await fs2.readFile(file, "utf-8");
229
- if (verbose) {
230
- spinner.stop();
231
- console.log(chalk2.dim(`Uploading: ${relativePath}`));
232
- spinner.start();
233
- }
234
- try {
235
- const uploadResponse = await fetch(uploadUrl, {
236
- method: "POST",
237
- headers: {
238
- "Content-Type": "application/json",
239
- "X-API-Key": credentials.apiKey
240
- },
241
- body: JSON.stringify({
242
- templateId,
243
- versionId,
244
- // Include versionId for version-specific storage path
245
- path: relativePath,
246
- content
247
- })
248
- });
249
- if (!uploadResponse.ok) {
250
- const errorText = await uploadResponse.text();
251
- if (verbose) {
252
- spinner.stop();
253
- console.error(chalk2.yellow(`Failed to upload ${relativePath}: ${uploadResponse.status}`));
254
- console.error(chalk2.yellow(`Response: ${errorText}`));
255
- spinner.start();
256
- } else {
257
- console.warn(chalk2.yellow(`Warning: Failed to upload ${relativePath}`));
258
- }
259
- }
260
- } catch (uploadError) {
261
- if (verbose) {
262
- spinner.stop();
263
- console.error(chalk2.yellow(`Network error uploading ${relativePath}:`));
264
- console.error(chalk2.yellow(uploadError instanceof Error ? uploadError.message : String(uploadError)));
265
- spinner.start();
266
- } else {
267
- console.warn(chalk2.yellow(`Warning: Failed to upload ${relativePath}`));
268
- }
269
- }
206
+ spinner.text = "Copying template files...";
207
+ tempDir = await fs2.mkdtemp(path2.join(os2.tmpdir(), "yoamigo-deploy-"));
208
+ await copyTemplateFiles(process.cwd(), tempDir, verbose ? spinner : null);
209
+ spinner.text = "Installing dependencies (this may take a minute)...";
210
+ if (verbose) {
211
+ spinner.stop();
212
+ console.log(chalk2.dim("Running: pnpm install --ignore-scripts"));
213
+ spinner.start();
214
+ }
215
+ try {
216
+ execSync("pnpm install --ignore-scripts", {
217
+ cwd: tempDir,
218
+ stdio: "pipe",
219
+ maxBuffer: 10 * 1024 * 1024
220
+ });
221
+ } catch (installError) {
222
+ throw new Error(`Failed to install dependencies: ${installError instanceof Error ? installError.message : "unknown error"}`);
223
+ }
224
+ spinner.text = "Creating tarball...";
225
+ const tarballPath = path2.join(tempDir, "repo.tar.gz");
226
+ try {
227
+ execSync(
228
+ `tar -czf "${tarballPath}" --exclude='repo.tar.gz' -C "${tempDir}" .`,
229
+ { maxBuffer: 50 * 1024 * 1024 }
230
+ );
231
+ } catch (tarError) {
232
+ throw new Error(`Failed to create tarball: ${tarError instanceof Error ? tarError.message : "unknown error"}`);
233
+ }
234
+ const tarballStats = await fs2.stat(tarballPath);
235
+ const tarballSizeKB = Math.round(tarballStats.size / 1024);
236
+ const tarballSizeMB = (tarballStats.size / (1024 * 1024)).toFixed(1);
237
+ if (verbose) {
238
+ spinner.stop();
239
+ console.log(chalk2.dim(`Tarball size: ${tarballSizeMB} MB (${tarballSizeKB} KB)`));
240
+ spinner.start();
241
+ }
242
+ spinner.text = "Getting upload URL...";
243
+ const uploadUrlEndpoint = `${API_BASE_URL2}/api/trpc/developer.getTarballUploadUrl`;
244
+ const uploadUrlResponse = await fetch(uploadUrlEndpoint, {
245
+ method: "POST",
246
+ headers: {
247
+ "Content-Type": "application/json",
248
+ "X-API-Key": credentials.apiKey
249
+ },
250
+ body: JSON.stringify({ templateId, versionId })
251
+ });
252
+ if (!uploadUrlResponse.ok) {
253
+ const errorText = await uploadUrlResponse.text();
254
+ throw new Error(`Failed to get upload URL: ${errorText}`);
255
+ }
256
+ const uploadUrlData = await uploadUrlResponse.json();
257
+ const uploadUrl = uploadUrlData.result?.data?.uploadUrl;
258
+ if (!uploadUrl) {
259
+ throw new Error("Failed to get upload URL from API");
260
+ }
261
+ spinner.text = `Uploading tarball (${tarballSizeMB} MB)...`;
262
+ const tarballBuffer = await fs2.readFile(tarballPath);
263
+ const uploadResponse = await fetch(uploadUrl, {
264
+ method: "PUT",
265
+ headers: {
266
+ "Content-Type": "application/gzip"
267
+ },
268
+ body: tarballBuffer
269
+ });
270
+ if (!uploadResponse.ok) {
271
+ const errorText = await uploadResponse.text();
272
+ throw new Error(`Failed to upload tarball: ${uploadResponse.status} ${errorText}`);
270
273
  }
271
274
  spinner.succeed(`Template v${version} deployed successfully!`);
272
275
  console.log();
273
276
  console.log(chalk2.green("Preview URL:"));
274
277
  console.log(chalk2.cyan(` ${APP_BASE_URL}/preview/${templateId}`));
275
278
  console.log();
279
+ console.log(chalk2.dim(`Tarball size: ${tarballSizeMB} MB`));
280
+ console.log();
276
281
  console.log(chalk2.dim("To submit for review, visit your dashboard:"));
277
282
  console.log(chalk2.dim(` ${APP_BASE_URL}/dashboard`));
278
283
  } catch (error) {
@@ -283,44 +288,54 @@ Response body: ${errorText}`));
283
288
  console.error(chalk2.red(error instanceof Error ? error.message : "Unknown error"));
284
289
  }
285
290
  process.exit(1);
291
+ } finally {
292
+ if (tempDir) {
293
+ try {
294
+ await fs2.remove(tempDir);
295
+ } catch {
296
+ }
297
+ }
286
298
  }
287
299
  });
288
- async function collectFiles(dir) {
289
- const files = [];
290
- const srcDir = path2.join(dir, "src");
291
- const publicDir = path2.join(dir, "public");
292
- for (const rootFile of ROOT_FILES) {
293
- const filePath = path2.join(dir, rootFile);
294
- if (await fs2.pathExists(filePath)) {
295
- files.push(filePath);
300
+ async function copyTemplateFiles(srcDir, destDir, spinner) {
301
+ const entries = await fs2.readdir(srcDir, { withFileTypes: true });
302
+ let fileCount = 0;
303
+ for (const entry of entries) {
304
+ if (EXCLUDE_PATTERNS.some((pattern) => entry.name === pattern || entry.name.startsWith("."))) {
305
+ continue;
306
+ }
307
+ const srcPath = path2.join(srcDir, entry.name);
308
+ const destPath = path2.join(destDir, entry.name);
309
+ if (entry.isDirectory()) {
310
+ await fs2.mkdirp(destPath);
311
+ await copyTemplateFilesRecursive(srcPath, destPath);
312
+ fileCount++;
313
+ } else {
314
+ await fs2.copy(srcPath, destPath);
315
+ fileCount++;
296
316
  }
297
317
  }
298
- if (await fs2.pathExists(srcDir)) {
299
- const srcFiles = await collectFilesRecursive(srcDir);
300
- files.push(...srcFiles);
301
- }
302
- if (await fs2.pathExists(publicDir)) {
303
- const publicFiles = await collectFilesRecursive(publicDir);
304
- files.push(...publicFiles);
318
+ if (spinner) {
319
+ spinner.stop();
320
+ console.log(chalk2.dim(`Copied ${fileCount} files/directories`));
321
+ spinner.start();
305
322
  }
306
- return files;
307
323
  }
308
- async function collectFilesRecursive(dir) {
309
- const files = [];
310
- const entries = await fs2.readdir(dir, { withFileTypes: true });
324
+ async function copyTemplateFilesRecursive(srcDir, destDir) {
325
+ const entries = await fs2.readdir(srcDir, { withFileTypes: true });
311
326
  for (const entry of entries) {
312
- const fullPath = path2.join(dir, entry.name);
313
- if (EXCLUDE_PATTERNS.some((pattern) => entry.name.includes(pattern))) {
327
+ if (EXCLUDE_PATTERNS.some((pattern) => entry.name === pattern)) {
314
328
  continue;
315
329
  }
330
+ const srcPath = path2.join(srcDir, entry.name);
331
+ const destPath = path2.join(destDir, entry.name);
316
332
  if (entry.isDirectory()) {
317
- const subFiles = await collectFilesRecursive(fullPath);
318
- files.push(...subFiles);
333
+ await fs2.mkdirp(destPath);
334
+ await copyTemplateFilesRecursive(srcPath, destPath);
319
335
  } else {
320
- files.push(fullPath);
336
+ await fs2.copy(srcPath, destPath);
321
337
  }
322
338
  }
323
- return files;
324
339
  }
325
340
 
326
341
  // src/commands/dev.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoamigo.com/cli",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "CLI for creating and managing YoAmigo templates",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -15,6 +15,8 @@
15
15
  "!templates/*/node_modules",
16
16
  "!templates/*/dist",
17
17
  "!templates/*/.vite",
18
+ "!templates/*/.env",
19
+ "!templates/*/.claude",
18
20
  "LICENSE",
19
21
  "README.md"
20
22
  ],
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.0.14] - 2025-01-07
6
+
7
+ ### Improvements
8
+ - Improved page navigation for smoother multi-page browsing
9
+
10
+ ## [1.0.13] - 2025-01-05
11
+
12
+ ### Improvements
13
+ - Updated header component styling
14
+
15
+ ## [1.0.6] - 2025-12-16
16
+
17
+ ### Fixed
18
+ - Added `preact` as direct dependency for production builds (required by pnpm strict resolution)
19
+
20
+ ## [1.0.5] - 2025-12-16
21
+
22
+ ### Changed
23
+ - Added tiptap dependencies for rich text editing support
24
+ - `@tiptap/core`, `@tiptap/react`, `@tiptap/starter-kit`, `@tiptap/extension-link`, `@tiptap/extension-text-style`, `@tiptap/pm`
25
+ - `dompurify` for HTML sanitization
26
+ - Updated `@yoamigo.com/core` to `^0.1.7`
27
+
28
+ ## [1.0.4] - 2025-12-16
29
+
30
+ ### Changed
31
+ - Initial versioned release with self-contained template architecture