@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.
- package/README.md +82 -16
- package/dist/cli.js +621 -294
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +618 -291
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +73 -278
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -278
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/default/.env.example +1 -1
- package/templates/default/.mcp.json +8 -0
- package/templates/default/CLAUDE.md +941 -0
- package/templates/default/bundle-entry.ts +18 -0
- package/templates/default/index.ts +26 -0
- package/templates/default/package.json +34 -0
- package/templates/default/pages/about.ts +66 -0
- package/templates/default/pages/home.ts +93 -0
- package/templates/default/pages/showcase.ts +146 -0
- package/templates/default/sections/about/about-default.tsx +237 -0
- package/templates/default/sections/about/about.schema.ts +259 -0
- package/templates/default/sections/about/index.ts +15 -0
- package/templates/default/sections/cta/cta-default.tsx +180 -0
- package/templates/default/sections/cta/cta.schema.ts +210 -0
- package/templates/default/sections/cta/index.ts +11 -0
- package/templates/default/sections/features/features-default.tsx +154 -0
- package/templates/default/sections/features/features.schema.ts +330 -0
- package/templates/default/sections/features/index.ts +11 -0
- package/templates/default/sections/gallery/gallery-default.tsx +134 -0
- package/templates/default/sections/gallery/gallery.schema.ts +397 -0
- package/templates/default/sections/gallery/index.ts +11 -0
- package/templates/default/sections/hero/hero-default.tsx +212 -0
- package/templates/default/sections/hero/hero.schema.ts +273 -0
- package/templates/default/sections/hero/index.ts +15 -0
- package/templates/default/sections/stats/index.ts +11 -0
- package/templates/default/sections/stats/stats-default.tsx +103 -0
- package/templates/default/sections/stats/stats.schema.ts +266 -0
- package/templates/default/sections/testimonials/index.ts +11 -0
- package/templates/default/sections/testimonials/testimonials-default.tsx +130 -0
- package/templates/default/sections/testimonials/testimonials.schema.ts +371 -0
- package/templates/default/sections-registry.ts +32 -0
- package/templates/default/theme.config.ts +107 -0
- package/templates/default/theme.layout.ts +21 -0
- package/templates/default/tsconfig.json +16 -7
- package/templates/default/README.md.ejs +0 -129
- package/templates/default/esbuild.config.js +0 -81
- package/templates/default/package.json.ejs +0 -31
- package/templates/default/src/config.ts.ejs +0 -98
- package/templates/default/src/index.ts.ejs +0 -11
- package/templates/default/src/layout.ts +0 -23
- package/templates/default/src/manifest.ts.ejs +0 -47
- package/templates/default/src/pages/home.ts.ejs +0 -37
- package/templates/default/src/sections/footer/footer-default.tsx +0 -28
- package/templates/default/src/sections/footer/footer.schema.ts +0 -45
- package/templates/default/src/sections/footer/index.ts +0 -2
- package/templates/default/src/sections/header/header-default.tsx +0 -61
- package/templates/default/src/sections/header/header.schema.ts +0 -46
- package/templates/default/src/sections/header/index.ts +0 -2
- package/templates/default/src/sections/hero/hero-default.tsx +0 -52
- package/templates/default/src/sections/hero/hero.schema.ts +0 -52
- 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
|
|
9
|
+
var crypto2 = require('crypto');
|
|
10
10
|
var glob = require('glob');
|
|
11
11
|
var module$1 = require('module');
|
|
12
|
-
var
|
|
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
|
|
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
|
|
56
|
-
var
|
|
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
|
|
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: () =>
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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("
|
|
1587
|
+
logger.log(" bundle-entry.ts - Theme manifest and exports");
|
|
1613
1588
|
logger.log(
|
|
1614
|
-
"
|
|
1589
|
+
" theme.config.ts - Design tokens (colors, typography, etc.)"
|
|
1615
1590
|
);
|
|
1616
|
-
logger.log("
|
|
1617
|
-
logger.log("
|
|
1618
|
-
logger.log("
|
|
1619
|
-
logger.log("
|
|
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
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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" ? "
|
|
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 =
|
|
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" ? "
|
|
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" ? "
|
|
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:
|
|
3780
|
-
const { themeName } = await
|
|
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://
|
|
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
|
|
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(
|
|
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://
|
|
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(
|
|
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
|
});
|