@onexapis/cli 1.1.17 → 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -16
- package/dist/cli.js +522 -286
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +519 -283
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +47 -270
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +47 -270
- 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 +37 -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.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import ora from 'ora';
|
|
|
4
4
|
import * as esbuild from 'esbuild';
|
|
5
5
|
import path8 from 'path';
|
|
6
6
|
import fs7 from 'fs/promises';
|
|
7
|
-
import
|
|
7
|
+
import crypto2 from 'crypto';
|
|
8
8
|
import { glob } from 'glob';
|
|
9
9
|
import { createRequire } from 'module';
|
|
10
|
-
import
|
|
10
|
+
import os3 from 'os';
|
|
11
11
|
import dotenv from 'dotenv';
|
|
12
12
|
import fs from 'fs-extra';
|
|
13
13
|
import ejs from 'ejs';
|
|
@@ -17,7 +17,7 @@ import fs2 from 'fs';
|
|
|
17
17
|
import inquirer from 'inquirer';
|
|
18
18
|
import archiver from 'archiver';
|
|
19
19
|
import FormData from 'form-data';
|
|
20
|
-
import
|
|
20
|
+
import fetch2 from 'node-fetch';
|
|
21
21
|
import { PutObjectCommand, GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
22
22
|
import AdmZip from 'adm-zip';
|
|
23
23
|
import chokidar from 'chokidar';
|
|
@@ -96,7 +96,7 @@ __export(compile_theme_exports, {
|
|
|
96
96
|
compilePreviewRuntime: () => compilePreviewRuntime,
|
|
97
97
|
compileStandaloneTheme: () => compileStandaloneTheme,
|
|
98
98
|
compileStandaloneThemeDev: () => compileStandaloneThemeDev,
|
|
99
|
-
generateManifest: () =>
|
|
99
|
+
generateManifest: () => generateManifest
|
|
100
100
|
});
|
|
101
101
|
async function resolveNodeModulesFile(startDir, relativePath) {
|
|
102
102
|
let dir = startDir;
|
|
@@ -484,7 +484,7 @@ async function contentHashEntry(outputDir) {
|
|
|
484
484
|
logger.warning("No entry file found in output, skipping content hash");
|
|
485
485
|
return;
|
|
486
486
|
}
|
|
487
|
-
const hash2 =
|
|
487
|
+
const hash2 = crypto2.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
488
488
|
const hashedName2 = `bundle-entry-${hash2}.js`;
|
|
489
489
|
const indexMapPath = path8.join(outputDir, "index.js.map");
|
|
490
490
|
const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
|
|
@@ -502,7 +502,7 @@ async function contentHashEntry(outputDir) {
|
|
|
502
502
|
logger.info(`Entry hashed: ${hashedName2}`);
|
|
503
503
|
return;
|
|
504
504
|
}
|
|
505
|
-
const hash =
|
|
505
|
+
const hash = crypto2.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
506
506
|
const hashedName = `bundle-entry-${hash}.js`;
|
|
507
507
|
const hashedMapName = `bundle-entry-${hash}.js.map`;
|
|
508
508
|
entryContent = entryContent.replace(
|
|
@@ -540,7 +540,7 @@ async function extractDataRequirements(themePath) {
|
|
|
540
540
|
}
|
|
541
541
|
return requirements;
|
|
542
542
|
}
|
|
543
|
-
async function
|
|
543
|
+
async function generateManifest(themeName, themePath, outputDir) {
|
|
544
544
|
let version2 = "1.0.0";
|
|
545
545
|
let themeId = themeName;
|
|
546
546
|
try {
|
|
@@ -675,7 +675,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
675
675
|
} catch {
|
|
676
676
|
}
|
|
677
677
|
await contentHashEntry(outputDir);
|
|
678
|
-
await
|
|
678
|
+
await generateManifest(themeName, themePath, outputDir);
|
|
679
679
|
await generateThemeData(themePath, outputDir, themeName);
|
|
680
680
|
if (result.metafile) {
|
|
681
681
|
const outputs = result.metafile.outputs;
|
|
@@ -756,7 +756,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
756
756
|
};
|
|
757
757
|
const context2 = await esbuild.context(buildOptions);
|
|
758
758
|
await context2.rebuild();
|
|
759
|
-
await
|
|
759
|
+
await generateManifest(themeName, themePath, outputDir);
|
|
760
760
|
await generateThemeData(themePath, outputDir, themeName);
|
|
761
761
|
return { context: context2, outputDir };
|
|
762
762
|
}
|
|
@@ -1492,38 +1492,13 @@ async function initCommand(projectName, options = {}) {
|
|
|
1492
1492
|
try {
|
|
1493
1493
|
fs2.mkdirSync(projectPath, { recursive: true });
|
|
1494
1494
|
await copyTemplate(template, projectPath, data);
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
const layoutContent = generateThemeLayout(data);
|
|
1502
|
-
await writeFile(path8.join(srcPath, "layout.ts"), layoutContent);
|
|
1503
|
-
const indexContent = generateThemeIndex(data);
|
|
1504
|
-
await writeFile(path8.join(srcPath, "index.ts"), indexContent);
|
|
1505
|
-
const sectionsPath = path8.join(srcPath, "sections");
|
|
1506
|
-
fs2.mkdirSync(sectionsPath, { recursive: true });
|
|
1507
|
-
await writeFile(
|
|
1508
|
-
path8.join(sectionsPath, "README.md"),
|
|
1509
|
-
`# ${displayName} Sections
|
|
1510
|
-
|
|
1511
|
-
Add your theme-specific sections here.
|
|
1512
|
-
`
|
|
1513
|
-
);
|
|
1514
|
-
const blocksPath = path8.join(srcPath, "blocks");
|
|
1515
|
-
fs2.mkdirSync(blocksPath, { recursive: true });
|
|
1516
|
-
await writeFile(
|
|
1517
|
-
path8.join(blocksPath, "README.md"),
|
|
1518
|
-
`# ${displayName} Blocks
|
|
1519
|
-
|
|
1520
|
-
Add your theme-specific blocks here.
|
|
1521
|
-
`
|
|
1495
|
+
await renameThemeInFiles(
|
|
1496
|
+
projectPath,
|
|
1497
|
+
name,
|
|
1498
|
+
displayName,
|
|
1499
|
+
description,
|
|
1500
|
+
author
|
|
1522
1501
|
);
|
|
1523
|
-
const pagesPath = path8.join(srcPath, "pages");
|
|
1524
|
-
fs2.mkdirSync(pagesPath, { recursive: true });
|
|
1525
|
-
const homePageContent = generateHomePage(data);
|
|
1526
|
-
await writeFile(path8.join(pagesPath, "home.ts"), homePageContent);
|
|
1527
1502
|
logger.stopSpinner(true, "Project structure created!");
|
|
1528
1503
|
if (options.git) {
|
|
1529
1504
|
logger.startSpinner("Initializing git repository...");
|
|
@@ -1567,14 +1542,14 @@ Add your theme-specific blocks here.
|
|
|
1567
1542
|
logger.log(` npm run dev # Start development mode`);
|
|
1568
1543
|
logger.newLine();
|
|
1569
1544
|
logger.section("Theme structure:");
|
|
1570
|
-
logger.log("
|
|
1545
|
+
logger.log(" bundle-entry.ts - Theme manifest and exports");
|
|
1571
1546
|
logger.log(
|
|
1572
|
-
"
|
|
1547
|
+
" theme.config.ts - Design tokens (colors, typography, etc.)"
|
|
1573
1548
|
);
|
|
1574
|
-
logger.log("
|
|
1575
|
-
logger.log("
|
|
1576
|
-
logger.log("
|
|
1577
|
-
logger.log("
|
|
1549
|
+
logger.log(" theme.layout.ts - Header and footer configuration");
|
|
1550
|
+
logger.log(" sections/ - Custom sections for your theme");
|
|
1551
|
+
logger.log(" pages/ - Page configurations");
|
|
1552
|
+
logger.log(" CLAUDE.md - AI assistant context");
|
|
1578
1553
|
logger.newLine();
|
|
1579
1554
|
logger.success(`Happy theming! \u{1F3A8}`);
|
|
1580
1555
|
} catch (error) {
|
|
@@ -1588,231 +1563,33 @@ Add your theme-specific blocks here.
|
|
|
1588
1563
|
process.exit(1);
|
|
1589
1564
|
}
|
|
1590
1565
|
}
|
|
1591
|
-
function
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
// Example: productCard: () => import("./blocks/product-card").then((m) => m.productCardDefinition),
|
|
1619
|
-
},
|
|
1620
|
-
|
|
1621
|
-
// Default pages
|
|
1622
|
-
pages: {
|
|
1623
|
-
home: () => import("./pages/home").then((m) => m.homePageConfig),
|
|
1624
|
-
},
|
|
1625
|
-
|
|
1626
|
-
// Supported page types
|
|
1627
|
-
supportedPageTypes: ["home", "about", "contact", "custom"],
|
|
1628
|
-
|
|
1629
|
-
// Preview image (optional)
|
|
1630
|
-
preview: undefined,
|
|
1631
|
-
|
|
1632
|
-
// Tags for categorization (optional)
|
|
1633
|
-
tags: ["custom"],
|
|
1634
|
-
};
|
|
1635
|
-
|
|
1636
|
-
export default manifest;
|
|
1637
|
-
`;
|
|
1638
|
-
}
|
|
1639
|
-
function generateThemeConfig(data) {
|
|
1640
|
-
return `import type { ThemeConfig } from "@onexapis/core";
|
|
1641
|
-
|
|
1642
|
-
/**
|
|
1643
|
-
* ${data.displayName} Theme Configuration
|
|
1644
|
-
* Design tokens: colors, typography, spacing, etc.
|
|
1645
|
-
*/
|
|
1646
|
-
export const themeConfig: ThemeConfig = {
|
|
1647
|
-
// Color palette
|
|
1648
|
-
colors: {
|
|
1649
|
-
primary: {
|
|
1650
|
-
50: "#eff6ff",
|
|
1651
|
-
100: "#dbeafe",
|
|
1652
|
-
200: "#bfdbfe",
|
|
1653
|
-
300: "#93c5fd",
|
|
1654
|
-
400: "#60a5fa",
|
|
1655
|
-
500: "#3b82f6",
|
|
1656
|
-
600: "#2563eb",
|
|
1657
|
-
700: "#1d4ed8",
|
|
1658
|
-
800: "#1e40af",
|
|
1659
|
-
900: "#1e3a8a",
|
|
1660
|
-
},
|
|
1661
|
-
secondary: {
|
|
1662
|
-
50: "#f8fafc",
|
|
1663
|
-
100: "#f1f5f9",
|
|
1664
|
-
200: "#e2e8f0",
|
|
1665
|
-
300: "#cbd5e1",
|
|
1666
|
-
400: "#94a3b8",
|
|
1667
|
-
500: "#64748b",
|
|
1668
|
-
600: "#475569",
|
|
1669
|
-
700: "#334155",
|
|
1670
|
-
800: "#1e293b",
|
|
1671
|
-
900: "#0f172a",
|
|
1672
|
-
},
|
|
1673
|
-
accent: {
|
|
1674
|
-
50: "#fdf4ff",
|
|
1675
|
-
100: "#fae8ff",
|
|
1676
|
-
200: "#f5d0fe",
|
|
1677
|
-
300: "#f0abfc",
|
|
1678
|
-
400: "#e879f9",
|
|
1679
|
-
500: "#d946ef",
|
|
1680
|
-
600: "#c026d3",
|
|
1681
|
-
700: "#a21caf",
|
|
1682
|
-
800: "#86198f",
|
|
1683
|
-
900: "#701a75",
|
|
1684
|
-
},
|
|
1685
|
-
},
|
|
1686
|
-
|
|
1687
|
-
// Typography
|
|
1688
|
-
typography: {
|
|
1689
|
-
fontFamily: {
|
|
1690
|
-
sans: ["Inter", "system-ui", "sans-serif"],
|
|
1691
|
-
serif: ["Georgia", "serif"],
|
|
1692
|
-
mono: ["Monaco", "monospace"],
|
|
1693
|
-
},
|
|
1694
|
-
fontSize: {
|
|
1695
|
-
xs: "0.75rem",
|
|
1696
|
-
sm: "0.875rem",
|
|
1697
|
-
base: "1rem",
|
|
1698
|
-
lg: "1.125rem",
|
|
1699
|
-
xl: "1.25rem",
|
|
1700
|
-
"2xl": "1.5rem",
|
|
1701
|
-
"3xl": "1.875rem",
|
|
1702
|
-
"4xl": "2.25rem",
|
|
1703
|
-
"5xl": "3rem",
|
|
1704
|
-
},
|
|
1705
|
-
},
|
|
1706
|
-
|
|
1707
|
-
// Spacing
|
|
1708
|
-
spacing: {
|
|
1709
|
-
xs: "0.5rem",
|
|
1710
|
-
sm: "1rem",
|
|
1711
|
-
md: "1.5rem",
|
|
1712
|
-
lg: "2rem",
|
|
1713
|
-
xl: "3rem",
|
|
1714
|
-
"2xl": "4rem",
|
|
1715
|
-
"3xl": "6rem",
|
|
1716
|
-
"4xl": "8rem",
|
|
1717
|
-
},
|
|
1718
|
-
|
|
1719
|
-
// Border radius
|
|
1720
|
-
borderRadius: {
|
|
1721
|
-
none: "0",
|
|
1722
|
-
sm: "0.125rem",
|
|
1723
|
-
md: "0.375rem",
|
|
1724
|
-
lg: "0.5rem",
|
|
1725
|
-
xl: "0.75rem",
|
|
1726
|
-
full: "9999px",
|
|
1727
|
-
},
|
|
1728
|
-
|
|
1729
|
-
// Breakpoints
|
|
1730
|
-
breakpoints: {
|
|
1731
|
-
sm: "640px",
|
|
1732
|
-
md: "768px",
|
|
1733
|
-
lg: "1024px",
|
|
1734
|
-
xl: "1280px",
|
|
1735
|
-
"2xl": "1536px",
|
|
1736
|
-
},
|
|
1737
|
-
};
|
|
1738
|
-
`;
|
|
1739
|
-
}
|
|
1740
|
-
function generateThemeLayout(data) {
|
|
1741
|
-
return `import type { ThemeLayoutConfig } from "@onexapis/core";
|
|
1742
|
-
|
|
1743
|
-
/**
|
|
1744
|
-
* ${data.themeName} Theme Layout
|
|
1745
|
-
* Define header and footer sections
|
|
1746
|
-
*/
|
|
1747
|
-
export const themeLayout: ThemeLayoutConfig = {
|
|
1748
|
-
// Header section configuration
|
|
1749
|
-
header: undefined,
|
|
1750
|
-
// Example:
|
|
1751
|
-
// header: {
|
|
1752
|
-
// type: "header",
|
|
1753
|
-
// template: "default",
|
|
1754
|
-
// enabled: true,
|
|
1755
|
-
// settings: {},
|
|
1756
|
-
// },
|
|
1757
|
-
|
|
1758
|
-
// Footer section configuration
|
|
1759
|
-
footer: undefined,
|
|
1760
|
-
// Example:
|
|
1761
|
-
// footer: {
|
|
1762
|
-
// type: "footer",
|
|
1763
|
-
// template: "default",
|
|
1764
|
-
// enabled: true,
|
|
1765
|
-
// settings: {},
|
|
1766
|
-
// },
|
|
1767
|
-
};
|
|
1768
|
-
`;
|
|
1769
|
-
}
|
|
1770
|
-
function generateThemeIndex(data) {
|
|
1771
|
-
return `/**
|
|
1772
|
-
* ${data.themeNamePascal} Theme
|
|
1773
|
-
*/
|
|
1774
|
-
|
|
1775
|
-
export { manifest as ${data.themeNamePascal}Manifest } from "./manifest";
|
|
1776
|
-
export { themeConfig as ${data.themeNamePascal}Config } from "./config";
|
|
1777
|
-
export { themeLayout as ${data.themeNamePascal}Layout } from "./layout";
|
|
1778
|
-
`;
|
|
1779
|
-
}
|
|
1780
|
-
function generateHomePage(data) {
|
|
1781
|
-
return `import type { PageConfig } from "@onexapis/core";
|
|
1782
|
-
|
|
1783
|
-
/**
|
|
1784
|
-
* Home Page Configuration
|
|
1785
|
-
*/
|
|
1786
|
-
export const homePageConfig: PageConfig = {
|
|
1787
|
-
type: "home",
|
|
1788
|
-
title: "${data.displayName}",
|
|
1789
|
-
description: "Welcome to ${data.displayName}",
|
|
1790
|
-
|
|
1791
|
-
// SEO metadata
|
|
1792
|
-
seo: {
|
|
1793
|
-
title: "${data.displayName} - Home",
|
|
1794
|
-
description: "Welcome to ${data.displayName}",
|
|
1795
|
-
keywords: [],
|
|
1796
|
-
ogImage: undefined,
|
|
1797
|
-
},
|
|
1798
|
-
|
|
1799
|
-
// Page sections
|
|
1800
|
-
sections: [
|
|
1801
|
-
// Add your sections here
|
|
1802
|
-
// Example:
|
|
1803
|
-
// {
|
|
1804
|
-
// id: "hero-1",
|
|
1805
|
-
// type: "hero",
|
|
1806
|
-
// template: "default",
|
|
1807
|
-
// order: 0,
|
|
1808
|
-
// enabled: true,
|
|
1809
|
-
// settings: {},
|
|
1810
|
-
// components: [],
|
|
1811
|
-
// blocks: [],
|
|
1812
|
-
// },
|
|
1813
|
-
],
|
|
1814
|
-
};
|
|
1815
|
-
`;
|
|
1566
|
+
async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
|
|
1567
|
+
const configPath = path8.join(projectPath, "theme.config.ts");
|
|
1568
|
+
if (fs2.existsSync(configPath)) {
|
|
1569
|
+
let content = fs2.readFileSync(configPath, "utf-8");
|
|
1570
|
+
content = content.replace(
|
|
1571
|
+
/name: "My Simple Theme"/,
|
|
1572
|
+
`name: "${displayName}"`
|
|
1573
|
+
);
|
|
1574
|
+
content = content.replace(
|
|
1575
|
+
/description: ".*?"/,
|
|
1576
|
+
`description: "${description}"`
|
|
1577
|
+
);
|
|
1578
|
+
fs2.writeFileSync(configPath, content, "utf-8");
|
|
1579
|
+
}
|
|
1580
|
+
const pkgPath = path8.join(projectPath, "package.json");
|
|
1581
|
+
if (fs2.existsSync(pkgPath)) {
|
|
1582
|
+
let content = fs2.readFileSync(pkgPath, "utf-8");
|
|
1583
|
+
content = content.replace(
|
|
1584
|
+
/@onex-themes\/my-simple/g,
|
|
1585
|
+
`@onex-themes/${themeName}`
|
|
1586
|
+
);
|
|
1587
|
+
content = content.replace(
|
|
1588
|
+
/"description": ".*?"/,
|
|
1589
|
+
`"description": "${description}"`
|
|
1590
|
+
);
|
|
1591
|
+
fs2.writeFileSync(pkgPath, content, "utf-8");
|
|
1592
|
+
}
|
|
1816
1593
|
}
|
|
1817
1594
|
|
|
1818
1595
|
// src/commands/create-section.ts
|
|
@@ -3139,7 +2916,7 @@ async function deployCommand(options) {
|
|
|
3139
2916
|
if (options.environment) {
|
|
3140
2917
|
formData.append("environment", options.environment);
|
|
3141
2918
|
}
|
|
3142
|
-
const response = await
|
|
2919
|
+
const response = await fetch2(uploadEndpoint, {
|
|
3143
2920
|
method: "POST",
|
|
3144
2921
|
body: formData,
|
|
3145
2922
|
headers: formData.getHeaders()
|
|
@@ -3220,7 +2997,7 @@ function getBucketName(env) {
|
|
|
3220
2997
|
return process.env.BUCKET_NAME;
|
|
3221
2998
|
}
|
|
3222
2999
|
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
3223
|
-
return environment === "production" ? "
|
|
3000
|
+
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3224
3001
|
}
|
|
3225
3002
|
async function findCompiledThemeDir(themeId, version2) {
|
|
3226
3003
|
const searchPaths = [path8.resolve(process.cwd(), "dist")];
|
|
@@ -3336,7 +3113,7 @@ async function uploadCommand(options) {
|
|
|
3336
3113
|
}
|
|
3337
3114
|
spinner.succeed(`Found compiled theme at: ${compiledDir}`);
|
|
3338
3115
|
spinner.start("Creating bundle.zip...");
|
|
3339
|
-
const tmpDir =
|
|
3116
|
+
const tmpDir = os3.tmpdir();
|
|
3340
3117
|
const bundleZipPath = path8.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
|
|
3341
3118
|
await createZipFromDir(compiledDir, bundleZipPath);
|
|
3342
3119
|
const bundleZipBuffer = await fs.readFile(bundleZipPath);
|
|
@@ -3489,7 +3266,7 @@ function getBucketName2(env) {
|
|
|
3489
3266
|
return process.env.BUCKET_NAME;
|
|
3490
3267
|
}
|
|
3491
3268
|
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
3492
|
-
return environment === "production" ? "
|
|
3269
|
+
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3493
3270
|
}
|
|
3494
3271
|
async function streamToString(stream) {
|
|
3495
3272
|
const chunks = [];
|
|
@@ -3690,7 +3467,7 @@ function getBucketName3(env) {
|
|
|
3690
3467
|
return process.env.BUCKET_NAME;
|
|
3691
3468
|
}
|
|
3692
3469
|
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
3693
|
-
return environment === "production" ? "
|
|
3470
|
+
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3694
3471
|
}
|
|
3695
3472
|
async function streamToString2(stream) {
|
|
3696
3473
|
const chunks = [];
|
|
@@ -3734,8 +3511,8 @@ function runInstall(cwd) {
|
|
|
3734
3511
|
});
|
|
3735
3512
|
}
|
|
3736
3513
|
async function promptThemeName(originalName) {
|
|
3737
|
-
const { default:
|
|
3738
|
-
const { themeName } = await
|
|
3514
|
+
const { default: inquirer7 } = await import('inquirer');
|
|
3515
|
+
const { themeName } = await inquirer7.prompt([
|
|
3739
3516
|
{
|
|
3740
3517
|
type: "input",
|
|
3741
3518
|
name: "themeName",
|
|
@@ -3888,7 +3665,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3888
3665
|
[
|
|
3889
3666
|
"# API Configuration (enables real data in preview)",
|
|
3890
3667
|
"# Get your Company ID from the OneX dashboard",
|
|
3891
|
-
"NEXT_PUBLIC_API_URL=https://
|
|
3668
|
+
"NEXT_PUBLIC_API_URL=https://platform-dev.onexeos.com",
|
|
3892
3669
|
"NEXT_PUBLIC_COMPANY_ID=",
|
|
3893
3670
|
""
|
|
3894
3671
|
].join("\n")
|
|
@@ -4236,7 +4013,7 @@ async function devCommand(options) {
|
|
|
4236
4013
|
logger.info(`File changed: ${filePath}`);
|
|
4237
4014
|
try {
|
|
4238
4015
|
await context2.rebuild();
|
|
4239
|
-
await
|
|
4016
|
+
await generateManifest(themeName, themePath, outputDir);
|
|
4240
4017
|
server.broadcast({ type: "reload", timestamp: Date.now() });
|
|
4241
4018
|
logger.success("Rebuilt successfully");
|
|
4242
4019
|
} catch (error) {
|
|
@@ -4271,7 +4048,7 @@ async function devCommand(options) {
|
|
|
4271
4048
|
|
|
4272
4049
|
// src/commands/config.ts
|
|
4273
4050
|
init_logger();
|
|
4274
|
-
var CONFIG_DIR = path8.join(
|
|
4051
|
+
var CONFIG_DIR = path8.join(os3.homedir(), ".onexthm");
|
|
4275
4052
|
var CONFIG_FILE = path8.join(CONFIG_DIR, ".env");
|
|
4276
4053
|
var CONFIG_ENTRIES = [
|
|
4277
4054
|
{
|
|
@@ -4301,7 +4078,7 @@ var CONFIG_ENTRIES = [
|
|
|
4301
4078
|
key: "NEXT_PUBLIC_API_URL",
|
|
4302
4079
|
label: "API URL",
|
|
4303
4080
|
required: false,
|
|
4304
|
-
defaultValue: "https://
|
|
4081
|
+
defaultValue: "https://platform-dev.onexeos.com"
|
|
4305
4082
|
},
|
|
4306
4083
|
{
|
|
4307
4084
|
key: "NEXT_PUBLIC_COMPANY_ID",
|
|
@@ -4412,6 +4189,461 @@ async function configCommand() {
|
|
|
4412
4189
|
);
|
|
4413
4190
|
}
|
|
4414
4191
|
|
|
4192
|
+
// src/commands/login.ts
|
|
4193
|
+
init_logger();
|
|
4194
|
+
var AUTH_DIR = path8.join(os3.homedir(), ".onexthm");
|
|
4195
|
+
var AUTH_FILE = path8.join(AUTH_DIR, "auth.json");
|
|
4196
|
+
function getApiUrl() {
|
|
4197
|
+
return process.env.NEXT_PUBLIC_API_URL || process.env.ONEXTHM_API_URL || "https://platform-dev.onexeos.com";
|
|
4198
|
+
}
|
|
4199
|
+
async function saveAuthTokens(tokens) {
|
|
4200
|
+
await fs.ensureDir(AUTH_DIR);
|
|
4201
|
+
const key = getMachineKey();
|
|
4202
|
+
const data = JSON.stringify(tokens);
|
|
4203
|
+
const encrypted = encrypt(data, key);
|
|
4204
|
+
await fs.writeFile(AUTH_FILE, encrypted, "utf-8");
|
|
4205
|
+
}
|
|
4206
|
+
function loadAuthTokens() {
|
|
4207
|
+
try {
|
|
4208
|
+
if (!fs.existsSync(AUTH_FILE)) return null;
|
|
4209
|
+
const encrypted = fs.readFileSync(AUTH_FILE, "utf-8");
|
|
4210
|
+
const key = getMachineKey();
|
|
4211
|
+
const data = decrypt(encrypted, key);
|
|
4212
|
+
return JSON.parse(data);
|
|
4213
|
+
} catch {
|
|
4214
|
+
return null;
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
async function clearAuthTokens() {
|
|
4218
|
+
try {
|
|
4219
|
+
await fs.remove(AUTH_FILE);
|
|
4220
|
+
} catch {
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
function isTokenExpired(tokens) {
|
|
4224
|
+
return Date.now() / 1e3 > tokens.expiresAt - 60;
|
|
4225
|
+
}
|
|
4226
|
+
async function getValidTokens() {
|
|
4227
|
+
const tokens = loadAuthTokens();
|
|
4228
|
+
if (!tokens) return null;
|
|
4229
|
+
if (!isTokenExpired(tokens)) return tokens;
|
|
4230
|
+
try {
|
|
4231
|
+
const apiUrl = getApiUrl();
|
|
4232
|
+
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
4233
|
+
method: "POST",
|
|
4234
|
+
headers: { "Content-Type": "application/json" },
|
|
4235
|
+
body: JSON.stringify({ refresh_token: tokens.refreshToken })
|
|
4236
|
+
});
|
|
4237
|
+
if (!response.ok) {
|
|
4238
|
+
await clearAuthTokens();
|
|
4239
|
+
return null;
|
|
4240
|
+
}
|
|
4241
|
+
const data = await response.json();
|
|
4242
|
+
const body = data.statusCode ? data.body : data;
|
|
4243
|
+
const refreshed = {
|
|
4244
|
+
...tokens,
|
|
4245
|
+
accessToken: body.AccessToken || tokens.accessToken,
|
|
4246
|
+
idToken: body.IdToken || tokens.idToken,
|
|
4247
|
+
expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
|
|
4248
|
+
};
|
|
4249
|
+
await saveAuthTokens(refreshed);
|
|
4250
|
+
return refreshed;
|
|
4251
|
+
} catch {
|
|
4252
|
+
await clearAuthTokens();
|
|
4253
|
+
return null;
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
async function authenticatedFetch(url, init) {
|
|
4257
|
+
const tokens = await getValidTokens();
|
|
4258
|
+
if (!tokens) {
|
|
4259
|
+
throw new Error("Not logged in. Run: onexthm login");
|
|
4260
|
+
}
|
|
4261
|
+
const headers = new Headers(init?.headers);
|
|
4262
|
+
headers.set("Authorization", `Bearer ${tokens.idToken}`);
|
|
4263
|
+
headers.set("Content-Type", "application/json");
|
|
4264
|
+
return fetch(url, { ...init, headers });
|
|
4265
|
+
}
|
|
4266
|
+
function getMachineKey() {
|
|
4267
|
+
let seed;
|
|
4268
|
+
if (process.platform === "darwin") {
|
|
4269
|
+
seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
|
|
4270
|
+
} else if (process.platform === "linux") {
|
|
4271
|
+
try {
|
|
4272
|
+
seed = `onexthm:${fs.readFileSync("/etc/machine-id", "utf-8").trim()}`;
|
|
4273
|
+
} catch {
|
|
4274
|
+
seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
|
|
4275
|
+
}
|
|
4276
|
+
} else {
|
|
4277
|
+
seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
|
|
4278
|
+
}
|
|
4279
|
+
return crypto2.createHash("sha256").update(seed).digest();
|
|
4280
|
+
}
|
|
4281
|
+
function encrypt(text, key) {
|
|
4282
|
+
const iv = crypto2.randomBytes(16);
|
|
4283
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
|
|
4284
|
+
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
4285
|
+
encrypted += cipher.final("hex");
|
|
4286
|
+
const tag = cipher.getAuthTag();
|
|
4287
|
+
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
|
|
4288
|
+
}
|
|
4289
|
+
function decrypt(text, key) {
|
|
4290
|
+
const [ivHex, tagHex, encrypted] = text.split(":");
|
|
4291
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
4292
|
+
const tag = Buffer.from(tagHex, "hex");
|
|
4293
|
+
const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
|
|
4294
|
+
decipher.setAuthTag(tag);
|
|
4295
|
+
let decrypted = decipher.update(encrypted, "hex", "utf-8");
|
|
4296
|
+
decrypted += decipher.final("utf-8");
|
|
4297
|
+
return decrypted;
|
|
4298
|
+
}
|
|
4299
|
+
function parseJwtClaims(idToken) {
|
|
4300
|
+
try {
|
|
4301
|
+
const payload = idToken.split(".")[1];
|
|
4302
|
+
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
|
|
4303
|
+
return JSON.parse(decoded);
|
|
4304
|
+
} catch {
|
|
4305
|
+
return {};
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
// src/commands/login.ts
|
|
4310
|
+
async function loginCommand() {
|
|
4311
|
+
logger.header("OneX Theme Developer Login");
|
|
4312
|
+
const existing = loadAuthTokens();
|
|
4313
|
+
if (existing) {
|
|
4314
|
+
logger.info(`Already logged in as: ${existing.user.email}`);
|
|
4315
|
+
const { relogin } = await inquirer.prompt([
|
|
4316
|
+
{
|
|
4317
|
+
type: "confirm",
|
|
4318
|
+
name: "relogin",
|
|
4319
|
+
message: "Re-login with different account?",
|
|
4320
|
+
default: false
|
|
4321
|
+
}
|
|
4322
|
+
]);
|
|
4323
|
+
if (!relogin) return;
|
|
4324
|
+
}
|
|
4325
|
+
const { email, password } = await inquirer.prompt([
|
|
4326
|
+
{
|
|
4327
|
+
type: "input",
|
|
4328
|
+
name: "email",
|
|
4329
|
+
message: "Email:",
|
|
4330
|
+
validate: (input) => input.includes("@") ? true : "Enter a valid email"
|
|
4331
|
+
},
|
|
4332
|
+
{
|
|
4333
|
+
type: "password",
|
|
4334
|
+
name: "password",
|
|
4335
|
+
message: "Password:",
|
|
4336
|
+
validate: (input) => input.length >= 6 ? true : "Password too short"
|
|
4337
|
+
}
|
|
4338
|
+
]);
|
|
4339
|
+
logger.startSpinner("Logging in...");
|
|
4340
|
+
try {
|
|
4341
|
+
const apiUrl = getApiUrl();
|
|
4342
|
+
const response = await fetch(`${apiUrl}/auth/login`, {
|
|
4343
|
+
method: "POST",
|
|
4344
|
+
headers: { "Content-Type": "application/json" },
|
|
4345
|
+
body: JSON.stringify({ username: email, password })
|
|
4346
|
+
});
|
|
4347
|
+
const raw = await response.json();
|
|
4348
|
+
const data = raw.statusCode ? raw.body : raw;
|
|
4349
|
+
if (!response.ok || data.error) {
|
|
4350
|
+
logger.stopSpinner(false, "Login failed");
|
|
4351
|
+
logger.error(data.message || data.error || "Invalid credentials");
|
|
4352
|
+
process.exit(1);
|
|
4353
|
+
}
|
|
4354
|
+
const idToken = data.IdToken;
|
|
4355
|
+
const accessToken = data.AccessToken;
|
|
4356
|
+
const refreshToken = data.RefreshToken;
|
|
4357
|
+
const expiresIn = data.ExpiresIn || 3600;
|
|
4358
|
+
if (!idToken) {
|
|
4359
|
+
logger.stopSpinner(false, "Login failed");
|
|
4360
|
+
logger.error("No token received from server");
|
|
4361
|
+
process.exit(1);
|
|
4362
|
+
}
|
|
4363
|
+
const claims = parseJwtClaims(idToken);
|
|
4364
|
+
const tokens = {
|
|
4365
|
+
accessToken,
|
|
4366
|
+
idToken,
|
|
4367
|
+
refreshToken,
|
|
4368
|
+
expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
|
|
4369
|
+
user: {
|
|
4370
|
+
email: claims.email || email,
|
|
4371
|
+
name: claims.name,
|
|
4372
|
+
companyId: claims["custom:company_id"],
|
|
4373
|
+
userId: claims.sub
|
|
4374
|
+
}
|
|
4375
|
+
};
|
|
4376
|
+
await saveAuthTokens(tokens);
|
|
4377
|
+
logger.stopSpinner(true, "Logged in!");
|
|
4378
|
+
logger.newLine();
|
|
4379
|
+
logger.info(` Email: ${tokens.user.email}`);
|
|
4380
|
+
if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
|
|
4381
|
+
if (tokens.user.companyId)
|
|
4382
|
+
logger.info(` Company: ${tokens.user.companyId}`);
|
|
4383
|
+
logger.newLine();
|
|
4384
|
+
logger.success("Token stored securely in ~/.onexthm/auth.json (encrypted)");
|
|
4385
|
+
} catch (error) {
|
|
4386
|
+
logger.stopSpinner(false, "Login failed");
|
|
4387
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4388
|
+
process.exit(1);
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
// src/commands/logout.ts
|
|
4393
|
+
init_logger();
|
|
4394
|
+
async function logoutCommand() {
|
|
4395
|
+
const tokens = loadAuthTokens();
|
|
4396
|
+
if (!tokens) {
|
|
4397
|
+
logger.info("Not logged in.");
|
|
4398
|
+
return;
|
|
4399
|
+
}
|
|
4400
|
+
await clearAuthTokens();
|
|
4401
|
+
logger.success(`Logged out (was: ${tokens.user.email})`);
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
// src/commands/whoami.ts
|
|
4405
|
+
init_logger();
|
|
4406
|
+
async function whoamiCommand() {
|
|
4407
|
+
const tokens = loadAuthTokens();
|
|
4408
|
+
if (!tokens) {
|
|
4409
|
+
logger.error("Not logged in. Run: onexthm login");
|
|
4410
|
+
process.exit(1);
|
|
4411
|
+
}
|
|
4412
|
+
const expired = isTokenExpired(tokens);
|
|
4413
|
+
logger.header("OneX Theme Developer");
|
|
4414
|
+
logger.info(` Email: ${tokens.user.email}`);
|
|
4415
|
+
if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
|
|
4416
|
+
if (tokens.user.companyId)
|
|
4417
|
+
logger.info(` Company: ${tokens.user.companyId}`);
|
|
4418
|
+
logger.info(
|
|
4419
|
+
` Status: ${expired ? "\u26A0 Token expired (will auto-refresh)" : "\u2713 Active"}`
|
|
4420
|
+
);
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
// src/commands/publish.ts
|
|
4424
|
+
init_logger();
|
|
4425
|
+
async function publishCommand(options) {
|
|
4426
|
+
logger.header("OneX Theme Publish");
|
|
4427
|
+
const tokens = await getValidTokens();
|
|
4428
|
+
if (!tokens) {
|
|
4429
|
+
logger.error("Not logged in. Run: onexthm login");
|
|
4430
|
+
process.exit(1);
|
|
4431
|
+
}
|
|
4432
|
+
logger.info(`Logged in as: ${tokens.user.email}`);
|
|
4433
|
+
let themePath;
|
|
4434
|
+
if (options.theme) {
|
|
4435
|
+
themePath = path8.resolve(options.theme);
|
|
4436
|
+
} else {
|
|
4437
|
+
const isThemeDir = [
|
|
4438
|
+
"theme.config.ts",
|
|
4439
|
+
"bundle-entry.ts",
|
|
4440
|
+
"manifest.ts"
|
|
4441
|
+
].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
|
|
4442
|
+
if (isThemeDir) {
|
|
4443
|
+
themePath = process.cwd();
|
|
4444
|
+
} else {
|
|
4445
|
+
logger.error(
|
|
4446
|
+
"Not in a theme directory. Run from theme root or use --theme flag."
|
|
4447
|
+
);
|
|
4448
|
+
process.exit(1);
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
const pkgPath = path8.join(themePath, "package.json");
|
|
4452
|
+
if (!fs.existsSync(pkgPath)) {
|
|
4453
|
+
logger.error("No package.json found in theme directory");
|
|
4454
|
+
process.exit(1);
|
|
4455
|
+
}
|
|
4456
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
4457
|
+
const themeId = pkg.name?.replace("@onex-themes/", "") || path8.basename(themePath);
|
|
4458
|
+
const version2 = pkg.version || "1.0.0";
|
|
4459
|
+
logger.newLine();
|
|
4460
|
+
logger.info(`Theme: ${themeId}`);
|
|
4461
|
+
logger.info(`Version: ${version2}`);
|
|
4462
|
+
logger.newLine();
|
|
4463
|
+
const apiUrl = getApiUrl();
|
|
4464
|
+
logger.startSpinner("Registering theme...");
|
|
4465
|
+
try {
|
|
4466
|
+
const regResponse = await authenticatedFetch(
|
|
4467
|
+
`${apiUrl}/website-api/themes/register`,
|
|
4468
|
+
{
|
|
4469
|
+
method: "POST",
|
|
4470
|
+
body: JSON.stringify({
|
|
4471
|
+
themeId,
|
|
4472
|
+
name: pkg.displayName || themeId,
|
|
4473
|
+
description: pkg.description || "",
|
|
4474
|
+
email: tokens.user.email
|
|
4475
|
+
})
|
|
4476
|
+
}
|
|
4477
|
+
);
|
|
4478
|
+
const regData = await regResponse.json();
|
|
4479
|
+
const regBody = regData.statusCode ? regData.body : regData;
|
|
4480
|
+
if (!regResponse.ok && regBody.error && !regBody.error.includes("already registered")) {
|
|
4481
|
+
logger.stopSpinner(false, "Registration failed");
|
|
4482
|
+
logger.error(regBody.error);
|
|
4483
|
+
process.exit(1);
|
|
4484
|
+
}
|
|
4485
|
+
logger.stopSpinner(true, regBody.message || "Theme registered");
|
|
4486
|
+
} catch (error) {
|
|
4487
|
+
logger.stopSpinner(false, "Registration failed");
|
|
4488
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4489
|
+
process.exit(1);
|
|
4490
|
+
}
|
|
4491
|
+
logger.startSpinner("Building theme...");
|
|
4492
|
+
try {
|
|
4493
|
+
const { execSync: execSync3 } = await import('child_process');
|
|
4494
|
+
execSync3(
|
|
4495
|
+
"npx tsup bundle-entry.ts --format esm --target es2020 --outDir dist",
|
|
4496
|
+
{
|
|
4497
|
+
cwd: themePath,
|
|
4498
|
+
stdio: "ignore"
|
|
4499
|
+
}
|
|
4500
|
+
);
|
|
4501
|
+
logger.stopSpinner(true, "Theme compiled");
|
|
4502
|
+
} catch {
|
|
4503
|
+
logger.stopSpinner(false, "Build failed");
|
|
4504
|
+
logger.error("Run 'onexthm build' to see build errors");
|
|
4505
|
+
process.exit(1);
|
|
4506
|
+
}
|
|
4507
|
+
logger.startSpinner("Getting upload URL...");
|
|
4508
|
+
let bundleUploadUrl;
|
|
4509
|
+
let sourceUploadUrl;
|
|
4510
|
+
try {
|
|
4511
|
+
const pubResponse = await authenticatedFetch(
|
|
4512
|
+
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`,
|
|
4513
|
+
{
|
|
4514
|
+
method: "POST",
|
|
4515
|
+
body: JSON.stringify({ version: version2 })
|
|
4516
|
+
}
|
|
4517
|
+
);
|
|
4518
|
+
const pubData = await pubResponse.json();
|
|
4519
|
+
const pubBody = pubData.statusCode ? pubData.body : pubData;
|
|
4520
|
+
if (!pubResponse.ok || !pubBody.bundleUploadUrl) {
|
|
4521
|
+
logger.stopSpinner(false, "Failed to get upload URL");
|
|
4522
|
+
logger.error(pubBody.error || "Server error");
|
|
4523
|
+
process.exit(1);
|
|
4524
|
+
}
|
|
4525
|
+
bundleUploadUrl = pubBody.bundleUploadUrl;
|
|
4526
|
+
sourceUploadUrl = pubBody.sourceUploadUrl;
|
|
4527
|
+
logger.stopSpinner(true, "Upload URL obtained");
|
|
4528
|
+
} catch (error) {
|
|
4529
|
+
logger.stopSpinner(false, "Failed");
|
|
4530
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4531
|
+
process.exit(1);
|
|
4532
|
+
}
|
|
4533
|
+
logger.startSpinner("Uploading bundle...");
|
|
4534
|
+
try {
|
|
4535
|
+
const archiver3 = await import('archiver');
|
|
4536
|
+
const { createWriteStream } = await import('fs');
|
|
4537
|
+
const distDir = path8.join(themePath, "dist");
|
|
4538
|
+
if (!fs.existsSync(distDir)) {
|
|
4539
|
+
logger.stopSpinner(false, "No dist/ directory");
|
|
4540
|
+
logger.error("Build the theme first: onexthm build");
|
|
4541
|
+
process.exit(1);
|
|
4542
|
+
}
|
|
4543
|
+
const bundleZipPath = path8.join(themePath, "dist", "bundle.zip");
|
|
4544
|
+
await createZip(distDir, bundleZipPath, ["bundle.zip"]);
|
|
4545
|
+
const bundleBuffer = fs.readFileSync(bundleZipPath);
|
|
4546
|
+
const bundleRes = await fetch(bundleUploadUrl, {
|
|
4547
|
+
method: "PUT",
|
|
4548
|
+
headers: { "Content-Type": "application/zip" },
|
|
4549
|
+
body: bundleBuffer
|
|
4550
|
+
});
|
|
4551
|
+
if (!bundleRes.ok) {
|
|
4552
|
+
throw new Error(`Upload failed: ${bundleRes.status}`);
|
|
4553
|
+
}
|
|
4554
|
+
const sizeMB = (bundleBuffer.length / 1024 / 1024).toFixed(2);
|
|
4555
|
+
logger.stopSpinner(true, `Bundle uploaded (${sizeMB} MB)`);
|
|
4556
|
+
} catch (error) {
|
|
4557
|
+
logger.stopSpinner(false, "Upload failed");
|
|
4558
|
+
logger.error(error instanceof Error ? error.message : "Upload error");
|
|
4559
|
+
process.exit(1);
|
|
4560
|
+
}
|
|
4561
|
+
logger.startSpinner("Uploading source...");
|
|
4562
|
+
try {
|
|
4563
|
+
const sourceZipPath = path8.join(themePath, "dist", "source.zip");
|
|
4564
|
+
await createZip(themePath, sourceZipPath, [
|
|
4565
|
+
"node_modules",
|
|
4566
|
+
"dist",
|
|
4567
|
+
".git",
|
|
4568
|
+
".env",
|
|
4569
|
+
".env.local"
|
|
4570
|
+
]);
|
|
4571
|
+
const sourceBuffer = fs.readFileSync(sourceZipPath);
|
|
4572
|
+
const sourceRes = await fetch(sourceUploadUrl, {
|
|
4573
|
+
method: "PUT",
|
|
4574
|
+
headers: { "Content-Type": "application/zip" },
|
|
4575
|
+
body: sourceBuffer
|
|
4576
|
+
});
|
|
4577
|
+
if (!sourceRes.ok) {
|
|
4578
|
+
throw new Error(`Source upload failed: ${sourceRes.status}`);
|
|
4579
|
+
}
|
|
4580
|
+
const sizeMB = (sourceBuffer.length / 1024 / 1024).toFixed(2);
|
|
4581
|
+
logger.stopSpinner(true, `Source uploaded (${sizeMB} MB)`);
|
|
4582
|
+
} catch (error) {
|
|
4583
|
+
logger.stopSpinner(false, "Source upload failed");
|
|
4584
|
+
logger.info("Source upload skipped (bundle was uploaded successfully)");
|
|
4585
|
+
}
|
|
4586
|
+
logger.startSpinner("Scanning and publishing...");
|
|
4587
|
+
try {
|
|
4588
|
+
const confirmResponse = await authenticatedFetch(
|
|
4589
|
+
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/confirm`,
|
|
4590
|
+
{ method: "POST" }
|
|
4591
|
+
);
|
|
4592
|
+
const confirmData = await confirmResponse.json();
|
|
4593
|
+
const confirmBody = confirmData.statusCode ? confirmData.body : confirmData;
|
|
4594
|
+
if (!confirmResponse.ok || !confirmBody.success) {
|
|
4595
|
+
logger.stopSpinner(false, "Publishing failed");
|
|
4596
|
+
if (confirmBody.violations) {
|
|
4597
|
+
logger.error("Theme rejected \u2014 security violations found:");
|
|
4598
|
+
for (const v of confirmBody.violations) {
|
|
4599
|
+
logger.log(` \u274C ${v.file}: ${v.violation}`);
|
|
4600
|
+
}
|
|
4601
|
+
} else {
|
|
4602
|
+
logger.error(confirmBody.error || "Unknown error");
|
|
4603
|
+
}
|
|
4604
|
+
if (confirmBody.warnings?.length) {
|
|
4605
|
+
logger.newLine();
|
|
4606
|
+
logger.info("Warnings:");
|
|
4607
|
+
for (const w of confirmBody.warnings) {
|
|
4608
|
+
logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
process.exit(1);
|
|
4612
|
+
}
|
|
4613
|
+
logger.stopSpinner(true, confirmBody.message || "Published!");
|
|
4614
|
+
if (confirmBody.warnings?.length) {
|
|
4615
|
+
logger.newLine();
|
|
4616
|
+
logger.info("Warnings (non-blocking):");
|
|
4617
|
+
for (const w of confirmBody.warnings) {
|
|
4618
|
+
logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
} catch (error) {
|
|
4622
|
+
logger.stopSpinner(false, "Publishing failed");
|
|
4623
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4624
|
+
process.exit(1);
|
|
4625
|
+
}
|
|
4626
|
+
logger.newLine();
|
|
4627
|
+
logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
|
|
4628
|
+
}
|
|
4629
|
+
async function createZip(sourceDir, outputPath, exclude) {
|
|
4630
|
+
const archiver3 = (await import('archiver')).default;
|
|
4631
|
+
const { createWriteStream } = await import('fs');
|
|
4632
|
+
return new Promise((resolve, reject) => {
|
|
4633
|
+
const output = createWriteStream(outputPath);
|
|
4634
|
+
const archive = archiver3("zip", { zlib: { level: 9 } });
|
|
4635
|
+
output.on("close", resolve);
|
|
4636
|
+
archive.on("error", reject);
|
|
4637
|
+
archive.pipe(output);
|
|
4638
|
+
archive.glob("**/*", {
|
|
4639
|
+
cwd: sourceDir,
|
|
4640
|
+
ignore: exclude.map((e) => `${e}/**`),
|
|
4641
|
+
dot: false
|
|
4642
|
+
});
|
|
4643
|
+
archive.finalize();
|
|
4644
|
+
});
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4415
4647
|
// src/cli.ts
|
|
4416
4648
|
try {
|
|
4417
4649
|
const projectRoot = getProjectRoot();
|
|
@@ -4423,7 +4655,7 @@ try {
|
|
|
4423
4655
|
} catch {
|
|
4424
4656
|
}
|
|
4425
4657
|
dotenv.config({
|
|
4426
|
-
path: path8.join(
|
|
4658
|
+
path: path8.join(os3.homedir(), ".onexthm", ".env"),
|
|
4427
4659
|
quiet: true
|
|
4428
4660
|
});
|
|
4429
4661
|
var require2 = createRequire(import.meta.url);
|
|
@@ -4480,6 +4712,10 @@ program.command("clone").description("Clone theme source code from S3").argument
|
|
|
4480
4712
|
"staging"
|
|
4481
4713
|
).option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
|
|
4482
4714
|
program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
|
|
4715
|
+
program.command("login").description("Login to OneX platform").action(loginCommand);
|
|
4716
|
+
program.command("logout").description("Logout from OneX platform").action(logoutCommand);
|
|
4717
|
+
program.command("whoami").description("Show current logged-in developer").action(whoamiCommand);
|
|
4718
|
+
program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").action(publishCommand);
|
|
4483
4719
|
program.configureOutput({
|
|
4484
4720
|
writeErr: (str) => process.stderr.write(chalk4.red(str))
|
|
4485
4721
|
});
|