@onexapis/cli 1.1.17 → 1.1.19

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 +621 -294
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cli.mjs +618 -291
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/index.js +73 -278
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +73 -278
  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 +34 -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
@@ -2810,6 +2587,79 @@ async function validateCommand(options) {
2810
2587
  }
2811
2588
  }
2812
2589
  }
2590
+ if (fs__default.default.existsSync(sectionsDir)) {
2591
+ const sections = fs__default.default.readdirSync(sectionsDir).filter(
2592
+ (name) => fs__default.default.statSync(path8__default.default.join(sectionsDir, name)).isDirectory()
2593
+ );
2594
+ for (const sectionName of sections) {
2595
+ const sectionPath = path8__default.default.join(sectionsDir, sectionName);
2596
+ const tsxFiles = fs__default.default.readdirSync(sectionPath).filter((f) => f.endsWith(".tsx") && !f.endsWith(".schema.ts"));
2597
+ for (const tsxFile of tsxFiles) {
2598
+ const filePath = path8__default.default.join(sectionPath, tsxFile);
2599
+ const content = fs__default.default.readFileSync(filePath, "utf-8");
2600
+ const relPath = `sections/${sectionName}/${tsxFile}`;
2601
+ if (!content.includes('"use client"') && !content.includes("'use client'")) {
2602
+ issues.push({
2603
+ type: "error",
2604
+ file: relPath,
2605
+ message: 'Missing "use client" directive at top of file'
2606
+ });
2607
+ }
2608
+ if (!content.includes("ComponentRenderer") && !content.includes("BlockRenderer")) {
2609
+ issues.push({
2610
+ type: "warning",
2611
+ file: relPath,
2612
+ message: "No ComponentRenderer or BlockRenderer found \u2014 sections should use core renderers for content"
2613
+ });
2614
+ }
2615
+ if (!content.includes("data-section-id")) {
2616
+ issues.push({
2617
+ type: "error",
2618
+ file: relPath,
2619
+ message: "Missing data-section-id attribute \u2014 editor cannot select this section"
2620
+ });
2621
+ }
2622
+ if (/\beval\s*\(/.test(content)) {
2623
+ issues.push({
2624
+ type: "error",
2625
+ file: relPath,
2626
+ message: "eval() detected \u2014 arbitrary code execution risk"
2627
+ });
2628
+ }
2629
+ if (content.includes("document.cookie")) {
2630
+ issues.push({
2631
+ type: "error",
2632
+ file: relPath,
2633
+ message: "document.cookie access \u2014 session hijacking risk"
2634
+ });
2635
+ }
2636
+ if (/\brequire\s*\(/.test(content)) {
2637
+ issues.push({
2638
+ type: "warning",
2639
+ file: relPath,
2640
+ message: "require() detected \u2014 themes should use ES module imports"
2641
+ });
2642
+ }
2643
+ }
2644
+ }
2645
+ }
2646
+ const registryPath = path8__default.default.join(themePath, "sections-registry.ts");
2647
+ const bundleEntryPath = path8__default.default.join(themePath, "bundle-entry.ts");
2648
+ const registryContent = fs__default.default.existsSync(registryPath) ? fs__default.default.readFileSync(registryPath, "utf-8") : fs__default.default.existsSync(bundleEntryPath) ? fs__default.default.readFileSync(bundleEntryPath, "utf-8") : "";
2649
+ if (fs__default.default.existsSync(sectionsDir) && registryContent) {
2650
+ const sections = fs__default.default.readdirSync(sectionsDir).filter(
2651
+ (name) => fs__default.default.statSync(path8__default.default.join(sectionsDir, name)).isDirectory()
2652
+ );
2653
+ for (const sectionName of sections) {
2654
+ if (!registryContent.includes(`sections/${sectionName}`) && !registryContent.includes(`"${sectionName}"`)) {
2655
+ issues.push({
2656
+ type: "warning",
2657
+ file: `sections/${sectionName}/`,
2658
+ message: "Section not found in sections-registry.ts or bundle-entry.ts \u2014 may not be included in build"
2659
+ });
2660
+ }
2661
+ }
2662
+ }
2813
2663
  logger.stopSpinner(true, "Validation complete");
2814
2664
  const errors = issues.filter((i) => i.type === "error");
2815
2665
  const warnings = issues.filter((i) => i.type === "warning");
@@ -2922,7 +2772,7 @@ async function buildCommand(options) {
2922
2772
  logger.stopSpinner(true, "Lint passed");
2923
2773
  const pkgJson = fs__default.default.readJsonSync(packageJsonPath);
2924
2774
  const buildScript = pkgJson.scripts?.build || "";
2925
- const isRecursive = buildScript.includes("onexthm build") || buildScript.includes("onex-cli build");
2775
+ const isRecursive = buildScript.includes("onexthm build") || buildScript.includes("onex build") || buildScript.includes("onex-cli build");
2926
2776
  logger.startSpinner(
2927
2777
  options.watch ? "Building (watch mode)..." : "Building..."
2928
2778
  );
@@ -2956,18 +2806,36 @@ function runCommand(command, args, cwd) {
2956
2806
  return new Promise((resolve) => {
2957
2807
  const proc = child_process.spawn(command, args, {
2958
2808
  cwd,
2959
- stdio: "pipe",
2809
+ stdio: ["pipe", "pipe", "pipe"],
2960
2810
  shell: true
2961
2811
  });
2962
- let hasError = false;
2812
+ let stdout = "";
2813
+ let stderr = "";
2814
+ proc.stdout.on("data", (data) => {
2815
+ stdout += data.toString();
2816
+ });
2963
2817
  proc.stderr.on("data", (data) => {
2964
- const message = data.toString();
2965
- if (message.includes("error") || message.includes("Error") || message.includes("ERROR")) {
2966
- hasError = true;
2967
- }
2818
+ stderr += data.toString();
2968
2819
  });
2969
2820
  proc.on("close", (code) => {
2970
- resolve(code === 0 && !hasError);
2821
+ if (code !== 0) {
2822
+ const output = (stderr + stdout).trim();
2823
+ if (output) {
2824
+ logger.newLine();
2825
+ for (const line of output.split("\n")) {
2826
+ const t = line.trim();
2827
+ if (!t) continue;
2828
+ if (t.includes("error") || t.includes("Error") || t.includes("TS")) {
2829
+ logger.log(` \u274C ${t}`);
2830
+ } else if (t.includes("warning") || t.includes("Warning")) {
2831
+ logger.log(` \u26A0\uFE0F ${t}`);
2832
+ } else {
2833
+ logger.log(` ${t}`);
2834
+ }
2835
+ }
2836
+ }
2837
+ }
2838
+ resolve(code === 0);
2971
2839
  });
2972
2840
  proc.on("error", () => {
2973
2841
  resolve(false);
@@ -3181,7 +3049,7 @@ async function deployCommand(options) {
3181
3049
  if (options.environment) {
3182
3050
  formData.append("environment", options.environment);
3183
3051
  }
3184
- const response = await fetch__default.default(uploadEndpoint, {
3052
+ const response = await fetch2__default.default(uploadEndpoint, {
3185
3053
  method: "POST",
3186
3054
  body: formData,
3187
3055
  headers: formData.getHeaders()
@@ -3262,7 +3130,7 @@ function getBucketName(env) {
3262
3130
  return process.env.BUCKET_NAME;
3263
3131
  }
3264
3132
  const environment = env || process.env.ENVIRONMENT || "staging";
3265
- return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
3133
+ return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3266
3134
  }
3267
3135
  async function findCompiledThemeDir(themeId, version2) {
3268
3136
  const searchPaths = [path8__default.default.resolve(process.cwd(), "dist")];
@@ -3378,7 +3246,7 @@ async function uploadCommand(options) {
3378
3246
  }
3379
3247
  spinner.succeed(`Found compiled theme at: ${compiledDir}`);
3380
3248
  spinner.start("Creating bundle.zip...");
3381
- const tmpDir = os__default.default.tmpdir();
3249
+ const tmpDir = os3__default.default.tmpdir();
3382
3250
  const bundleZipPath = path8__default.default.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
3383
3251
  await createZipFromDir(compiledDir, bundleZipPath);
3384
3252
  const bundleZipBuffer = await fs__default.default.readFile(bundleZipPath);
@@ -3531,7 +3399,7 @@ function getBucketName2(env) {
3531
3399
  return process.env.BUCKET_NAME;
3532
3400
  }
3533
3401
  const environment = env || process.env.ENVIRONMENT || "staging";
3534
- return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
3402
+ return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3535
3403
  }
3536
3404
  async function streamToString(stream) {
3537
3405
  const chunks = [];
@@ -3732,7 +3600,7 @@ function getBucketName3(env) {
3732
3600
  return process.env.BUCKET_NAME;
3733
3601
  }
3734
3602
  const environment = env || process.env.ENVIRONMENT || "staging";
3735
- return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
3603
+ return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3736
3604
  }
3737
3605
  async function streamToString2(stream) {
3738
3606
  const chunks = [];
@@ -3776,8 +3644,8 @@ function runInstall(cwd) {
3776
3644
  });
3777
3645
  }
3778
3646
  async function promptThemeName(originalName) {
3779
- const { default: inquirer6 } = await import('inquirer');
3780
- const { themeName } = await inquirer6.prompt([
3647
+ const { default: inquirer7 } = await import('inquirer');
3648
+ const { themeName } = await inquirer7.prompt([
3781
3649
  {
3782
3650
  type: "input",
3783
3651
  name: "themeName",
@@ -3930,7 +3798,7 @@ async function cloneCommand(themeName, options) {
3930
3798
  [
3931
3799
  "# API Configuration (enables real data in preview)",
3932
3800
  "# Get your Company ID from the OneX dashboard",
3933
- "NEXT_PUBLIC_API_URL=https://api-dev.onexeos.com",
3801
+ "NEXT_PUBLIC_API_URL=https://platform-dev.onexeos.com",
3934
3802
  "NEXT_PUBLIC_COMPANY_ID=",
3935
3803
  ""
3936
3804
  ].join("\n")
@@ -4278,7 +4146,7 @@ async function devCommand(options) {
4278
4146
  logger.info(`File changed: ${filePath}`);
4279
4147
  try {
4280
4148
  await context2.rebuild();
4281
- await generateManifest2(themeName, themePath, outputDir);
4149
+ await generateManifest(themeName, themePath, outputDir);
4282
4150
  server.broadcast({ type: "reload", timestamp: Date.now() });
4283
4151
  logger.success("Rebuilt successfully");
4284
4152
  } catch (error) {
@@ -4313,7 +4181,7 @@ async function devCommand(options) {
4313
4181
 
4314
4182
  // src/commands/config.ts
4315
4183
  init_logger();
4316
- var CONFIG_DIR = path8__default.default.join(os__default.default.homedir(), ".onexthm");
4184
+ var CONFIG_DIR = path8__default.default.join(os3__default.default.homedir(), ".onexthm");
4317
4185
  var CONFIG_FILE = path8__default.default.join(CONFIG_DIR, ".env");
4318
4186
  var CONFIG_ENTRIES = [
4319
4187
  {
@@ -4343,7 +4211,7 @@ var CONFIG_ENTRIES = [
4343
4211
  key: "NEXT_PUBLIC_API_URL",
4344
4212
  label: "API URL",
4345
4213
  required: false,
4346
- defaultValue: "https://api-dev.onexeos.com"
4214
+ defaultValue: "https://platform-dev.onexeos.com"
4347
4215
  },
4348
4216
  {
4349
4217
  key: "NEXT_PUBLIC_COMPANY_ID",
@@ -4454,6 +4322,461 @@ async function configCommand() {
4454
4322
  );
4455
4323
  }
4456
4324
 
4325
+ // src/commands/login.ts
4326
+ init_logger();
4327
+ var AUTH_DIR = path8__default.default.join(os3__default.default.homedir(), ".onexthm");
4328
+ var AUTH_FILE = path8__default.default.join(AUTH_DIR, "auth.json");
4329
+ function getApiUrl() {
4330
+ return process.env.NEXT_PUBLIC_API_URL || process.env.ONEXTHM_API_URL || "https://platform-dev.onexeos.com";
4331
+ }
4332
+ async function saveAuthTokens(tokens) {
4333
+ await fs__default.default.ensureDir(AUTH_DIR);
4334
+ const key = getMachineKey();
4335
+ const data = JSON.stringify(tokens);
4336
+ const encrypted = encrypt(data, key);
4337
+ await fs__default.default.writeFile(AUTH_FILE, encrypted, "utf-8");
4338
+ }
4339
+ function loadAuthTokens() {
4340
+ try {
4341
+ if (!fs__default.default.existsSync(AUTH_FILE)) return null;
4342
+ const encrypted = fs__default.default.readFileSync(AUTH_FILE, "utf-8");
4343
+ const key = getMachineKey();
4344
+ const data = decrypt(encrypted, key);
4345
+ return JSON.parse(data);
4346
+ } catch {
4347
+ return null;
4348
+ }
4349
+ }
4350
+ async function clearAuthTokens() {
4351
+ try {
4352
+ await fs__default.default.remove(AUTH_FILE);
4353
+ } catch {
4354
+ }
4355
+ }
4356
+ function isTokenExpired(tokens) {
4357
+ return Date.now() / 1e3 > tokens.expiresAt - 60;
4358
+ }
4359
+ async function getValidTokens() {
4360
+ const tokens = loadAuthTokens();
4361
+ if (!tokens) return null;
4362
+ if (!isTokenExpired(tokens)) return tokens;
4363
+ try {
4364
+ const apiUrl = getApiUrl();
4365
+ const response = await fetch(`${apiUrl}/auth/refresh`, {
4366
+ method: "POST",
4367
+ headers: { "Content-Type": "application/json" },
4368
+ body: JSON.stringify({ refresh_token: tokens.refreshToken })
4369
+ });
4370
+ if (!response.ok) {
4371
+ await clearAuthTokens();
4372
+ return null;
4373
+ }
4374
+ const data = await response.json();
4375
+ const body = data.statusCode ? data.body : data;
4376
+ const refreshed = {
4377
+ ...tokens,
4378
+ accessToken: body.AccessToken || tokens.accessToken,
4379
+ idToken: body.IdToken || tokens.idToken,
4380
+ expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
4381
+ };
4382
+ await saveAuthTokens(refreshed);
4383
+ return refreshed;
4384
+ } catch {
4385
+ await clearAuthTokens();
4386
+ return null;
4387
+ }
4388
+ }
4389
+ async function authenticatedFetch(url, init) {
4390
+ const tokens = await getValidTokens();
4391
+ if (!tokens) {
4392
+ throw new Error("Not logged in. Run: onexthm login");
4393
+ }
4394
+ const headers = new Headers(init?.headers);
4395
+ headers.set("Authorization", `Bearer ${tokens.idToken}`);
4396
+ headers.set("Content-Type", "application/json");
4397
+ return fetch(url, { ...init, headers });
4398
+ }
4399
+ function getMachineKey() {
4400
+ let seed;
4401
+ if (process.platform === "darwin") {
4402
+ seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
4403
+ } else if (process.platform === "linux") {
4404
+ try {
4405
+ seed = `onexthm:${fs__default.default.readFileSync("/etc/machine-id", "utf-8").trim()}`;
4406
+ } catch {
4407
+ seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
4408
+ }
4409
+ } else {
4410
+ seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
4411
+ }
4412
+ return crypto2__default.default.createHash("sha256").update(seed).digest();
4413
+ }
4414
+ function encrypt(text, key) {
4415
+ const iv = crypto2__default.default.randomBytes(16);
4416
+ const cipher = crypto2__default.default.createCipheriv("aes-256-gcm", key, iv);
4417
+ let encrypted = cipher.update(text, "utf-8", "hex");
4418
+ encrypted += cipher.final("hex");
4419
+ const tag = cipher.getAuthTag();
4420
+ return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
4421
+ }
4422
+ function decrypt(text, key) {
4423
+ const [ivHex, tagHex, encrypted] = text.split(":");
4424
+ const iv = Buffer.from(ivHex, "hex");
4425
+ const tag = Buffer.from(tagHex, "hex");
4426
+ const decipher = crypto2__default.default.createDecipheriv("aes-256-gcm", key, iv);
4427
+ decipher.setAuthTag(tag);
4428
+ let decrypted = decipher.update(encrypted, "hex", "utf-8");
4429
+ decrypted += decipher.final("utf-8");
4430
+ return decrypted;
4431
+ }
4432
+ function parseJwtClaims(idToken) {
4433
+ try {
4434
+ const payload = idToken.split(".")[1];
4435
+ const decoded = Buffer.from(payload, "base64url").toString("utf-8");
4436
+ return JSON.parse(decoded);
4437
+ } catch {
4438
+ return {};
4439
+ }
4440
+ }
4441
+
4442
+ // src/commands/login.ts
4443
+ async function loginCommand() {
4444
+ logger.header("OneX Theme Developer Login");
4445
+ const existing = loadAuthTokens();
4446
+ if (existing) {
4447
+ logger.info(`Already logged in as: ${existing.user.email}`);
4448
+ const { relogin } = await inquirer__default.default.prompt([
4449
+ {
4450
+ type: "confirm",
4451
+ name: "relogin",
4452
+ message: "Re-login with different account?",
4453
+ default: false
4454
+ }
4455
+ ]);
4456
+ if (!relogin) return;
4457
+ }
4458
+ const { email, password } = await inquirer__default.default.prompt([
4459
+ {
4460
+ type: "input",
4461
+ name: "email",
4462
+ message: "Email:",
4463
+ validate: (input) => input.includes("@") ? true : "Enter a valid email"
4464
+ },
4465
+ {
4466
+ type: "password",
4467
+ name: "password",
4468
+ message: "Password:",
4469
+ validate: (input) => input.length >= 6 ? true : "Password too short"
4470
+ }
4471
+ ]);
4472
+ logger.startSpinner("Logging in...");
4473
+ try {
4474
+ const apiUrl = getApiUrl();
4475
+ const response = await fetch(`${apiUrl}/auth/login`, {
4476
+ method: "POST",
4477
+ headers: { "Content-Type": "application/json" },
4478
+ body: JSON.stringify({ username: email, password })
4479
+ });
4480
+ const raw = await response.json();
4481
+ const data = raw.statusCode ? raw.body : raw;
4482
+ if (!response.ok || data.error) {
4483
+ logger.stopSpinner(false, "Login failed");
4484
+ logger.error(data.message || data.error || "Invalid credentials");
4485
+ process.exit(1);
4486
+ }
4487
+ const idToken = data.IdToken;
4488
+ const accessToken = data.AccessToken;
4489
+ const refreshToken = data.RefreshToken;
4490
+ const expiresIn = data.ExpiresIn || 3600;
4491
+ if (!idToken) {
4492
+ logger.stopSpinner(false, "Login failed");
4493
+ logger.error("No token received from server");
4494
+ process.exit(1);
4495
+ }
4496
+ const claims = parseJwtClaims(idToken);
4497
+ const tokens = {
4498
+ accessToken,
4499
+ idToken,
4500
+ refreshToken,
4501
+ expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
4502
+ user: {
4503
+ email: claims.email || email,
4504
+ name: claims.name,
4505
+ companyId: claims["custom:company_id"],
4506
+ userId: claims.sub
4507
+ }
4508
+ };
4509
+ await saveAuthTokens(tokens);
4510
+ logger.stopSpinner(true, "Logged in!");
4511
+ logger.newLine();
4512
+ logger.info(` Email: ${tokens.user.email}`);
4513
+ if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
4514
+ if (tokens.user.companyId)
4515
+ logger.info(` Company: ${tokens.user.companyId}`);
4516
+ logger.newLine();
4517
+ logger.success("Token stored securely in ~/.onexthm/auth.json (encrypted)");
4518
+ } catch (error) {
4519
+ logger.stopSpinner(false, "Login failed");
4520
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4521
+ process.exit(1);
4522
+ }
4523
+ }
4524
+
4525
+ // src/commands/logout.ts
4526
+ init_logger();
4527
+ async function logoutCommand() {
4528
+ const tokens = loadAuthTokens();
4529
+ if (!tokens) {
4530
+ logger.info("Not logged in.");
4531
+ return;
4532
+ }
4533
+ await clearAuthTokens();
4534
+ logger.success(`Logged out (was: ${tokens.user.email})`);
4535
+ }
4536
+
4537
+ // src/commands/whoami.ts
4538
+ init_logger();
4539
+ async function whoamiCommand() {
4540
+ const tokens = loadAuthTokens();
4541
+ if (!tokens) {
4542
+ logger.error("Not logged in. Run: onexthm login");
4543
+ process.exit(1);
4544
+ }
4545
+ const expired = isTokenExpired(tokens);
4546
+ logger.header("OneX Theme Developer");
4547
+ logger.info(` Email: ${tokens.user.email}`);
4548
+ if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
4549
+ if (tokens.user.companyId)
4550
+ logger.info(` Company: ${tokens.user.companyId}`);
4551
+ logger.info(
4552
+ ` Status: ${expired ? "\u26A0 Token expired (will auto-refresh)" : "\u2713 Active"}`
4553
+ );
4554
+ }
4555
+
4556
+ // src/commands/publish.ts
4557
+ init_logger();
4558
+ async function publishCommand(options) {
4559
+ logger.header("OneX Theme Publish");
4560
+ const tokens = await getValidTokens();
4561
+ if (!tokens) {
4562
+ logger.error("Not logged in. Run: onexthm login");
4563
+ process.exit(1);
4564
+ }
4565
+ logger.info(`Logged in as: ${tokens.user.email}`);
4566
+ let themePath;
4567
+ if (options.theme) {
4568
+ themePath = path8__default.default.resolve(options.theme);
4569
+ } else {
4570
+ const isThemeDir = [
4571
+ "theme.config.ts",
4572
+ "bundle-entry.ts",
4573
+ "manifest.ts"
4574
+ ].some((f) => fs__default.default.existsSync(path8__default.default.join(process.cwd(), f)));
4575
+ if (isThemeDir) {
4576
+ themePath = process.cwd();
4577
+ } else {
4578
+ logger.error(
4579
+ "Not in a theme directory. Run from theme root or use --theme flag."
4580
+ );
4581
+ process.exit(1);
4582
+ }
4583
+ }
4584
+ const pkgPath = path8__default.default.join(themePath, "package.json");
4585
+ if (!fs__default.default.existsSync(pkgPath)) {
4586
+ logger.error("No package.json found in theme directory");
4587
+ process.exit(1);
4588
+ }
4589
+ const pkg = fs__default.default.readJsonSync(pkgPath);
4590
+ const themeId = pkg.name?.replace("@onex-themes/", "") || path8__default.default.basename(themePath);
4591
+ const version2 = pkg.version || "1.0.0";
4592
+ logger.newLine();
4593
+ logger.info(`Theme: ${themeId}`);
4594
+ logger.info(`Version: ${version2}`);
4595
+ logger.newLine();
4596
+ const apiUrl = getApiUrl();
4597
+ logger.startSpinner("Registering theme...");
4598
+ try {
4599
+ const regResponse = await authenticatedFetch(
4600
+ `${apiUrl}/website-api/themes/register`,
4601
+ {
4602
+ method: "POST",
4603
+ body: JSON.stringify({
4604
+ themeId,
4605
+ name: pkg.displayName || themeId,
4606
+ description: pkg.description || "",
4607
+ email: tokens.user.email
4608
+ })
4609
+ }
4610
+ );
4611
+ const regData = await regResponse.json();
4612
+ const regBody = regData.statusCode ? regData.body : regData;
4613
+ if (!regResponse.ok && regBody.error && !regBody.error.includes("already registered")) {
4614
+ logger.stopSpinner(false, "Registration failed");
4615
+ logger.error(regBody.error);
4616
+ process.exit(1);
4617
+ }
4618
+ logger.stopSpinner(true, regBody.message || "Theme registered");
4619
+ } catch (error) {
4620
+ logger.stopSpinner(false, "Registration failed");
4621
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4622
+ process.exit(1);
4623
+ }
4624
+ logger.startSpinner("Building theme...");
4625
+ try {
4626
+ const { execSync: execSync3 } = await import('child_process');
4627
+ execSync3(
4628
+ "npx tsup bundle-entry.ts --format esm --target es2020 --outDir dist",
4629
+ {
4630
+ cwd: themePath,
4631
+ stdio: "ignore"
4632
+ }
4633
+ );
4634
+ logger.stopSpinner(true, "Theme compiled");
4635
+ } catch {
4636
+ logger.stopSpinner(false, "Build failed");
4637
+ logger.error("Run 'onexthm build' to see build errors");
4638
+ process.exit(1);
4639
+ }
4640
+ logger.startSpinner("Getting upload URL...");
4641
+ let bundleUploadUrl;
4642
+ let sourceUploadUrl;
4643
+ try {
4644
+ const pubResponse = await authenticatedFetch(
4645
+ `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`,
4646
+ {
4647
+ method: "POST",
4648
+ body: JSON.stringify({ version: version2 })
4649
+ }
4650
+ );
4651
+ const pubData = await pubResponse.json();
4652
+ const pubBody = pubData.statusCode ? pubData.body : pubData;
4653
+ if (!pubResponse.ok || !pubBody.bundleUploadUrl) {
4654
+ logger.stopSpinner(false, "Failed to get upload URL");
4655
+ logger.error(pubBody.error || "Server error");
4656
+ process.exit(1);
4657
+ }
4658
+ bundleUploadUrl = pubBody.bundleUploadUrl;
4659
+ sourceUploadUrl = pubBody.sourceUploadUrl;
4660
+ logger.stopSpinner(true, "Upload URL obtained");
4661
+ } catch (error) {
4662
+ logger.stopSpinner(false, "Failed");
4663
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4664
+ process.exit(1);
4665
+ }
4666
+ logger.startSpinner("Uploading bundle...");
4667
+ try {
4668
+ const archiver3 = await import('archiver');
4669
+ const { createWriteStream } = await import('fs');
4670
+ const distDir = path8__default.default.join(themePath, "dist");
4671
+ if (!fs__default.default.existsSync(distDir)) {
4672
+ logger.stopSpinner(false, "No dist/ directory");
4673
+ logger.error("Build the theme first: onexthm build");
4674
+ process.exit(1);
4675
+ }
4676
+ const bundleZipPath = path8__default.default.join(themePath, "dist", "bundle.zip");
4677
+ await createZip(distDir, bundleZipPath, ["bundle.zip"]);
4678
+ const bundleBuffer = fs__default.default.readFileSync(bundleZipPath);
4679
+ const bundleRes = await fetch(bundleUploadUrl, {
4680
+ method: "PUT",
4681
+ headers: { "Content-Type": "application/zip" },
4682
+ body: bundleBuffer
4683
+ });
4684
+ if (!bundleRes.ok) {
4685
+ throw new Error(`Upload failed: ${bundleRes.status}`);
4686
+ }
4687
+ const sizeMB = (bundleBuffer.length / 1024 / 1024).toFixed(2);
4688
+ logger.stopSpinner(true, `Bundle uploaded (${sizeMB} MB)`);
4689
+ } catch (error) {
4690
+ logger.stopSpinner(false, "Upload failed");
4691
+ logger.error(error instanceof Error ? error.message : "Upload error");
4692
+ process.exit(1);
4693
+ }
4694
+ logger.startSpinner("Uploading source...");
4695
+ try {
4696
+ const sourceZipPath = path8__default.default.join(themePath, "dist", "source.zip");
4697
+ await createZip(themePath, sourceZipPath, [
4698
+ "node_modules",
4699
+ "dist",
4700
+ ".git",
4701
+ ".env",
4702
+ ".env.local"
4703
+ ]);
4704
+ const sourceBuffer = fs__default.default.readFileSync(sourceZipPath);
4705
+ const sourceRes = await fetch(sourceUploadUrl, {
4706
+ method: "PUT",
4707
+ headers: { "Content-Type": "application/zip" },
4708
+ body: sourceBuffer
4709
+ });
4710
+ if (!sourceRes.ok) {
4711
+ throw new Error(`Source upload failed: ${sourceRes.status}`);
4712
+ }
4713
+ const sizeMB = (sourceBuffer.length / 1024 / 1024).toFixed(2);
4714
+ logger.stopSpinner(true, `Source uploaded (${sizeMB} MB)`);
4715
+ } catch (error) {
4716
+ logger.stopSpinner(false, "Source upload failed");
4717
+ logger.info("Source upload skipped (bundle was uploaded successfully)");
4718
+ }
4719
+ logger.startSpinner("Scanning and publishing...");
4720
+ try {
4721
+ const confirmResponse = await authenticatedFetch(
4722
+ `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/confirm`,
4723
+ { method: "POST" }
4724
+ );
4725
+ const confirmData = await confirmResponse.json();
4726
+ const confirmBody = confirmData.statusCode ? confirmData.body : confirmData;
4727
+ if (!confirmResponse.ok || !confirmBody.success) {
4728
+ logger.stopSpinner(false, "Publishing failed");
4729
+ if (confirmBody.violations) {
4730
+ logger.error("Theme rejected \u2014 security violations found:");
4731
+ for (const v of confirmBody.violations) {
4732
+ logger.log(` \u274C ${v.file}: ${v.violation}`);
4733
+ }
4734
+ } else {
4735
+ logger.error(confirmBody.error || "Unknown error");
4736
+ }
4737
+ if (confirmBody.warnings?.length) {
4738
+ logger.newLine();
4739
+ logger.info("Warnings:");
4740
+ for (const w of confirmBody.warnings) {
4741
+ logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
4742
+ }
4743
+ }
4744
+ process.exit(1);
4745
+ }
4746
+ logger.stopSpinner(true, confirmBody.message || "Published!");
4747
+ if (confirmBody.warnings?.length) {
4748
+ logger.newLine();
4749
+ logger.info("Warnings (non-blocking):");
4750
+ for (const w of confirmBody.warnings) {
4751
+ logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
4752
+ }
4753
+ }
4754
+ } catch (error) {
4755
+ logger.stopSpinner(false, "Publishing failed");
4756
+ logger.error(error instanceof Error ? error.message : "Connection failed");
4757
+ process.exit(1);
4758
+ }
4759
+ logger.newLine();
4760
+ logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
4761
+ }
4762
+ async function createZip(sourceDir, outputPath, exclude) {
4763
+ const archiver3 = (await import('archiver')).default;
4764
+ const { createWriteStream } = await import('fs');
4765
+ return new Promise((resolve, reject) => {
4766
+ const output = createWriteStream(outputPath);
4767
+ const archive = archiver3("zip", { zlib: { level: 9 } });
4768
+ output.on("close", resolve);
4769
+ archive.on("error", reject);
4770
+ archive.pipe(output);
4771
+ archive.glob("**/*", {
4772
+ cwd: sourceDir,
4773
+ ignore: exclude.map((e) => `${e}/**`),
4774
+ dot: false
4775
+ });
4776
+ archive.finalize();
4777
+ });
4778
+ }
4779
+
4457
4780
  // src/cli.ts
4458
4781
  try {
4459
4782
  const projectRoot = getProjectRoot();
@@ -4465,7 +4788,7 @@ try {
4465
4788
  } catch {
4466
4789
  }
4467
4790
  dotenv__default.default.config({
4468
- path: path8__default.default.join(os__default.default.homedir(), ".onexthm", ".env"),
4791
+ path: path8__default.default.join(os3__default.default.homedir(), ".onexthm", ".env"),
4469
4792
  quiet: true
4470
4793
  });
4471
4794
  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 +4845,10 @@ program.command("clone").description("Clone theme source code from S3").argument
4522
4845
  "staging"
4523
4846
  ).option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
4524
4847
  program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
4848
+ program.command("login").description("Login to OneX platform").action(loginCommand);
4849
+ program.command("logout").description("Logout from OneX platform").action(logoutCommand);
4850
+ program.command("whoami").description("Show current logged-in developer").action(whoamiCommand);
4851
+ program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").action(publishCommand);
4525
4852
  program.configureOutput({
4526
4853
  writeErr: (str) => process.stderr.write(chalk4__default.default.red(str))
4527
4854
  });