@onexapis/cli 1.1.17 → 1.1.18

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 (61) hide show
  1. package/README.md +82 -16
  2. package/dist/cli.js +522 -286
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cli.mjs +519 -283
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/index.js +47 -270
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +47 -270
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +1 -1
  11. package/templates/default/.env.example +1 -1
  12. package/templates/default/.mcp.json +8 -0
  13. package/templates/default/CLAUDE.md +941 -0
  14. package/templates/default/bundle-entry.ts +18 -0
  15. package/templates/default/index.ts +26 -0
  16. package/templates/default/package.json +37 -0
  17. package/templates/default/pages/about.ts +66 -0
  18. package/templates/default/pages/home.ts +93 -0
  19. package/templates/default/pages/showcase.ts +146 -0
  20. package/templates/default/sections/about/about-default.tsx +237 -0
  21. package/templates/default/sections/about/about.schema.ts +259 -0
  22. package/templates/default/sections/about/index.ts +15 -0
  23. package/templates/default/sections/cta/cta-default.tsx +180 -0
  24. package/templates/default/sections/cta/cta.schema.ts +210 -0
  25. package/templates/default/sections/cta/index.ts +11 -0
  26. package/templates/default/sections/features/features-default.tsx +154 -0
  27. package/templates/default/sections/features/features.schema.ts +330 -0
  28. package/templates/default/sections/features/index.ts +11 -0
  29. package/templates/default/sections/gallery/gallery-default.tsx +134 -0
  30. package/templates/default/sections/gallery/gallery.schema.ts +397 -0
  31. package/templates/default/sections/gallery/index.ts +11 -0
  32. package/templates/default/sections/hero/hero-default.tsx +212 -0
  33. package/templates/default/sections/hero/hero.schema.ts +273 -0
  34. package/templates/default/sections/hero/index.ts +15 -0
  35. package/templates/default/sections/stats/index.ts +11 -0
  36. package/templates/default/sections/stats/stats-default.tsx +103 -0
  37. package/templates/default/sections/stats/stats.schema.ts +266 -0
  38. package/templates/default/sections/testimonials/index.ts +11 -0
  39. package/templates/default/sections/testimonials/testimonials-default.tsx +130 -0
  40. package/templates/default/sections/testimonials/testimonials.schema.ts +371 -0
  41. package/templates/default/sections-registry.ts +32 -0
  42. package/templates/default/theme.config.ts +107 -0
  43. package/templates/default/theme.layout.ts +21 -0
  44. package/templates/default/tsconfig.json +16 -7
  45. package/templates/default/README.md.ejs +0 -129
  46. package/templates/default/esbuild.config.js +0 -81
  47. package/templates/default/package.json.ejs +0 -31
  48. package/templates/default/src/config.ts.ejs +0 -98
  49. package/templates/default/src/index.ts.ejs +0 -11
  50. package/templates/default/src/layout.ts +0 -23
  51. package/templates/default/src/manifest.ts.ejs +0 -47
  52. package/templates/default/src/pages/home.ts.ejs +0 -37
  53. package/templates/default/src/sections/footer/footer-default.tsx +0 -28
  54. package/templates/default/src/sections/footer/footer.schema.ts +0 -45
  55. package/templates/default/src/sections/footer/index.ts +0 -2
  56. package/templates/default/src/sections/header/header-default.tsx +0 -61
  57. package/templates/default/src/sections/header/header.schema.ts +0 -46
  58. package/templates/default/src/sections/header/index.ts +0 -2
  59. package/templates/default/src/sections/hero/hero-default.tsx +0 -52
  60. package/templates/default/src/sections/hero/hero.schema.ts +0 -52
  61. package/templates/default/src/sections/hero/index.ts +0 -2
package/dist/cli.js CHANGED
@@ -6,10 +6,10 @@ var ora = require('ora');
6
6
  var esbuild = require('esbuild');
7
7
  var path8 = require('path');
8
8
  var fs7 = require('fs/promises');
9
- var crypto = require('crypto');
9
+ var crypto2 = require('crypto');
10
10
  var glob = require('glob');
11
11
  var module$1 = require('module');
12
- var os = require('os');
12
+ var os3 = require('os');
13
13
  var dotenv = require('dotenv');
14
14
  var fs = require('fs-extra');
15
15
  var ejs = require('ejs');
@@ -19,7 +19,7 @@ var fs2 = require('fs');
19
19
  var inquirer = require('inquirer');
20
20
  var archiver = require('archiver');
21
21
  var FormData = require('form-data');
22
- var fetch = require('node-fetch');
22
+ var fetch2 = require('node-fetch');
23
23
  var clientS3 = require('@aws-sdk/client-s3');
24
24
  var AdmZip = require('adm-zip');
25
25
  var chokidar = require('chokidar');
@@ -52,8 +52,8 @@ var ora__default = /*#__PURE__*/_interopDefault(ora);
52
52
  var esbuild__namespace = /*#__PURE__*/_interopNamespace(esbuild);
53
53
  var path8__default = /*#__PURE__*/_interopDefault(path8);
54
54
  var fs7__default = /*#__PURE__*/_interopDefault(fs7);
55
- var crypto__default = /*#__PURE__*/_interopDefault(crypto);
56
- var os__default = /*#__PURE__*/_interopDefault(os);
55
+ var crypto2__default = /*#__PURE__*/_interopDefault(crypto2);
56
+ var os3__default = /*#__PURE__*/_interopDefault(os3);
57
57
  var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
58
58
  var fs__default = /*#__PURE__*/_interopDefault(fs);
59
59
  var ejs__default = /*#__PURE__*/_interopDefault(ejs);
@@ -61,7 +61,7 @@ var fs2__default = /*#__PURE__*/_interopDefault(fs2);
61
61
  var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
62
62
  var archiver__default = /*#__PURE__*/_interopDefault(archiver);
63
63
  var FormData__default = /*#__PURE__*/_interopDefault(FormData);
64
- var fetch__default = /*#__PURE__*/_interopDefault(fetch);
64
+ var fetch2__default = /*#__PURE__*/_interopDefault(fetch2);
65
65
  var AdmZip__default = /*#__PURE__*/_interopDefault(AdmZip);
66
66
  var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
67
67
  var http__default = /*#__PURE__*/_interopDefault(http);
@@ -138,7 +138,7 @@ __export(compile_theme_exports, {
138
138
  compilePreviewRuntime: () => compilePreviewRuntime,
139
139
  compileStandaloneTheme: () => compileStandaloneTheme,
140
140
  compileStandaloneThemeDev: () => compileStandaloneThemeDev,
141
- generateManifest: () => generateManifest2
141
+ generateManifest: () => generateManifest
142
142
  });
143
143
  async function resolveNodeModulesFile(startDir, relativePath) {
144
144
  let dir = startDir;
@@ -526,7 +526,7 @@ async function contentHashEntry(outputDir) {
526
526
  logger.warning("No entry file found in output, skipping content hash");
527
527
  return;
528
528
  }
529
- const hash2 = crypto__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
529
+ const hash2 = crypto2__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
530
530
  const hashedName2 = `bundle-entry-${hash2}.js`;
531
531
  const indexMapPath = path8__default.default.join(outputDir, "index.js.map");
532
532
  const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
@@ -544,7 +544,7 @@ async function contentHashEntry(outputDir) {
544
544
  logger.info(`Entry hashed: ${hashedName2}`);
545
545
  return;
546
546
  }
547
- const hash = crypto__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
547
+ const hash = crypto2__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
548
548
  const hashedName = `bundle-entry-${hash}.js`;
549
549
  const hashedMapName = `bundle-entry-${hash}.js.map`;
550
550
  entryContent = entryContent.replace(
@@ -582,7 +582,7 @@ async function extractDataRequirements(themePath) {
582
582
  }
583
583
  return requirements;
584
584
  }
585
- async function generateManifest2(themeName, themePath, outputDir) {
585
+ async function generateManifest(themeName, themePath, outputDir) {
586
586
  let version2 = "1.0.0";
587
587
  let themeId = themeName;
588
588
  try {
@@ -717,7 +717,7 @@ async function compileStandaloneTheme(themePath, themeName) {
717
717
  } catch {
718
718
  }
719
719
  await contentHashEntry(outputDir);
720
- await generateManifest2(themeName, themePath, outputDir);
720
+ await generateManifest(themeName, themePath, outputDir);
721
721
  await generateThemeData(themePath, outputDir, themeName);
722
722
  if (result.metafile) {
723
723
  const outputs = result.metafile.outputs;
@@ -798,7 +798,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
798
798
  };
799
799
  const context2 = await esbuild__namespace.context(buildOptions);
800
800
  await context2.rebuild();
801
- await generateManifest2(themeName, themePath, outputDir);
801
+ await generateManifest(themeName, themePath, outputDir);
802
802
  await generateThemeData(themePath, outputDir, themeName);
803
803
  return { context: context2, outputDir };
804
804
  }
@@ -1534,38 +1534,13 @@ async function initCommand(projectName, options = {}) {
1534
1534
  try {
1535
1535
  fs2__default.default.mkdirSync(projectPath, { recursive: true });
1536
1536
  await copyTemplate(template, projectPath, data);
1537
- const srcPath = path8__default.default.join(projectPath, "src");
1538
- fs2__default.default.mkdirSync(srcPath, { recursive: true });
1539
- const manifestContent = generateManifest(data);
1540
- await writeFile(path8__default.default.join(srcPath, "manifest.ts"), manifestContent);
1541
- const configContent = generateThemeConfig(data);
1542
- await writeFile(path8__default.default.join(srcPath, "config.ts"), configContent);
1543
- const layoutContent = generateThemeLayout(data);
1544
- await writeFile(path8__default.default.join(srcPath, "layout.ts"), layoutContent);
1545
- const indexContent = generateThemeIndex(data);
1546
- await writeFile(path8__default.default.join(srcPath, "index.ts"), indexContent);
1547
- const sectionsPath = path8__default.default.join(srcPath, "sections");
1548
- fs2__default.default.mkdirSync(sectionsPath, { recursive: true });
1549
- await writeFile(
1550
- path8__default.default.join(sectionsPath, "README.md"),
1551
- `# ${displayName} Sections
1552
-
1553
- Add your theme-specific sections here.
1554
- `
1555
- );
1556
- const blocksPath = path8__default.default.join(srcPath, "blocks");
1557
- fs2__default.default.mkdirSync(blocksPath, { recursive: true });
1558
- await writeFile(
1559
- path8__default.default.join(blocksPath, "README.md"),
1560
- `# ${displayName} Blocks
1561
-
1562
- Add your theme-specific blocks here.
1563
- `
1537
+ await renameThemeInFiles(
1538
+ projectPath,
1539
+ name,
1540
+ displayName,
1541
+ description,
1542
+ author
1564
1543
  );
1565
- const pagesPath = path8__default.default.join(srcPath, "pages");
1566
- fs2__default.default.mkdirSync(pagesPath, { recursive: true });
1567
- const homePageContent = generateHomePage(data);
1568
- await writeFile(path8__default.default.join(pagesPath, "home.ts"), homePageContent);
1569
1544
  logger.stopSpinner(true, "Project structure created!");
1570
1545
  if (options.git) {
1571
1546
  logger.startSpinner("Initializing git repository...");
@@ -1609,14 +1584,14 @@ Add your theme-specific blocks here.
1609
1584
  logger.log(` npm run dev # Start development mode`);
1610
1585
  logger.newLine();
1611
1586
  logger.section("Theme structure:");
1612
- logger.log(" src/manifest.ts - Theme manifest and exports");
1587
+ logger.log(" bundle-entry.ts - Theme manifest and exports");
1613
1588
  logger.log(
1614
- " src/config.ts - Design tokens (colors, typography, etc.)"
1589
+ " theme.config.ts - Design tokens (colors, typography, etc.)"
1615
1590
  );
1616
- logger.log(" src/layout.ts - Header and footer configuration");
1617
- logger.log(" src/sections/ - Custom sections for your theme");
1618
- logger.log(" src/blocks/ - Reusable blocks");
1619
- logger.log(" src/pages/ - Page configurations");
1591
+ logger.log(" theme.layout.ts - Header and footer configuration");
1592
+ logger.log(" sections/ - Custom sections for your theme");
1593
+ logger.log(" pages/ - Page configurations");
1594
+ logger.log(" CLAUDE.md - AI assistant context");
1620
1595
  logger.newLine();
1621
1596
  logger.success(`Happy theming! \u{1F3A8}`);
1622
1597
  } catch (error) {
@@ -1630,231 +1605,33 @@ Add your theme-specific blocks here.
1630
1605
  process.exit(1);
1631
1606
  }
1632
1607
  }
1633
- function generateManifest(data) {
1634
- return `import type { ThemeExport } from "@onexapis/core";
1635
-
1636
- /**
1637
- * ${data.displayName} Theme Manifest
1638
- * ${data.description}
1639
- */
1640
- export const manifest: ThemeExport = {
1641
- id: "${data.themeName}",
1642
- name: "${data.displayName}",
1643
- description: "${data.description}",
1644
- version: "1.0.0",
1645
- author: "${data.author}",
1646
-
1647
- // Theme configuration
1648
- config: () => import("./config").then((m) => m.themeConfig),
1649
-
1650
- // Theme layout (header/footer sections)
1651
- layout: () => import("./layout").then((m) => m.themeLayout),
1652
-
1653
- // Available sections in this theme
1654
- sections: {
1655
- // Example: hero: () => import("./sections/hero").then((m) => m.heroSchema),
1656
- },
1657
-
1658
- // Available blocks in this theme
1659
- blocks: {
1660
- // Example: productCard: () => import("./blocks/product-card").then((m) => m.productCardDefinition),
1661
- },
1662
-
1663
- // Default pages
1664
- pages: {
1665
- home: () => import("./pages/home").then((m) => m.homePageConfig),
1666
- },
1667
-
1668
- // Supported page types
1669
- supportedPageTypes: ["home", "about", "contact", "custom"],
1670
-
1671
- // Preview image (optional)
1672
- preview: undefined,
1673
-
1674
- // Tags for categorization (optional)
1675
- tags: ["custom"],
1676
- };
1677
-
1678
- export default manifest;
1679
- `;
1680
- }
1681
- function generateThemeConfig(data) {
1682
- return `import type { ThemeConfig } from "@onexapis/core";
1683
-
1684
- /**
1685
- * ${data.displayName} Theme Configuration
1686
- * Design tokens: colors, typography, spacing, etc.
1687
- */
1688
- export const themeConfig: ThemeConfig = {
1689
- // Color palette
1690
- colors: {
1691
- primary: {
1692
- 50: "#eff6ff",
1693
- 100: "#dbeafe",
1694
- 200: "#bfdbfe",
1695
- 300: "#93c5fd",
1696
- 400: "#60a5fa",
1697
- 500: "#3b82f6",
1698
- 600: "#2563eb",
1699
- 700: "#1d4ed8",
1700
- 800: "#1e40af",
1701
- 900: "#1e3a8a",
1702
- },
1703
- secondary: {
1704
- 50: "#f8fafc",
1705
- 100: "#f1f5f9",
1706
- 200: "#e2e8f0",
1707
- 300: "#cbd5e1",
1708
- 400: "#94a3b8",
1709
- 500: "#64748b",
1710
- 600: "#475569",
1711
- 700: "#334155",
1712
- 800: "#1e293b",
1713
- 900: "#0f172a",
1714
- },
1715
- accent: {
1716
- 50: "#fdf4ff",
1717
- 100: "#fae8ff",
1718
- 200: "#f5d0fe",
1719
- 300: "#f0abfc",
1720
- 400: "#e879f9",
1721
- 500: "#d946ef",
1722
- 600: "#c026d3",
1723
- 700: "#a21caf",
1724
- 800: "#86198f",
1725
- 900: "#701a75",
1726
- },
1727
- },
1728
-
1729
- // Typography
1730
- typography: {
1731
- fontFamily: {
1732
- sans: ["Inter", "system-ui", "sans-serif"],
1733
- serif: ["Georgia", "serif"],
1734
- mono: ["Monaco", "monospace"],
1735
- },
1736
- fontSize: {
1737
- xs: "0.75rem",
1738
- sm: "0.875rem",
1739
- base: "1rem",
1740
- lg: "1.125rem",
1741
- xl: "1.25rem",
1742
- "2xl": "1.5rem",
1743
- "3xl": "1.875rem",
1744
- "4xl": "2.25rem",
1745
- "5xl": "3rem",
1746
- },
1747
- },
1748
-
1749
- // Spacing
1750
- spacing: {
1751
- xs: "0.5rem",
1752
- sm: "1rem",
1753
- md: "1.5rem",
1754
- lg: "2rem",
1755
- xl: "3rem",
1756
- "2xl": "4rem",
1757
- "3xl": "6rem",
1758
- "4xl": "8rem",
1759
- },
1760
-
1761
- // Border radius
1762
- borderRadius: {
1763
- none: "0",
1764
- sm: "0.125rem",
1765
- md: "0.375rem",
1766
- lg: "0.5rem",
1767
- xl: "0.75rem",
1768
- full: "9999px",
1769
- },
1770
-
1771
- // Breakpoints
1772
- breakpoints: {
1773
- sm: "640px",
1774
- md: "768px",
1775
- lg: "1024px",
1776
- xl: "1280px",
1777
- "2xl": "1536px",
1778
- },
1779
- };
1780
- `;
1781
- }
1782
- function generateThemeLayout(data) {
1783
- return `import type { ThemeLayoutConfig } from "@onexapis/core";
1784
-
1785
- /**
1786
- * ${data.themeName} Theme Layout
1787
- * Define header and footer sections
1788
- */
1789
- export const themeLayout: ThemeLayoutConfig = {
1790
- // Header section configuration
1791
- header: undefined,
1792
- // Example:
1793
- // header: {
1794
- // type: "header",
1795
- // template: "default",
1796
- // enabled: true,
1797
- // settings: {},
1798
- // },
1799
-
1800
- // Footer section configuration
1801
- footer: undefined,
1802
- // Example:
1803
- // footer: {
1804
- // type: "footer",
1805
- // template: "default",
1806
- // enabled: true,
1807
- // settings: {},
1808
- // },
1809
- };
1810
- `;
1811
- }
1812
- function generateThemeIndex(data) {
1813
- return `/**
1814
- * ${data.themeNamePascal} Theme
1815
- */
1816
-
1817
- export { manifest as ${data.themeNamePascal}Manifest } from "./manifest";
1818
- export { themeConfig as ${data.themeNamePascal}Config } from "./config";
1819
- export { themeLayout as ${data.themeNamePascal}Layout } from "./layout";
1820
- `;
1821
- }
1822
- function generateHomePage(data) {
1823
- return `import type { PageConfig } from "@onexapis/core";
1824
-
1825
- /**
1826
- * Home Page Configuration
1827
- */
1828
- export const homePageConfig: PageConfig = {
1829
- type: "home",
1830
- title: "${data.displayName}",
1831
- description: "Welcome to ${data.displayName}",
1832
-
1833
- // SEO metadata
1834
- seo: {
1835
- title: "${data.displayName} - Home",
1836
- description: "Welcome to ${data.displayName}",
1837
- keywords: [],
1838
- ogImage: undefined,
1839
- },
1840
-
1841
- // Page sections
1842
- sections: [
1843
- // Add your sections here
1844
- // Example:
1845
- // {
1846
- // id: "hero-1",
1847
- // type: "hero",
1848
- // template: "default",
1849
- // order: 0,
1850
- // enabled: true,
1851
- // settings: {},
1852
- // components: [],
1853
- // blocks: [],
1854
- // },
1855
- ],
1856
- };
1857
- `;
1608
+ async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
1609
+ const configPath = path8__default.default.join(projectPath, "theme.config.ts");
1610
+ if (fs2__default.default.existsSync(configPath)) {
1611
+ let content = fs2__default.default.readFileSync(configPath, "utf-8");
1612
+ content = content.replace(
1613
+ /name: "My Simple Theme"/,
1614
+ `name: "${displayName}"`
1615
+ );
1616
+ content = content.replace(
1617
+ /description: ".*?"/,
1618
+ `description: "${description}"`
1619
+ );
1620
+ fs2__default.default.writeFileSync(configPath, content, "utf-8");
1621
+ }
1622
+ const pkgPath = path8__default.default.join(projectPath, "package.json");
1623
+ if (fs2__default.default.existsSync(pkgPath)) {
1624
+ let content = fs2__default.default.readFileSync(pkgPath, "utf-8");
1625
+ content = content.replace(
1626
+ /@onex-themes\/my-simple/g,
1627
+ `@onex-themes/${themeName}`
1628
+ );
1629
+ content = content.replace(
1630
+ /"description": ".*?"/,
1631
+ `"description": "${description}"`
1632
+ );
1633
+ fs2__default.default.writeFileSync(pkgPath, content, "utf-8");
1634
+ }
1858
1635
  }
1859
1636
 
1860
1637
  // src/commands/create-section.ts
@@ -3181,7 +2958,7 @@ async function deployCommand(options) {
3181
2958
  if (options.environment) {
3182
2959
  formData.append("environment", options.environment);
3183
2960
  }
3184
- const response = await fetch__default.default(uploadEndpoint, {
2961
+ const response = await fetch2__default.default(uploadEndpoint, {
3185
2962
  method: "POST",
3186
2963
  body: formData,
3187
2964
  headers: formData.getHeaders()
@@ -3262,7 +3039,7 @@ function getBucketName(env) {
3262
3039
  return process.env.BUCKET_NAME;
3263
3040
  }
3264
3041
  const environment = env || process.env.ENVIRONMENT || "staging";
3265
- return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
3042
+ return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3266
3043
  }
3267
3044
  async function findCompiledThemeDir(themeId, version2) {
3268
3045
  const searchPaths = [path8__default.default.resolve(process.cwd(), "dist")];
@@ -3378,7 +3155,7 @@ async function uploadCommand(options) {
3378
3155
  }
3379
3156
  spinner.succeed(`Found compiled theme at: ${compiledDir}`);
3380
3157
  spinner.start("Creating bundle.zip...");
3381
- const tmpDir = os__default.default.tmpdir();
3158
+ const tmpDir = os3__default.default.tmpdir();
3382
3159
  const bundleZipPath = path8__default.default.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
3383
3160
  await createZipFromDir(compiledDir, bundleZipPath);
3384
3161
  const bundleZipBuffer = await fs__default.default.readFile(bundleZipPath);
@@ -3531,7 +3308,7 @@ function getBucketName2(env) {
3531
3308
  return process.env.BUCKET_NAME;
3532
3309
  }
3533
3310
  const environment = env || process.env.ENVIRONMENT || "staging";
3534
- return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
3311
+ return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3535
3312
  }
3536
3313
  async function streamToString(stream) {
3537
3314
  const chunks = [];
@@ -3732,7 +3509,7 @@ function getBucketName3(env) {
3732
3509
  return process.env.BUCKET_NAME;
3733
3510
  }
3734
3511
  const environment = env || process.env.ENVIRONMENT || "staging";
3735
- return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
3512
+ return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3736
3513
  }
3737
3514
  async function streamToString2(stream) {
3738
3515
  const chunks = [];
@@ -3776,8 +3553,8 @@ function runInstall(cwd) {
3776
3553
  });
3777
3554
  }
3778
3555
  async function promptThemeName(originalName) {
3779
- const { default: inquirer6 } = await import('inquirer');
3780
- const { themeName } = await inquirer6.prompt([
3556
+ const { default: inquirer7 } = await import('inquirer');
3557
+ const { themeName } = await inquirer7.prompt([
3781
3558
  {
3782
3559
  type: "input",
3783
3560
  name: "themeName",
@@ -3930,7 +3707,7 @@ async function cloneCommand(themeName, options) {
3930
3707
  [
3931
3708
  "# API Configuration (enables real data in preview)",
3932
3709
  "# Get your Company ID from the OneX dashboard",
3933
- "NEXT_PUBLIC_API_URL=https://api-dev.onexeos.com",
3710
+ "NEXT_PUBLIC_API_URL=https://platform-dev.onexeos.com",
3934
3711
  "NEXT_PUBLIC_COMPANY_ID=",
3935
3712
  ""
3936
3713
  ].join("\n")
@@ -4278,7 +4055,7 @@ async function devCommand(options) {
4278
4055
  logger.info(`File changed: ${filePath}`);
4279
4056
  try {
4280
4057
  await context2.rebuild();
4281
- await generateManifest2(themeName, themePath, outputDir);
4058
+ await generateManifest(themeName, themePath, outputDir);
4282
4059
  server.broadcast({ type: "reload", timestamp: Date.now() });
4283
4060
  logger.success("Rebuilt successfully");
4284
4061
  } catch (error) {
@@ -4313,7 +4090,7 @@ async function devCommand(options) {
4313
4090
 
4314
4091
  // src/commands/config.ts
4315
4092
  init_logger();
4316
- var CONFIG_DIR = path8__default.default.join(os__default.default.homedir(), ".onexthm");
4093
+ var CONFIG_DIR = path8__default.default.join(os3__default.default.homedir(), ".onexthm");
4317
4094
  var CONFIG_FILE = path8__default.default.join(CONFIG_DIR, ".env");
4318
4095
  var CONFIG_ENTRIES = [
4319
4096
  {
@@ -4343,7 +4120,7 @@ var CONFIG_ENTRIES = [
4343
4120
  key: "NEXT_PUBLIC_API_URL",
4344
4121
  label: "API URL",
4345
4122
  required: false,
4346
- defaultValue: "https://api-dev.onexeos.com"
4123
+ defaultValue: "https://platform-dev.onexeos.com"
4347
4124
  },
4348
4125
  {
4349
4126
  key: "NEXT_PUBLIC_COMPANY_ID",
@@ -4454,6 +4231,461 @@ async function configCommand() {
4454
4231
  );
4455
4232
  }
4456
4233
 
4234
+ // src/commands/login.ts
4235
+ init_logger();
4236
+ var AUTH_DIR = path8__default.default.join(os3__default.default.homedir(), ".onexthm");
4237
+ var AUTH_FILE = path8__default.default.join(AUTH_DIR, "auth.json");
4238
+ function getApiUrl() {
4239
+ return process.env.NEXT_PUBLIC_API_URL || process.env.ONEXTHM_API_URL || "https://platform-dev.onexeos.com";
4240
+ }
4241
+ async function saveAuthTokens(tokens) {
4242
+ await fs__default.default.ensureDir(AUTH_DIR);
4243
+ const key = getMachineKey();
4244
+ const data = JSON.stringify(tokens);
4245
+ const encrypted = encrypt(data, key);
4246
+ await fs__default.default.writeFile(AUTH_FILE, encrypted, "utf-8");
4247
+ }
4248
+ function loadAuthTokens() {
4249
+ try {
4250
+ if (!fs__default.default.existsSync(AUTH_FILE)) return null;
4251
+ const encrypted = fs__default.default.readFileSync(AUTH_FILE, "utf-8");
4252
+ const key = getMachineKey();
4253
+ const data = decrypt(encrypted, key);
4254
+ return JSON.parse(data);
4255
+ } catch {
4256
+ return null;
4257
+ }
4258
+ }
4259
+ async function clearAuthTokens() {
4260
+ try {
4261
+ await fs__default.default.remove(AUTH_FILE);
4262
+ } catch {
4263
+ }
4264
+ }
4265
+ function isTokenExpired(tokens) {
4266
+ return Date.now() / 1e3 > tokens.expiresAt - 60;
4267
+ }
4268
+ async function getValidTokens() {
4269
+ const tokens = loadAuthTokens();
4270
+ if (!tokens) return null;
4271
+ if (!isTokenExpired(tokens)) return tokens;
4272
+ try {
4273
+ const apiUrl = getApiUrl();
4274
+ const response = await fetch(`${apiUrl}/auth/refresh`, {
4275
+ method: "POST",
4276
+ headers: { "Content-Type": "application/json" },
4277
+ body: JSON.stringify({ refresh_token: tokens.refreshToken })
4278
+ });
4279
+ if (!response.ok) {
4280
+ await clearAuthTokens();
4281
+ return null;
4282
+ }
4283
+ const data = await response.json();
4284
+ const body = data.statusCode ? data.body : data;
4285
+ const refreshed = {
4286
+ ...tokens,
4287
+ accessToken: body.AccessToken || tokens.accessToken,
4288
+ idToken: body.IdToken || tokens.idToken,
4289
+ expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
4290
+ };
4291
+ await saveAuthTokens(refreshed);
4292
+ return refreshed;
4293
+ } catch {
4294
+ await clearAuthTokens();
4295
+ return null;
4296
+ }
4297
+ }
4298
+ async function authenticatedFetch(url, init) {
4299
+ const tokens = await getValidTokens();
4300
+ if (!tokens) {
4301
+ throw new Error("Not logged in. Run: onexthm login");
4302
+ }
4303
+ const headers = new Headers(init?.headers);
4304
+ headers.set("Authorization", `Bearer ${tokens.idToken}`);
4305
+ headers.set("Content-Type", "application/json");
4306
+ return fetch(url, { ...init, headers });
4307
+ }
4308
+ function getMachineKey() {
4309
+ let seed;
4310
+ if (process.platform === "darwin") {
4311
+ seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
4312
+ } else if (process.platform === "linux") {
4313
+ try {
4314
+ seed = `onexthm:${fs__default.default.readFileSync("/etc/machine-id", "utf-8").trim()}`;
4315
+ } catch {
4316
+ seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
4317
+ }
4318
+ } else {
4319
+ seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
4320
+ }
4321
+ return crypto2__default.default.createHash("sha256").update(seed).digest();
4322
+ }
4323
+ function encrypt(text, key) {
4324
+ const iv = crypto2__default.default.randomBytes(16);
4325
+ const cipher = crypto2__default.default.createCipheriv("aes-256-gcm", key, iv);
4326
+ let encrypted = cipher.update(text, "utf-8", "hex");
4327
+ encrypted += cipher.final("hex");
4328
+ const tag = cipher.getAuthTag();
4329
+ return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
4330
+ }
4331
+ function decrypt(text, key) {
4332
+ const [ivHex, tagHex, encrypted] = text.split(":");
4333
+ const iv = Buffer.from(ivHex, "hex");
4334
+ const tag = Buffer.from(tagHex, "hex");
4335
+ const decipher = crypto2__default.default.createDecipheriv("aes-256-gcm", key, iv);
4336
+ decipher.setAuthTag(tag);
4337
+ let decrypted = decipher.update(encrypted, "hex", "utf-8");
4338
+ decrypted += decipher.final("utf-8");
4339
+ return decrypted;
4340
+ }
4341
+ function parseJwtClaims(idToken) {
4342
+ try {
4343
+ const payload = idToken.split(".")[1];
4344
+ const decoded = Buffer.from(payload, "base64url").toString("utf-8");
4345
+ return JSON.parse(decoded);
4346
+ } catch {
4347
+ return {};
4348
+ }
4349
+ }
4350
+
4351
+ // src/commands/login.ts
4352
+ async function loginCommand() {
4353
+ logger.header("OneX Theme Developer Login");
4354
+ const existing = loadAuthTokens();
4355
+ if (existing) {
4356
+ logger.info(`Already logged in as: ${existing.user.email}`);
4357
+ const { relogin } = await inquirer__default.default.prompt([
4358
+ {
4359
+ type: "confirm",
4360
+ name: "relogin",
4361
+ message: "Re-login with different account?",
4362
+ default: false
4363
+ }
4364
+ ]);
4365
+ if (!relogin) return;
4366
+ }
4367
+ const { email, password } = await inquirer__default.default.prompt([
4368
+ {
4369
+ type: "input",
4370
+ name: "email",
4371
+ message: "Email:",
4372
+ validate: (input) => input.includes("@") ? true : "Enter a valid email"
4373
+ },
4374
+ {
4375
+ type: "password",
4376
+ name: "password",
4377
+ message: "Password:",
4378
+ validate: (input) => input.length >= 6 ? true : "Password too short"
4379
+ }
4380
+ ]);
4381
+ logger.startSpinner("Logging in...");
4382
+ try {
4383
+ const apiUrl = getApiUrl();
4384
+ const response = await fetch(`${apiUrl}/auth/login`, {
4385
+ method: "POST",
4386
+ headers: { "Content-Type": "application/json" },
4387
+ body: JSON.stringify({ username: email, password })
4388
+ });
4389
+ const raw = await response.json();
4390
+ const data = raw.statusCode ? raw.body : raw;
4391
+ if (!response.ok || data.error) {
4392
+ logger.stopSpinner(false, "Login failed");
4393
+ logger.error(data.message || data.error || "Invalid credentials");
4394
+ process.exit(1);
4395
+ }
4396
+ const idToken = data.IdToken;
4397
+ const accessToken = data.AccessToken;
4398
+ const refreshToken = data.RefreshToken;
4399
+ const expiresIn = data.ExpiresIn || 3600;
4400
+ if (!idToken) {
4401
+ logger.stopSpinner(false, "Login failed");
4402
+ logger.error("No token received from server");
4403
+ process.exit(1);
4404
+ }
4405
+ const claims = parseJwtClaims(idToken);
4406
+ const tokens = {
4407
+ accessToken,
4408
+ idToken,
4409
+ refreshToken,
4410
+ expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
4411
+ user: {
4412
+ email: claims.email || email,
4413
+ name: claims.name,
4414
+ companyId: claims["custom:company_id"],
4415
+ userId: claims.sub
4416
+ }
4417
+ };
4418
+ await saveAuthTokens(tokens);
4419
+ logger.stopSpinner(true, "Logged in!");
4420
+ logger.newLine();
4421
+ logger.info(` Email: ${tokens.user.email}`);
4422
+ if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
4423
+ if (tokens.user.companyId)
4424
+ logger.info(` Company: ${tokens.user.companyId}`);
4425
+ logger.newLine();
4426
+ logger.success("Token stored securely in ~/.onexthm/auth.json (encrypted)");
4427
+ } catch (error) {
4428
+ logger.stopSpinner(false, "Login failed");
4429
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4430
+ process.exit(1);
4431
+ }
4432
+ }
4433
+
4434
+ // src/commands/logout.ts
4435
+ init_logger();
4436
+ async function logoutCommand() {
4437
+ const tokens = loadAuthTokens();
4438
+ if (!tokens) {
4439
+ logger.info("Not logged in.");
4440
+ return;
4441
+ }
4442
+ await clearAuthTokens();
4443
+ logger.success(`Logged out (was: ${tokens.user.email})`);
4444
+ }
4445
+
4446
+ // src/commands/whoami.ts
4447
+ init_logger();
4448
+ async function whoamiCommand() {
4449
+ const tokens = loadAuthTokens();
4450
+ if (!tokens) {
4451
+ logger.error("Not logged in. Run: onexthm login");
4452
+ process.exit(1);
4453
+ }
4454
+ const expired = isTokenExpired(tokens);
4455
+ logger.header("OneX Theme Developer");
4456
+ logger.info(` Email: ${tokens.user.email}`);
4457
+ if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
4458
+ if (tokens.user.companyId)
4459
+ logger.info(` Company: ${tokens.user.companyId}`);
4460
+ logger.info(
4461
+ ` Status: ${expired ? "\u26A0 Token expired (will auto-refresh)" : "\u2713 Active"}`
4462
+ );
4463
+ }
4464
+
4465
+ // src/commands/publish.ts
4466
+ init_logger();
4467
+ async function publishCommand(options) {
4468
+ logger.header("OneX Theme Publish");
4469
+ const tokens = await getValidTokens();
4470
+ if (!tokens) {
4471
+ logger.error("Not logged in. Run: onexthm login");
4472
+ process.exit(1);
4473
+ }
4474
+ logger.info(`Logged in as: ${tokens.user.email}`);
4475
+ let themePath;
4476
+ if (options.theme) {
4477
+ themePath = path8__default.default.resolve(options.theme);
4478
+ } else {
4479
+ const isThemeDir = [
4480
+ "theme.config.ts",
4481
+ "bundle-entry.ts",
4482
+ "manifest.ts"
4483
+ ].some((f) => fs__default.default.existsSync(path8__default.default.join(process.cwd(), f)));
4484
+ if (isThemeDir) {
4485
+ themePath = process.cwd();
4486
+ } else {
4487
+ logger.error(
4488
+ "Not in a theme directory. Run from theme root or use --theme flag."
4489
+ );
4490
+ process.exit(1);
4491
+ }
4492
+ }
4493
+ const pkgPath = path8__default.default.join(themePath, "package.json");
4494
+ if (!fs__default.default.existsSync(pkgPath)) {
4495
+ logger.error("No package.json found in theme directory");
4496
+ process.exit(1);
4497
+ }
4498
+ const pkg = fs__default.default.readJsonSync(pkgPath);
4499
+ const themeId = pkg.name?.replace("@onex-themes/", "") || path8__default.default.basename(themePath);
4500
+ const version2 = pkg.version || "1.0.0";
4501
+ logger.newLine();
4502
+ logger.info(`Theme: ${themeId}`);
4503
+ logger.info(`Version: ${version2}`);
4504
+ logger.newLine();
4505
+ const apiUrl = getApiUrl();
4506
+ logger.startSpinner("Registering theme...");
4507
+ try {
4508
+ const regResponse = await authenticatedFetch(
4509
+ `${apiUrl}/website-api/themes/register`,
4510
+ {
4511
+ method: "POST",
4512
+ body: JSON.stringify({
4513
+ themeId,
4514
+ name: pkg.displayName || themeId,
4515
+ description: pkg.description || "",
4516
+ email: tokens.user.email
4517
+ })
4518
+ }
4519
+ );
4520
+ const regData = await regResponse.json();
4521
+ const regBody = regData.statusCode ? regData.body : regData;
4522
+ if (!regResponse.ok && regBody.error && !regBody.error.includes("already registered")) {
4523
+ logger.stopSpinner(false, "Registration failed");
4524
+ logger.error(regBody.error);
4525
+ process.exit(1);
4526
+ }
4527
+ logger.stopSpinner(true, regBody.message || "Theme registered");
4528
+ } catch (error) {
4529
+ logger.stopSpinner(false, "Registration failed");
4530
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4531
+ process.exit(1);
4532
+ }
4533
+ logger.startSpinner("Building theme...");
4534
+ try {
4535
+ const { execSync: execSync3 } = await import('child_process');
4536
+ execSync3(
4537
+ "npx tsup bundle-entry.ts --format esm --target es2020 --outDir dist",
4538
+ {
4539
+ cwd: themePath,
4540
+ stdio: "ignore"
4541
+ }
4542
+ );
4543
+ logger.stopSpinner(true, "Theme compiled");
4544
+ } catch {
4545
+ logger.stopSpinner(false, "Build failed");
4546
+ logger.error("Run 'onexthm build' to see build errors");
4547
+ process.exit(1);
4548
+ }
4549
+ logger.startSpinner("Getting upload URL...");
4550
+ let bundleUploadUrl;
4551
+ let sourceUploadUrl;
4552
+ try {
4553
+ const pubResponse = await authenticatedFetch(
4554
+ `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`,
4555
+ {
4556
+ method: "POST",
4557
+ body: JSON.stringify({ version: version2 })
4558
+ }
4559
+ );
4560
+ const pubData = await pubResponse.json();
4561
+ const pubBody = pubData.statusCode ? pubData.body : pubData;
4562
+ if (!pubResponse.ok || !pubBody.bundleUploadUrl) {
4563
+ logger.stopSpinner(false, "Failed to get upload URL");
4564
+ logger.error(pubBody.error || "Server error");
4565
+ process.exit(1);
4566
+ }
4567
+ bundleUploadUrl = pubBody.bundleUploadUrl;
4568
+ sourceUploadUrl = pubBody.sourceUploadUrl;
4569
+ logger.stopSpinner(true, "Upload URL obtained");
4570
+ } catch (error) {
4571
+ logger.stopSpinner(false, "Failed");
4572
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4573
+ process.exit(1);
4574
+ }
4575
+ logger.startSpinner("Uploading bundle...");
4576
+ try {
4577
+ const archiver3 = await import('archiver');
4578
+ const { createWriteStream } = await import('fs');
4579
+ const distDir = path8__default.default.join(themePath, "dist");
4580
+ if (!fs__default.default.existsSync(distDir)) {
4581
+ logger.stopSpinner(false, "No dist/ directory");
4582
+ logger.error("Build the theme first: onexthm build");
4583
+ process.exit(1);
4584
+ }
4585
+ const bundleZipPath = path8__default.default.join(themePath, "dist", "bundle.zip");
4586
+ await createZip(distDir, bundleZipPath, ["bundle.zip"]);
4587
+ const bundleBuffer = fs__default.default.readFileSync(bundleZipPath);
4588
+ const bundleRes = await fetch(bundleUploadUrl, {
4589
+ method: "PUT",
4590
+ headers: { "Content-Type": "application/zip" },
4591
+ body: bundleBuffer
4592
+ });
4593
+ if (!bundleRes.ok) {
4594
+ throw new Error(`Upload failed: ${bundleRes.status}`);
4595
+ }
4596
+ const sizeMB = (bundleBuffer.length / 1024 / 1024).toFixed(2);
4597
+ logger.stopSpinner(true, `Bundle uploaded (${sizeMB} MB)`);
4598
+ } catch (error) {
4599
+ logger.stopSpinner(false, "Upload failed");
4600
+ logger.error(error instanceof Error ? error.message : "Upload error");
4601
+ process.exit(1);
4602
+ }
4603
+ logger.startSpinner("Uploading source...");
4604
+ try {
4605
+ const sourceZipPath = path8__default.default.join(themePath, "dist", "source.zip");
4606
+ await createZip(themePath, sourceZipPath, [
4607
+ "node_modules",
4608
+ "dist",
4609
+ ".git",
4610
+ ".env",
4611
+ ".env.local"
4612
+ ]);
4613
+ const sourceBuffer = fs__default.default.readFileSync(sourceZipPath);
4614
+ const sourceRes = await fetch(sourceUploadUrl, {
4615
+ method: "PUT",
4616
+ headers: { "Content-Type": "application/zip" },
4617
+ body: sourceBuffer
4618
+ });
4619
+ if (!sourceRes.ok) {
4620
+ throw new Error(`Source upload failed: ${sourceRes.status}`);
4621
+ }
4622
+ const sizeMB = (sourceBuffer.length / 1024 / 1024).toFixed(2);
4623
+ logger.stopSpinner(true, `Source uploaded (${sizeMB} MB)`);
4624
+ } catch (error) {
4625
+ logger.stopSpinner(false, "Source upload failed");
4626
+ logger.info("Source upload skipped (bundle was uploaded successfully)");
4627
+ }
4628
+ logger.startSpinner("Scanning and publishing...");
4629
+ try {
4630
+ const confirmResponse = await authenticatedFetch(
4631
+ `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/confirm`,
4632
+ { method: "POST" }
4633
+ );
4634
+ const confirmData = await confirmResponse.json();
4635
+ const confirmBody = confirmData.statusCode ? confirmData.body : confirmData;
4636
+ if (!confirmResponse.ok || !confirmBody.success) {
4637
+ logger.stopSpinner(false, "Publishing failed");
4638
+ if (confirmBody.violations) {
4639
+ logger.error("Theme rejected \u2014 security violations found:");
4640
+ for (const v of confirmBody.violations) {
4641
+ logger.log(` \u274C ${v.file}: ${v.violation}`);
4642
+ }
4643
+ } else {
4644
+ logger.error(confirmBody.error || "Unknown error");
4645
+ }
4646
+ if (confirmBody.warnings?.length) {
4647
+ logger.newLine();
4648
+ logger.info("Warnings:");
4649
+ for (const w of confirmBody.warnings) {
4650
+ logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
4651
+ }
4652
+ }
4653
+ process.exit(1);
4654
+ }
4655
+ logger.stopSpinner(true, confirmBody.message || "Published!");
4656
+ if (confirmBody.warnings?.length) {
4657
+ logger.newLine();
4658
+ logger.info("Warnings (non-blocking):");
4659
+ for (const w of confirmBody.warnings) {
4660
+ logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
4661
+ }
4662
+ }
4663
+ } catch (error) {
4664
+ logger.stopSpinner(false, "Publishing failed");
4665
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4666
+ process.exit(1);
4667
+ }
4668
+ logger.newLine();
4669
+ logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
4670
+ }
4671
+ async function createZip(sourceDir, outputPath, exclude) {
4672
+ const archiver3 = (await import('archiver')).default;
4673
+ const { createWriteStream } = await import('fs');
4674
+ return new Promise((resolve, reject) => {
4675
+ const output = createWriteStream(outputPath);
4676
+ const archive = archiver3("zip", { zlib: { level: 9 } });
4677
+ output.on("close", resolve);
4678
+ archive.on("error", reject);
4679
+ archive.pipe(output);
4680
+ archive.glob("**/*", {
4681
+ cwd: sourceDir,
4682
+ ignore: exclude.map((e) => `${e}/**`),
4683
+ dot: false
4684
+ });
4685
+ archive.finalize();
4686
+ });
4687
+ }
4688
+
4457
4689
  // src/cli.ts
4458
4690
  try {
4459
4691
  const projectRoot = getProjectRoot();
@@ -4465,7 +4697,7 @@ try {
4465
4697
  } catch {
4466
4698
  }
4467
4699
  dotenv__default.default.config({
4468
- path: path8__default.default.join(os__default.default.homedir(), ".onexthm", ".env"),
4700
+ path: path8__default.default.join(os3__default.default.homedir(), ".onexthm", ".env"),
4469
4701
  quiet: true
4470
4702
  });
4471
4703
  var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href)));
@@ -4522,6 +4754,10 @@ program.command("clone").description("Clone theme source code from S3").argument
4522
4754
  "staging"
4523
4755
  ).option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
4524
4756
  program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
4757
+ program.command("login").description("Login to OneX platform").action(loginCommand);
4758
+ program.command("logout").description("Logout from OneX platform").action(logoutCommand);
4759
+ program.command("whoami").description("Show current logged-in developer").action(whoamiCommand);
4760
+ program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").action(publishCommand);
4525
4761
  program.configureOutput({
4526
4762
  writeErr: (str) => process.stderr.write(chalk4__default.default.red(str))
4527
4763
  });