@onexapis/cli 1.0.0 → 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.
package/dist/index.mjs CHANGED
@@ -6,8 +6,10 @@ import chalk4 from 'chalk';
6
6
  import ora from 'ora';
7
7
  import fs from 'fs-extra';
8
8
  import ejs from 'ejs';
9
- import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
10
- import { glob } from 'glob';
9
+ import { PutObjectCommand, GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
10
+ import os from 'os';
11
+ import archiver from 'archiver';
12
+ import AdmZip from 'adm-zip';
11
13
 
12
14
  var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
13
15
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -87,8 +89,8 @@ function validateThemeName(name) {
87
89
  return /^[a-z][a-z0-9-]*$/.test(name);
88
90
  }
89
91
  function pathExists(filePath) {
90
- const fs7 = __require("fs-extra");
91
- return fs7.existsSync(filePath);
92
+ const fs8 = __require("fs-extra");
93
+ return fs8.existsSync(filePath);
92
94
  }
93
95
  function validateCategory(category) {
94
96
  const validCategories = [
@@ -205,18 +207,19 @@ function getProjectRoot() {
205
207
  return process.cwd();
206
208
  }
207
209
  function getThemesDir() {
208
- try {
209
- return path.join(getProjectRoot(), "src/themes");
210
- } catch (e) {
211
- return path.join(getProjectRoot(), "themes");
210
+ const root = getProjectRoot();
211
+ const themesDir = path.join(root, "themes");
212
+ if (fs.existsSync(themesDir)) {
213
+ return themesDir;
212
214
  }
215
+ return path.join(root, "src/themes");
213
216
  }
214
217
  function getFeaturesDir() {
215
218
  return path.join(getProjectRoot(), "src/features");
216
219
  }
217
220
  function isOneXProject() {
218
221
  const root = getProjectRoot();
219
- return fs.existsSync(path.join(root, "src/themes")) && fs.existsSync(path.join(root, "src/lib/registry"));
222
+ return fs.existsSync(path.join(root, "themes")) || fs.existsSync(path.join(root, "src/themes"));
220
223
  }
221
224
  function ensureOneXProject() {
222
225
  if (!isOneXProject()) {
@@ -233,12 +236,12 @@ function listThemes() {
233
236
  }
234
237
  return fs.readdirSync(themesDir).filter((name) => {
235
238
  const themePath = path.join(themesDir, name);
236
- return fs.statSync(themePath).isDirectory() && fs.existsSync(path.join(themePath, "manifest.ts"));
239
+ return fs.statSync(themePath).isDirectory() && (fs.existsSync(path.join(themePath, "theme.config.ts")) || fs.existsSync(path.join(themePath, "bundle-entry.ts")) || fs.existsSync(path.join(themePath, "manifest.ts")));
237
240
  });
238
241
  }
239
242
  function themeExists(themeName) {
240
243
  const themePath = path.join(getThemesDir(), themeName);
241
- return fs.existsSync(themePath) && fs.existsSync(path.join(themePath, "manifest.ts"));
244
+ return fs.existsSync(themePath) && (fs.existsSync(path.join(themePath, "theme.config.ts")) || fs.existsSync(path.join(themePath, "bundle-entry.ts")) || fs.existsSync(path.join(themePath, "manifest.ts")));
242
245
  }
243
246
  function detectPackageManager() {
244
247
  const userAgent = process.env.npm_config_user_agent || "";
@@ -432,7 +435,9 @@ Add your theme-specific blocks here.
432
435
  logger.newLine();
433
436
  logger.section("Theme structure:");
434
437
  logger.log(" src/manifest.ts - Theme manifest and exports");
435
- logger.log(" src/config.ts - Design tokens (colors, typography, etc.)");
438
+ logger.log(
439
+ " src/config.ts - Design tokens (colors, typography, etc.)"
440
+ );
436
441
  logger.log(" src/layout.ts - Header and footer configuration");
437
442
  logger.log(" src/sections/ - Custom sections for your theme");
438
443
  logger.log(" src/blocks/ - Reusable blocks");
@@ -1435,8 +1440,16 @@ async function listThemesInfo() {
1435
1440
  }
1436
1441
  logger.log("");
1437
1442
  for (const theme of themes) {
1438
- const manifestPath = path.join(getThemesDir(), theme, "manifest.ts");
1439
- const manifestContent = fs.readFileSync(manifestPath, "utf-8");
1443
+ const themeDir = path.join(getThemesDir(), theme);
1444
+ const candidates = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"];
1445
+ let manifestContent = "";
1446
+ for (const candidate of candidates) {
1447
+ const candidatePath = path.join(themeDir, candidate);
1448
+ if (fs.existsSync(candidatePath)) {
1449
+ manifestContent = fs.readFileSync(candidatePath, "utf-8");
1450
+ break;
1451
+ }
1452
+ }
1440
1453
  const nameMatch = manifestContent.match(/name:\s*["'](.+)["']/);
1441
1454
  const versionMatch = manifestContent.match(/version:\s*["'](.+)["']/);
1442
1455
  const descMatch = manifestContent.match(/description:\s*["'](.+)["']/);
@@ -1569,13 +1582,11 @@ function getS3Client() {
1569
1582
  return new S3Client({
1570
1583
  endpoint: endpointUrl,
1571
1584
  region: "us-east-1",
1572
- // MinIO doesn't use real regions
1573
1585
  credentials: {
1574
1586
  accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
1575
1587
  secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
1576
1588
  },
1577
1589
  forcePathStyle: true
1578
- // Required for MinIO
1579
1590
  });
1580
1591
  }
1581
1592
  if (adapterMode === "local") {
@@ -1645,36 +1656,40 @@ async function readManifest() {
1645
1656
  "No manifest.ts or package.json found. Are you in a theme directory?"
1646
1657
  );
1647
1658
  }
1648
- function getContentType(filename) {
1649
- const ext = path.extname(filename).toLowerCase();
1650
- const types = {
1651
- ".js": "application/javascript",
1652
- ".mjs": "application/javascript",
1653
- ".json": "application/json",
1654
- ".css": "text/css",
1655
- ".png": "image/png",
1656
- ".jpg": "image/jpeg",
1657
- ".jpeg": "image/jpeg",
1658
- ".svg": "image/svg+xml",
1659
- ".woff2": "font/woff2",
1660
- ".woff": "font/woff",
1661
- ".ttf": "font/ttf",
1662
- ".ts": "text/plain"
1663
- };
1664
- return types[ext] || "application/octet-stream";
1659
+ async function createZipFromDir(sourceDir, outputPath, excludePatterns = []) {
1660
+ return new Promise((resolve, reject) => {
1661
+ const output = fs.createWriteStream(outputPath);
1662
+ const archive = archiver("zip", { zlib: { level: 6 } });
1663
+ output.on("close", () => resolve());
1664
+ archive.on("error", (err) => reject(err));
1665
+ archive.pipe(output);
1666
+ archive.glob("**/*", {
1667
+ cwd: sourceDir,
1668
+ dot: true,
1669
+ ignore: excludePatterns
1670
+ });
1671
+ archive.finalize();
1672
+ });
1665
1673
  }
1666
- async function uploadFile(s3Client, bucket, themeId, version, baseDir, file) {
1667
- const filePath = path.join(baseDir, file);
1668
- const content = await fs.readFile(filePath);
1669
- const s3Key = `themes/${themeId}/${version}/${file}`;
1670
- await s3Client.send(
1671
- new PutObjectCommand({
1672
- Bucket: bucket,
1673
- Key: s3Key,
1674
- Body: content,
1675
- ContentType: getContentType(file)
1676
- })
1677
- );
1674
+ async function findSourceDir(themeId, explicitDir) {
1675
+ if (explicitDir) {
1676
+ if (await fs.pathExists(explicitDir)) return explicitDir;
1677
+ return null;
1678
+ }
1679
+ const searchPaths = [
1680
+ process.cwd(),
1681
+ path.resolve(process.cwd(), `../../themes/${themeId}`),
1682
+ path.resolve(process.cwd(), `../themes/${themeId}`)
1683
+ ];
1684
+ const markers = ["theme.config.ts", "bundle-entry.ts"];
1685
+ for (const dir of searchPaths) {
1686
+ for (const marker of markers) {
1687
+ if (await fs.pathExists(path.join(dir, marker))) {
1688
+ return dir;
1689
+ }
1690
+ }
1691
+ }
1692
+ return null;
1678
1693
  }
1679
1694
  async function updateLatestPointer(s3Client, bucket, themeId, version) {
1680
1695
  const latestData = {
@@ -1692,12 +1707,18 @@ async function updateLatestPointer(s3Client, bucket, themeId, version) {
1692
1707
  }
1693
1708
  async function uploadCommand(options) {
1694
1709
  logger.header("Upload Theme to S3");
1695
- ensureOneXProject();
1696
1710
  const spinner = ora("Preparing theme upload...").start();
1697
1711
  try {
1698
- const manifest = await readManifest();
1699
- const themeId = manifest.themeId;
1700
- const version = options.version || manifest.version || "1.0.0";
1712
+ let themeId;
1713
+ let version;
1714
+ if (options.theme) {
1715
+ themeId = options.theme;
1716
+ version = options.version || "1.0.0";
1717
+ } else {
1718
+ const manifest = await readManifest();
1719
+ themeId = manifest.themeId;
1720
+ version = options.version || manifest.version || "1.0.0";
1721
+ }
1701
1722
  spinner.text = `Found theme: ${themeId}@${version}`;
1702
1723
  const bucket = options.bucket || getBucketName(options.environment);
1703
1724
  const s3Client = getS3Client();
@@ -1718,67 +1739,113 @@ async function uploadCommand(options) {
1718
1739
  process.exit(1);
1719
1740
  }
1720
1741
  spinner.succeed(`Found compiled theme at: ${compiledDir}`);
1721
- spinner.start(`Scanning files...`);
1722
- const files = await glob("**/*", {
1723
- cwd: compiledDir,
1724
- nodir: true,
1725
- dot: true
1726
- });
1727
- if (files.length === 0) {
1728
- spinner.fail(chalk4.red("No files found in compiled theme directory"));
1729
- process.exit(1);
1730
- }
1731
- spinner.succeed(`Found ${files.length} files to upload`);
1742
+ spinner.start("Creating bundle.zip...");
1743
+ const tmpDir = os.tmpdir();
1744
+ const bundleZipPath = path.join(tmpDir, `${themeId}-${version}-bundle.zip`);
1745
+ await createZipFromDir(compiledDir, bundleZipPath);
1746
+ const bundleZipBuffer = await fs.readFile(bundleZipPath);
1747
+ const bundleSizeMB = (bundleZipBuffer.length / 1024 / 1024).toFixed(2);
1748
+ spinner.succeed(`Created bundle.zip (${bundleSizeMB} MB)`);
1732
1749
  if (options.dryRun) {
1733
- spinner.info(chalk4.yellow("Dry run mode - no files will be uploaded"));
1734
- console.log(chalk4.gray("\nFiles to upload:"));
1735
- const displayFiles = files.slice(0, 10);
1736
- displayFiles.forEach((file) => console.log(chalk4.gray(` - ${file}`)));
1737
- if (files.length > 10) {
1738
- console.log(chalk4.gray(` ... and ${files.length - 10} more files
1739
- `));
1740
- }
1741
- console.log(chalk4.cyan(`
1742
- S3 bucket: ${bucket}`));
1750
+ spinner.info(chalk4.yellow("Dry run mode \u2014 no files will be uploaded"));
1751
+ console.log();
1752
+ console.log(chalk4.gray(` bundle.zip: ${bundleSizeMB} MB`));
1743
1753
  console.log(
1744
- chalk4.cyan(`S3 path: s3://${bucket}/themes/${themeId}/${version}/
1745
- `)
1754
+ chalk4.cyan(
1755
+ ` S3 path: s3://${bucket}/themes/${themeId}/${version}/bundle.zip`
1756
+ )
1746
1757
  );
1758
+ if (!options.skipSource) {
1759
+ const sourceDir = await findSourceDir(themeId, options.sourceDir);
1760
+ if (sourceDir) {
1761
+ console.log(chalk4.gray(` source dir: ${sourceDir}`));
1762
+ console.log(
1763
+ chalk4.cyan(
1764
+ ` S3 path: s3://${bucket}/themes/${themeId}/${version}/source.zip`
1765
+ )
1766
+ );
1767
+ } else {
1768
+ console.log(
1769
+ chalk4.yellow(" source dir: not found (source.zip will be skipped)")
1770
+ );
1771
+ }
1772
+ }
1773
+ console.log();
1774
+ await fs.remove(bundleZipPath);
1747
1775
  return;
1748
1776
  }
1749
- spinner.start(`Uploading to S3: ${bucket}...`);
1750
- let uploaded = 0;
1751
- const total = files.length;
1752
- const batchSize = 10;
1753
- for (let i = 0; i < files.length; i += batchSize) {
1754
- const batch = files.slice(i, i + batchSize);
1755
- await Promise.all(
1756
- batch.map(async (file) => {
1757
- await uploadFile(
1758
- s3Client,
1759
- bucket,
1760
- themeId,
1761
- version,
1762
- compiledDir,
1763
- file
1764
- );
1765
- uploaded++;
1766
- spinner.text = `Uploading... ${uploaded}/${total} files (${Math.round(uploaded / total * 100)}%)`;
1767
- })
1768
- );
1777
+ spinner.start("Uploading bundle.zip to S3...");
1778
+ const bundleS3Key = `themes/${themeId}/${version}/bundle.zip`;
1779
+ await s3Client.send(
1780
+ new PutObjectCommand({
1781
+ Bucket: bucket,
1782
+ Key: bundleS3Key,
1783
+ Body: bundleZipBuffer,
1784
+ ContentType: "application/zip"
1785
+ })
1786
+ );
1787
+ spinner.succeed(
1788
+ `Uploaded bundle.zip ${chalk4.gray(`\u2192 s3://${bucket}/${bundleS3Key}`)}`
1789
+ );
1790
+ await fs.remove(bundleZipPath);
1791
+ let sourceUploaded = false;
1792
+ if (!options.skipSource) {
1793
+ spinner.start("Looking for source directory...");
1794
+ const sourceDir = await findSourceDir(themeId, options.sourceDir);
1795
+ if (sourceDir) {
1796
+ spinner.succeed(`Found source at: ${sourceDir}`);
1797
+ spinner.start("Creating source.zip...");
1798
+ const sourceZipPath = path.join(
1799
+ tmpDir,
1800
+ `${themeId}-${version}-source.zip`
1801
+ );
1802
+ await createZipFromDir(sourceDir, sourceZipPath, [
1803
+ "node_modules/**",
1804
+ "dist/**",
1805
+ ".git/**",
1806
+ "*.zip",
1807
+ ".next/**",
1808
+ ".turbo/**"
1809
+ ]);
1810
+ const sourceZipBuffer = await fs.readFile(sourceZipPath);
1811
+ const sourceSizeMB = (sourceZipBuffer.length / 1024 / 1024).toFixed(2);
1812
+ spinner.succeed(`Created source.zip (${sourceSizeMB} MB)`);
1813
+ spinner.start("Uploading source.zip to S3...");
1814
+ const sourceS3Key = `themes/${themeId}/${version}/source.zip`;
1815
+ await s3Client.send(
1816
+ new PutObjectCommand({
1817
+ Bucket: bucket,
1818
+ Key: sourceS3Key,
1819
+ Body: sourceZipBuffer,
1820
+ ContentType: "application/zip"
1821
+ })
1822
+ );
1823
+ spinner.succeed(
1824
+ `Uploaded source.zip ${chalk4.gray(`\u2192 s3://${bucket}/${sourceS3Key}`)}`
1825
+ );
1826
+ await fs.remove(sourceZipPath);
1827
+ sourceUploaded = true;
1828
+ } else {
1829
+ spinner.warn(
1830
+ chalk4.yellow("Source directory not found \u2014 skipping source.zip")
1831
+ );
1832
+ }
1769
1833
  }
1770
- spinner.succeed(`Uploaded ${total} files to S3`);
1771
1834
  spinner.start("Updating latest.json pointer...");
1772
1835
  await updateLatestPointer(s3Client, bucket, themeId, version);
1773
1836
  spinner.succeed("Updated latest.json pointer");
1774
1837
  console.log();
1775
- logger.success(chalk4.green.bold("\u2705 Theme uploaded successfully!"));
1838
+ logger.success(chalk4.green.bold("Theme uploaded successfully!"));
1776
1839
  console.log();
1777
1840
  console.log(
1778
1841
  chalk4.cyan(" Theme: ") + chalk4.white(`${themeId}@${version}`)
1779
1842
  );
1780
1843
  console.log(chalk4.cyan(" Bucket: ") + chalk4.white(bucket));
1781
- console.log(chalk4.cyan(" Files: ") + chalk4.white(total));
1844
+ console.log(
1845
+ chalk4.cyan(" Files: ") + chalk4.white(
1846
+ `bundle.zip${sourceUploaded ? " + source.zip" : ""}`
1847
+ )
1848
+ );
1782
1849
  console.log(
1783
1850
  chalk4.cyan(" Path: ") + chalk4.gray(`s3://${bucket}/themes/${themeId}/${version}/`)
1784
1851
  );
@@ -1882,79 +1949,45 @@ async function resolveLatestVersion(s3Client, bucket, themeId) {
1882
1949
  );
1883
1950
  }
1884
1951
  }
1885
- async function downloadManifest(s3Client, bucket, themeId, version) {
1886
- try {
1887
- const response = await s3Client.send(
1888
- new GetObjectCommand({
1889
- Bucket: bucket,
1890
- Key: `themes/${themeId}/${version}/manifest.json`
1891
- })
1892
- );
1893
- const body = await streamToString(response.Body);
1894
- return JSON.parse(body);
1895
- } catch (error) {
1896
- throw new Error(
1897
- `Failed to download manifest for ${themeId}@${version}: ${error.message}
1898
- The theme may not exist in S3 bucket "${bucket}".`
1899
- );
1900
- }
1901
- }
1902
- async function downloadFile(s3Client, bucket, themeId, version, file, outputDir) {
1903
- const s3Key = `themes/${themeId}/${version}/${file}`;
1904
- const localPath = path.join(outputDir, file);
1905
- await fs.ensureDir(path.dirname(localPath));
1906
- try {
1907
- const response = await s3Client.send(
1908
- new GetObjectCommand({
1909
- Bucket: bucket,
1910
- Key: s3Key
1911
- })
1912
- );
1913
- const content = await streamToBuffer(response.Body);
1914
- await fs.writeFile(localPath, content);
1915
- } catch (error) {
1916
- throw new Error(`Failed to download ${file}: ${error.message}`);
1917
- }
1918
- }
1919
- function collectFilesToDownload(manifest) {
1920
- const files = ["manifest.json"];
1921
- if (manifest.output) {
1922
- if (manifest.output.entry) {
1923
- files.push(manifest.output.entry);
1924
- }
1925
- if (manifest.output.chunks) {
1926
- files.push(...manifest.output.chunks);
1927
- }
1928
- if (manifest.output.assets) {
1929
- files.push(...manifest.output.assets);
1952
+ async function createCompatibilityFiles(outputDir, manifest) {
1953
+ var _a;
1954
+ const entryFile = ((_a = manifest.output) == null ? void 0 : _a.entry) || "bundle-entry.js";
1955
+ if (entryFile !== "bundle-entry.js" && entryFile.startsWith("bundle-entry-")) {
1956
+ const hashedPath = path.join(outputDir, entryFile);
1957
+ const stablePath = path.join(outputDir, "bundle-entry.js");
1958
+ if (await fs.pathExists(hashedPath)) {
1959
+ await fs.copy(hashedPath, stablePath);
1960
+ const mapPath = hashedPath + ".map";
1961
+ if (await fs.pathExists(mapPath)) {
1962
+ await fs.copy(mapPath, stablePath + ".map");
1963
+ }
1930
1964
  }
1931
1965
  }
1932
- return files;
1933
- }
1934
- async function createCompatibilityFiles(outputDir) {
1935
1966
  const sectionsRegistryPath = path.join(outputDir, "sections-registry.js");
1936
1967
  const content = `// Re-export all sections from bundle-entry
1937
1968
  // This file exists to maintain compatibility with the import path
1938
1969
  export * from './bundle-entry.js';
1939
1970
  `;
1940
1971
  await fs.writeFile(sectionsRegistryPath, content, "utf-8");
1972
+ const pkgJsonPath = path.join(outputDir, "package.json");
1973
+ await fs.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
1941
1974
  }
1942
1975
  function showDownloadFailureHelp(themeId, bucket) {
1943
1976
  console.log();
1944
- logger.error(chalk4.red.bold("\u274C Theme download failed"));
1977
+ logger.error(chalk4.red.bold("Theme download failed"));
1945
1978
  console.log();
1946
1979
  console.log(chalk4.yellow("Possible reasons:"));
1947
1980
  console.log(chalk4.gray(" 1. Theme not uploaded to S3 yet"));
1948
1981
  console.log(chalk4.gray(" 2. AWS credentials not configured correctly"));
1949
1982
  console.log(chalk4.gray(" 3. Bucket name or region is incorrect"));
1950
- console.log(chalk4.gray(" 4. Theme files are incomplete or corrupted"));
1983
+ console.log(chalk4.gray(" 4. bundle.zip is missing or corrupted"));
1951
1984
  console.log();
1952
1985
  console.log(chalk4.cyan.bold("To fix this:"));
1953
1986
  console.log();
1954
1987
  console.log(chalk4.white("1. Compile and upload the theme:"));
1955
1988
  console.log(chalk4.gray(` cd themes/${themeId}`));
1956
1989
  console.log(chalk4.gray(" pnpm build"));
1957
- console.log(chalk4.gray(" pnpm upload"));
1990
+ console.log(chalk4.gray(" onex upload"));
1958
1991
  console.log();
1959
1992
  console.log(chalk4.white("2. Verify AWS credentials are set:"));
1960
1993
  console.log(
@@ -1970,7 +2003,11 @@ function showDownloadFailureHelp(themeId, bucket) {
1970
2003
  );
1971
2004
  console.log();
1972
2005
  console.log(chalk4.white("4. Verify theme exists in S3:"));
1973
- console.log(chalk4.gray(` aws s3 ls s3://${bucket}/themes/${themeId}/`));
2006
+ console.log(
2007
+ chalk4.gray(
2008
+ ` aws s3 ls s3://${bucket}/themes/${themeId}/`
2009
+ )
2010
+ );
1974
2011
  console.log();
1975
2012
  }
1976
2013
  async function downloadCommand(options) {
@@ -1998,55 +2035,44 @@ async function downloadCommand(options) {
1998
2035
  spinner.succeed(
1999
2036
  `Resolved latest version: ${chalk4.cyan(resolvedVersion)}`
2000
2037
  );
2001
- spinner.start("Downloading manifest...");
2002
2038
  }
2003
- const manifest = await downloadManifest(
2004
- s3Client,
2005
- bucket,
2006
- themeId,
2007
- resolvedVersion
2039
+ spinner.start(
2040
+ `Downloading bundle.zip for ${themeId}@${resolvedVersion}...`
2041
+ );
2042
+ const s3Key = `themes/${themeId}/${resolvedVersion}/bundle.zip`;
2043
+ const response = await s3Client.send(
2044
+ new GetObjectCommand({
2045
+ Bucket: bucket,
2046
+ Key: s3Key
2047
+ })
2008
2048
  );
2009
- spinner.succeed("Downloaded manifest");
2010
- spinner.start("Preparing output directory...");
2049
+ const zipBuffer = await streamToBuffer(response.Body);
2050
+ const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
2051
+ spinner.succeed(`Downloaded bundle.zip (${sizeMB} MB)`);
2052
+ spinner.start("Extracting bundle...");
2011
2053
  await fs.remove(outputDir);
2012
2054
  await fs.ensureDir(outputDir);
2013
- spinner.succeed("Output directory ready");
2014
- spinner.start("Downloading theme files...");
2015
- const files = collectFilesToDownload(manifest);
2016
- let downloaded = 0;
2017
- const total = files.length;
2018
- const batchSize = 10;
2019
- for (let i = 0; i < files.length; i += batchSize) {
2020
- const batch = files.slice(i, i + batchSize);
2021
- await Promise.all(
2022
- batch.map(async (file) => {
2023
- await downloadFile(
2024
- s3Client,
2025
- bucket,
2026
- themeId,
2027
- resolvedVersion,
2028
- file,
2029
- outputDir
2030
- );
2031
- downloaded++;
2032
- spinner.text = `Downloading... ${downloaded}/${total} files (${Math.round(downloaded / total * 100)}%)`;
2033
- })
2034
- );
2035
- }
2036
- spinner.succeed(`Downloaded ${total} files`);
2037
- await createCompatibilityFiles(outputDir);
2055
+ const zip = new AdmZip(zipBuffer);
2056
+ zip.extractAllTo(outputDir, true);
2057
+ const entries = zip.getEntries().filter((e) => !e.isDirectory);
2058
+ spinner.succeed(`Extracted ${entries.length} files to ${outputDir}`);
2059
+ const manifestPath = path.join(outputDir, "manifest.json");
2060
+ const manifest = await fs.readJson(manifestPath);
2061
+ await createCompatibilityFiles(outputDir, manifest);
2038
2062
  console.log();
2039
- logger.success(chalk4.green.bold("\u2705 Theme downloaded successfully!"));
2063
+ logger.success(chalk4.green.bold("Theme downloaded successfully!"));
2040
2064
  console.log();
2041
2065
  console.log(
2042
2066
  chalk4.cyan(" Theme: ") + chalk4.white(`${themeId}@${resolvedVersion}`)
2043
2067
  );
2044
2068
  console.log(chalk4.cyan(" Bucket: ") + chalk4.white(bucket));
2045
2069
  console.log(chalk4.cyan(" Output: ") + chalk4.white(outputDir));
2046
- console.log(chalk4.cyan(" Files: ") + chalk4.white(total));
2047
- console.log(
2048
- chalk4.cyan(" Sections:") + chalk4.white(` ${manifest.counts.sections}`)
2049
- );
2070
+ console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
2071
+ if (manifest.counts) {
2072
+ console.log(
2073
+ chalk4.cyan(" Sections:") + chalk4.white(` ${manifest.counts.sections}`)
2074
+ );
2075
+ }
2050
2076
  console.log();
2051
2077
  } catch (error) {
2052
2078
  spinner.fail(chalk4.red("Download failed"));
@@ -2057,7 +2083,211 @@ async function downloadCommand(options) {
2057
2083
  process.exit(1);
2058
2084
  }
2059
2085
  }
2086
+ function getS3Client3() {
2087
+ const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
2088
+ if (adapterMode === "vps") {
2089
+ const endpoint = process.env.MINIO_ENDPOINT || "localhost:9000";
2090
+ const secure = process.env.MINIO_SECURE === "true";
2091
+ const endpointUrl = endpoint.startsWith("http") ? endpoint : `${secure ? "https" : "http"}://${endpoint}`;
2092
+ return new S3Client({
2093
+ endpoint: endpointUrl,
2094
+ region: "us-east-1",
2095
+ credentials: {
2096
+ accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
2097
+ secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
2098
+ },
2099
+ forcePathStyle: true
2100
+ });
2101
+ }
2102
+ if (adapterMode === "local") {
2103
+ return new S3Client({
2104
+ endpoint: "http://localhost:4569",
2105
+ region: "ap-southeast-1",
2106
+ credentials: {
2107
+ accessKeyId: "S3RVER",
2108
+ secretAccessKey: "S3RVER"
2109
+ },
2110
+ forcePathStyle: true
2111
+ });
2112
+ }
2113
+ return new S3Client({
2114
+ region: process.env.AWS_REGION || "ap-southeast-1"
2115
+ });
2116
+ }
2117
+ function getBucketName3(env) {
2118
+ if (process.env.BUCKET_NAME) {
2119
+ return process.env.BUCKET_NAME;
2120
+ }
2121
+ const environment = env || process.env.ENVIRONMENT || "staging";
2122
+ return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
2123
+ }
2124
+ async function streamToString2(stream) {
2125
+ const chunks = [];
2126
+ try {
2127
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
2128
+ const chunk = temp.value;
2129
+ chunks.push(Buffer.from(chunk));
2130
+ }
2131
+ } catch (temp) {
2132
+ error = [temp];
2133
+ } finally {
2134
+ try {
2135
+ more && (temp = iter.return) && await temp.call(iter);
2136
+ } finally {
2137
+ if (error)
2138
+ throw error[0];
2139
+ }
2140
+ }
2141
+ return Buffer.concat(chunks).toString("utf-8");
2142
+ }
2143
+ async function streamToBuffer2(stream) {
2144
+ const chunks = [];
2145
+ try {
2146
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
2147
+ const chunk = temp.value;
2148
+ chunks.push(Buffer.from(chunk));
2149
+ }
2150
+ } catch (temp) {
2151
+ error = [temp];
2152
+ } finally {
2153
+ try {
2154
+ more && (temp = iter.return) && await temp.call(iter);
2155
+ } finally {
2156
+ if (error)
2157
+ throw error[0];
2158
+ }
2159
+ }
2160
+ return Buffer.concat(chunks);
2161
+ }
2162
+ async function resolveLatestVersion2(s3Client, bucket, themeId) {
2163
+ try {
2164
+ const response = await s3Client.send(
2165
+ new GetObjectCommand({
2166
+ Bucket: bucket,
2167
+ Key: `themes/${themeId}/latest.json`
2168
+ })
2169
+ );
2170
+ const body = await streamToString2(response.Body);
2171
+ return JSON.parse(body).version;
2172
+ } catch (error) {
2173
+ throw new Error(
2174
+ `Failed to resolve latest version for theme "${themeId}": ${error.message}`
2175
+ );
2176
+ }
2177
+ }
2178
+ function runInstall(cwd) {
2179
+ return new Promise((resolve) => {
2180
+ const proc = spawn("pnpm", ["install"], {
2181
+ cwd,
2182
+ stdio: "inherit",
2183
+ shell: true
2184
+ });
2185
+ proc.on("close", (code) => resolve(code === 0));
2186
+ proc.on("error", () => resolve(false));
2187
+ });
2188
+ }
2189
+ async function cloneCommand(themeName, options) {
2190
+ logger.header("Clone Theme Source");
2191
+ const spinner = ora("Initializing clone...").start();
2192
+ try {
2193
+ const bucket = options.bucket || getBucketName3(options.environment);
2194
+ const outputDir = options.output || path.resolve(process.cwd(), themeName);
2195
+ const s3Client = getS3Client3();
2196
+ if (await fs.pathExists(outputDir)) {
2197
+ spinner.fail(
2198
+ chalk4.red(`Directory already exists: ${outputDir}`)
2199
+ );
2200
+ logger.info(
2201
+ chalk4.gray(
2202
+ "Use -o to specify a different output directory, or remove the existing directory."
2203
+ )
2204
+ );
2205
+ process.exit(1);
2206
+ }
2207
+ let version = options.version || "latest";
2208
+ if (version === "latest") {
2209
+ spinner.text = "Resolving latest version...";
2210
+ version = await resolveLatestVersion2(s3Client, bucket, themeName);
2211
+ spinner.succeed(`Resolved latest version: ${chalk4.cyan(version)}`);
2212
+ }
2213
+ spinner.start(
2214
+ `Downloading source.zip for ${themeName}@${version}...`
2215
+ );
2216
+ const s3Key = `themes/${themeName}/${version}/source.zip`;
2217
+ let zipBuffer;
2218
+ try {
2219
+ const response = await s3Client.send(
2220
+ new GetObjectCommand({
2221
+ Bucket: bucket,
2222
+ Key: s3Key
2223
+ })
2224
+ );
2225
+ zipBuffer = await streamToBuffer2(response.Body);
2226
+ } catch (error) {
2227
+ spinner.fail(chalk4.red(`Source not found: s3://${bucket}/${s3Key}`));
2228
+ console.log();
2229
+ console.log(
2230
+ chalk4.yellow("The theme source may not have been uploaded yet.")
2231
+ );
2232
+ console.log(
2233
+ chalk4.gray(
2234
+ `Upload source with: onex upload --theme ${themeName}`
2235
+ )
2236
+ );
2237
+ console.log();
2238
+ process.exit(1);
2239
+ }
2240
+ const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
2241
+ spinner.succeed(`Downloaded source.zip (${sizeMB} MB)`);
2242
+ spinner.start(`Extracting to ${outputDir}...`);
2243
+ await fs.ensureDir(outputDir);
2244
+ const zip = new AdmZip(zipBuffer);
2245
+ zip.extractAllTo(outputDir, true);
2246
+ const entries = zip.getEntries().filter((e) => !e.isDirectory);
2247
+ spinner.succeed(`Extracted ${entries.length} files`);
2248
+ if (options.install !== false) {
2249
+ const hasPkgJson = await fs.pathExists(
2250
+ path.join(outputDir, "package.json")
2251
+ );
2252
+ if (hasPkgJson) {
2253
+ spinner.start("Installing dependencies...");
2254
+ const success = await runInstall(outputDir);
2255
+ if (success) {
2256
+ spinner.succeed("Dependencies installed");
2257
+ } else {
2258
+ spinner.warn(
2259
+ chalk4.yellow(
2260
+ "Failed to install dependencies \u2014 run 'pnpm install' manually"
2261
+ )
2262
+ );
2263
+ }
2264
+ }
2265
+ }
2266
+ console.log();
2267
+ logger.success(chalk4.green.bold("Theme cloned successfully!"));
2268
+ console.log();
2269
+ console.log(
2270
+ chalk4.cyan(" Theme: ") + chalk4.white(`${themeName}@${version}`)
2271
+ );
2272
+ console.log(chalk4.cyan(" Location: ") + chalk4.white(outputDir));
2273
+ console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
2274
+ console.log();
2275
+ console.log(chalk4.cyan("Next steps:"));
2276
+ console.log(
2277
+ chalk4.gray(` cd ${path.relative(process.cwd(), outputDir)}`)
2278
+ );
2279
+ if (options.install === false) {
2280
+ console.log(chalk4.gray(" pnpm install"));
2281
+ }
2282
+ console.log(chalk4.gray(" onex build"));
2283
+ console.log();
2284
+ } catch (error) {
2285
+ spinner.fail(chalk4.red(`Clone failed: ${error.message}`));
2286
+ logger.error(error.stack || error.message);
2287
+ process.exit(1);
2288
+ }
2289
+ }
2060
2290
 
2061
- export { Logger, buildCommand, copyTemplate, createBlockCommand, createComponentCommand, createSectionCommand, detectPackageManager, downloadCommand, ensureOneXProject, getFeaturesDir, getProjectRoot, getThemesDir, getValidCategories, initCommand, installDependencies, isOneXProject, listCommand, listThemes, logger, pathExists, renderTemplate, themeExists, toCamelCase, toKebabCase, toPascalCase, uploadCommand, validateCategory, validateName, validateThemeName, writeFile };
2291
+ export { Logger, buildCommand, cloneCommand, copyTemplate, createBlockCommand, createComponentCommand, createSectionCommand, detectPackageManager, downloadCommand, ensureOneXProject, getFeaturesDir, getProjectRoot, getThemesDir, getValidCategories, initCommand, installDependencies, isOneXProject, listCommand, listThemes, logger, pathExists, renderTemplate, themeExists, toCamelCase, toKebabCase, toPascalCase, uploadCommand, validateCategory, validateName, validateThemeName, writeFile };
2062
2292
  //# sourceMappingURL=index.mjs.map
2063
2293
  //# sourceMappingURL=index.mjs.map