@onexapis/cli 1.1.43 → 1.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +361 -469
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +361 -469
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +38 -14
- package/dist/index.d.ts +38 -14
- package/dist/index.js +284 -443
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +284 -442
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -3
package/dist/index.mjs
CHANGED
|
@@ -12,8 +12,6 @@ import inquirer from 'inquirer';
|
|
|
12
12
|
import fs from 'fs-extra';
|
|
13
13
|
import ejs from 'ejs';
|
|
14
14
|
import os from 'os';
|
|
15
|
-
import { PutObjectCommand, GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
16
|
-
import archiver from 'archiver';
|
|
17
15
|
import AdmZip from 'adm-zip';
|
|
18
16
|
|
|
19
17
|
var __defProp = Object.defineProperty;
|
|
@@ -1323,8 +1321,8 @@ function validateThemeName(name) {
|
|
|
1323
1321
|
return /^[a-z][a-z0-9-]*$/.test(name);
|
|
1324
1322
|
}
|
|
1325
1323
|
function pathExists(filePath) {
|
|
1326
|
-
const
|
|
1327
|
-
return
|
|
1324
|
+
const fs11 = __require("fs-extra");
|
|
1325
|
+
return fs11.existsSync(filePath);
|
|
1328
1326
|
}
|
|
1329
1327
|
function validateCategory(category) {
|
|
1330
1328
|
const validCategories = [
|
|
@@ -1506,10 +1504,110 @@ async function installDependencies(projectPath, packageManager = "npm") {
|
|
|
1506
1504
|
});
|
|
1507
1505
|
}
|
|
1508
1506
|
var AUTH_DIR = path8.join(os.homedir(), ".onexthm");
|
|
1509
|
-
path8.join(AUTH_DIR, "auth.json");
|
|
1507
|
+
var AUTH_FILE = path8.join(AUTH_DIR, "auth.json");
|
|
1510
1508
|
function getApiUrl() {
|
|
1511
1509
|
return process.env.ONEXTHM_API_URL || process.env.NEXT_PUBLIC_API_URL || "https://platform-dev.onexeos.com";
|
|
1512
1510
|
}
|
|
1511
|
+
async function saveAuthTokens(tokens) {
|
|
1512
|
+
await fs.ensureDir(AUTH_DIR);
|
|
1513
|
+
const key = getMachineKey();
|
|
1514
|
+
const data = JSON.stringify(tokens);
|
|
1515
|
+
const encrypted = encrypt(data, key);
|
|
1516
|
+
await fs.writeFile(AUTH_FILE, encrypted, "utf-8");
|
|
1517
|
+
}
|
|
1518
|
+
function loadAuthTokens() {
|
|
1519
|
+
try {
|
|
1520
|
+
if (!fs.existsSync(AUTH_FILE)) return null;
|
|
1521
|
+
const encrypted = fs.readFileSync(AUTH_FILE, "utf-8");
|
|
1522
|
+
const key = getMachineKey();
|
|
1523
|
+
const data = decrypt(encrypted, key);
|
|
1524
|
+
return JSON.parse(data);
|
|
1525
|
+
} catch {
|
|
1526
|
+
return null;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
async function clearAuthTokens() {
|
|
1530
|
+
try {
|
|
1531
|
+
await fs.remove(AUTH_FILE);
|
|
1532
|
+
} catch {
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
function isTokenExpired(tokens) {
|
|
1536
|
+
return Date.now() / 1e3 > tokens.expiresAt - 60;
|
|
1537
|
+
}
|
|
1538
|
+
async function getValidTokens() {
|
|
1539
|
+
const tokens = loadAuthTokens();
|
|
1540
|
+
if (!tokens) return null;
|
|
1541
|
+
if (!isTokenExpired(tokens)) return tokens;
|
|
1542
|
+
try {
|
|
1543
|
+
const apiUrl = getApiUrl();
|
|
1544
|
+
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
1545
|
+
method: "POST",
|
|
1546
|
+
headers: { "Content-Type": "application/json" },
|
|
1547
|
+
body: JSON.stringify({ refresh_token: tokens.refreshToken })
|
|
1548
|
+
});
|
|
1549
|
+
if (!response.ok) {
|
|
1550
|
+
await clearAuthTokens();
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|
|
1553
|
+
const data = await response.json();
|
|
1554
|
+
const body = data.statusCode ? data.body : data;
|
|
1555
|
+
const refreshed = {
|
|
1556
|
+
...tokens,
|
|
1557
|
+
accessToken: body.AccessToken || tokens.accessToken,
|
|
1558
|
+
idToken: body.IdToken || tokens.idToken,
|
|
1559
|
+
expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
|
|
1560
|
+
};
|
|
1561
|
+
await saveAuthTokens(refreshed);
|
|
1562
|
+
return refreshed;
|
|
1563
|
+
} catch {
|
|
1564
|
+
await clearAuthTokens();
|
|
1565
|
+
return null;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
async function authenticatedFetch(url, init) {
|
|
1569
|
+
const tokens = await getValidTokens();
|
|
1570
|
+
if (!tokens) {
|
|
1571
|
+
throw new Error("Not logged in. Run: onexthm login");
|
|
1572
|
+
}
|
|
1573
|
+
const headers = new Headers(init?.headers);
|
|
1574
|
+
headers.set("Authorization", `Bearer ${tokens.idToken}`);
|
|
1575
|
+
headers.set("Content-Type", "application/json");
|
|
1576
|
+
return fetch(url, { ...init, headers });
|
|
1577
|
+
}
|
|
1578
|
+
function getMachineKey() {
|
|
1579
|
+
let seed;
|
|
1580
|
+
if (process.platform === "darwin") {
|
|
1581
|
+
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1582
|
+
} else if (process.platform === "linux") {
|
|
1583
|
+
try {
|
|
1584
|
+
seed = `onexthm:${fs.readFileSync("/etc/machine-id", "utf-8").trim()}`;
|
|
1585
|
+
} catch {
|
|
1586
|
+
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1587
|
+
}
|
|
1588
|
+
} else {
|
|
1589
|
+
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1590
|
+
}
|
|
1591
|
+
return crypto.createHash("sha256").update(seed).digest();
|
|
1592
|
+
}
|
|
1593
|
+
function encrypt(text, key) {
|
|
1594
|
+
const iv = crypto.randomBytes(16);
|
|
1595
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
1596
|
+
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
1597
|
+
encrypted += cipher.final("hex");
|
|
1598
|
+
const tag = cipher.getAuthTag();
|
|
1599
|
+
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
|
|
1600
|
+
}
|
|
1601
|
+
function decrypt(text, key) {
|
|
1602
|
+
const [ivHex, tagHex, encrypted] = text.split(":");
|
|
1603
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
1604
|
+
const tag = Buffer.from(tagHex, "hex");
|
|
1605
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
1606
|
+
decipher.setAuthTag(tag);
|
|
1607
|
+
let decrypted = decipher.update(encrypted, "hex", "utf-8");
|
|
1608
|
+
decrypted += decipher.final("utf-8");
|
|
1609
|
+
return decrypted;
|
|
1610
|
+
}
|
|
1513
1611
|
|
|
1514
1612
|
// src/commands/init.ts
|
|
1515
1613
|
async function initCommand(projectName, options = {}) {
|
|
@@ -2718,343 +2816,94 @@ function runCommand(command, args, cwd) {
|
|
|
2718
2816
|
|
|
2719
2817
|
// src/commands/upload.ts
|
|
2720
2818
|
init_logger();
|
|
2721
|
-
function
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
endpoint: endpointUrl,
|
|
2729
|
-
region: "us-east-1",
|
|
2730
|
-
credentials: {
|
|
2731
|
-
accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
|
|
2732
|
-
secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
|
|
2733
|
-
},
|
|
2734
|
-
forcePathStyle: true
|
|
2735
|
-
});
|
|
2736
|
-
}
|
|
2737
|
-
if (adapterMode === "local") {
|
|
2738
|
-
return new S3Client({
|
|
2739
|
-
endpoint: "http://localhost:4569",
|
|
2740
|
-
region: "ap-southeast-1",
|
|
2741
|
-
credentials: {
|
|
2742
|
-
accessKeyId: "S3RVER",
|
|
2743
|
-
secretAccessKey: "S3RVER"
|
|
2744
|
-
},
|
|
2745
|
-
forcePathStyle: true
|
|
2746
|
-
});
|
|
2747
|
-
}
|
|
2748
|
-
return new S3Client({
|
|
2749
|
-
region: process.env.AWS_REGION || "ap-southeast-1"
|
|
2750
|
-
});
|
|
2751
|
-
}
|
|
2752
|
-
function getBucketName(env) {
|
|
2753
|
-
if (process.env.BUCKET_NAME) {
|
|
2754
|
-
return process.env.BUCKET_NAME;
|
|
2755
|
-
}
|
|
2756
|
-
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
2757
|
-
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
2758
|
-
}
|
|
2759
|
-
async function findCompiledThemeDir(themeId, version) {
|
|
2760
|
-
const searchPaths = [path8.resolve(process.cwd(), "dist")];
|
|
2761
|
-
for (const dir of searchPaths) {
|
|
2762
|
-
if (await fs.pathExists(dir)) {
|
|
2763
|
-
const hasManifest = await fs.pathExists(path8.join(dir, "manifest.json"));
|
|
2764
|
-
const hasThemeEntry = await fs.pathExists(path8.join(dir, "bundle-entry.js")) || await fs.pathExists(path8.join(dir, "theme.config.js")) || await fs.pathExists(path8.join(dir, "index.js"));
|
|
2765
|
-
if (hasManifest || hasThemeEntry) {
|
|
2766
|
-
return dir;
|
|
2767
|
-
}
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
return null;
|
|
2771
|
-
}
|
|
2772
|
-
async function readManifest() {
|
|
2773
|
-
const manifestTsPath = path8.resolve(process.cwd(), "manifest.ts");
|
|
2774
|
-
if (await fs.pathExists(manifestTsPath)) {
|
|
2775
|
-
try {
|
|
2776
|
-
const module = await import(manifestTsPath);
|
|
2777
|
-
return module.default || module;
|
|
2778
|
-
} catch (error) {
|
|
2779
|
-
logger.warning("Failed to import manifest.ts, trying package.json");
|
|
2780
|
-
}
|
|
2781
|
-
}
|
|
2782
|
-
const packageJsonPath = path8.resolve(process.cwd(), "package.json");
|
|
2783
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
2784
|
-
const pkg = await fs.readJson(packageJsonPath);
|
|
2785
|
-
return {
|
|
2786
|
-
themeId: pkg.name?.replace("@onex-themes/", "") || "unknown",
|
|
2787
|
-
version: pkg.version || "1.0.0"
|
|
2788
|
-
};
|
|
2789
|
-
}
|
|
2790
|
-
throw new Error(
|
|
2791
|
-
"No manifest.ts or package.json found. Are you in a theme directory?"
|
|
2819
|
+
async function uploadCommand(_options) {
|
|
2820
|
+
logger.header("Upload Theme to S3 \u2014 DEPRECATED");
|
|
2821
|
+
console.log();
|
|
2822
|
+
console.log(
|
|
2823
|
+
chalk4.yellow.bold(
|
|
2824
|
+
"`onexthm upload` is deprecated and no longer functional."
|
|
2825
|
+
)
|
|
2792
2826
|
);
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
const output = fs.createWriteStream(outputPath);
|
|
2797
|
-
const archive = archiver("zip", { zlib: { level: 6 } });
|
|
2798
|
-
output.on("close", () => resolve());
|
|
2799
|
-
archive.on("error", (err) => reject(err));
|
|
2800
|
-
archive.pipe(output);
|
|
2801
|
-
archive.glob("**/*", {
|
|
2802
|
-
cwd: sourceDir,
|
|
2803
|
-
dot: true,
|
|
2804
|
-
ignore: excludePatterns
|
|
2805
|
-
});
|
|
2806
|
-
archive.finalize();
|
|
2807
|
-
});
|
|
2808
|
-
}
|
|
2809
|
-
async function findSourceDir(themeId, explicitDir) {
|
|
2810
|
-
if (explicitDir) {
|
|
2811
|
-
if (await fs.pathExists(explicitDir)) return explicitDir;
|
|
2812
|
-
return null;
|
|
2813
|
-
}
|
|
2814
|
-
const searchPaths = [
|
|
2815
|
-
process.cwd(),
|
|
2816
|
-
path8.resolve(process.cwd(), `../../themes/${themeId}`),
|
|
2817
|
-
path8.resolve(process.cwd(), `../themes/${themeId}`)
|
|
2818
|
-
];
|
|
2819
|
-
const markers = ["theme.config.ts", "bundle-entry.ts"];
|
|
2820
|
-
for (const dir of searchPaths) {
|
|
2821
|
-
for (const marker of markers) {
|
|
2822
|
-
if (await fs.pathExists(path8.join(dir, marker))) {
|
|
2823
|
-
return dir;
|
|
2824
|
-
}
|
|
2825
|
-
}
|
|
2826
|
-
}
|
|
2827
|
-
return null;
|
|
2828
|
-
}
|
|
2829
|
-
async function updateLatestPointer(s3Client, bucket, themeId, version) {
|
|
2830
|
-
const latestData = {
|
|
2831
|
-
version,
|
|
2832
|
-
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2833
|
-
};
|
|
2834
|
-
await s3Client.send(
|
|
2835
|
-
new PutObjectCommand({
|
|
2836
|
-
Bucket: bucket,
|
|
2837
|
-
Key: `themes/${themeId}/latest.json`,
|
|
2838
|
-
Body: JSON.stringify(latestData, null, 2),
|
|
2839
|
-
ContentType: "application/json"
|
|
2840
|
-
})
|
|
2827
|
+
console.log();
|
|
2828
|
+
console.log(
|
|
2829
|
+
"The platform no longer exposes themes via direct S3 access, so this"
|
|
2841
2830
|
);
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
if (!compiledDir) {
|
|
2862
|
-
spinner.fail(
|
|
2863
|
-
chalk4.red(
|
|
2864
|
-
`Compiled theme not found for ${themeId}@${version}. Run 'onexthm build' first.`
|
|
2865
|
-
)
|
|
2866
|
-
);
|
|
2867
|
-
logger.info(chalk4.gray(`Expected location:
|
|
2868
|
-
- ./dist/`));
|
|
2869
|
-
process.exit(1);
|
|
2870
|
-
}
|
|
2871
|
-
spinner.succeed(`Found compiled theme at: ${compiledDir}`);
|
|
2872
|
-
spinner.start("Creating bundle.zip...");
|
|
2873
|
-
const tmpDir = os.tmpdir();
|
|
2874
|
-
const bundleZipPath = path8.join(tmpDir, `${themeId}-${version}-bundle.zip`);
|
|
2875
|
-
await createZipFromDir(compiledDir, bundleZipPath);
|
|
2876
|
-
const bundleZipBuffer = await fs.readFile(bundleZipPath);
|
|
2877
|
-
const bundleSizeMB = (bundleZipBuffer.length / 1024 / 1024).toFixed(2);
|
|
2878
|
-
spinner.succeed(`Created bundle.zip (${bundleSizeMB} MB)`);
|
|
2879
|
-
if (options.dryRun) {
|
|
2880
|
-
spinner.info(chalk4.yellow("Dry run mode \u2014 no files will be uploaded"));
|
|
2881
|
-
console.log();
|
|
2882
|
-
console.log(chalk4.gray(` bundle.zip: ${bundleSizeMB} MB`));
|
|
2883
|
-
console.log(
|
|
2884
|
-
chalk4.cyan(
|
|
2885
|
-
` S3 path: s3://${bucket}/themes/${themeId}/${version}/bundle.zip`
|
|
2886
|
-
)
|
|
2887
|
-
);
|
|
2888
|
-
if (!options.skipSource) {
|
|
2889
|
-
const sourceDir = await findSourceDir(themeId, options.sourceDir);
|
|
2890
|
-
if (sourceDir) {
|
|
2891
|
-
console.log(chalk4.gray(` source dir: ${sourceDir}`));
|
|
2892
|
-
console.log(
|
|
2893
|
-
chalk4.cyan(
|
|
2894
|
-
` S3 path: s3://${bucket}/themes/${themeId}/${version}/source.zip`
|
|
2895
|
-
)
|
|
2896
|
-
);
|
|
2897
|
-
} else {
|
|
2898
|
-
console.log(
|
|
2899
|
-
chalk4.yellow(" source dir: not found (source.zip will be skipped)")
|
|
2900
|
-
);
|
|
2901
|
-
}
|
|
2902
|
-
}
|
|
2903
|
-
console.log();
|
|
2904
|
-
await fs.remove(bundleZipPath);
|
|
2905
|
-
return;
|
|
2906
|
-
}
|
|
2907
|
-
spinner.start("Uploading bundle.zip to S3...");
|
|
2908
|
-
const bundleS3Key = `themes/${themeId}/${version}/bundle.zip`;
|
|
2909
|
-
await s3Client.send(
|
|
2910
|
-
new PutObjectCommand({
|
|
2911
|
-
Bucket: bucket,
|
|
2912
|
-
Key: bundleS3Key,
|
|
2913
|
-
Body: bundleZipBuffer,
|
|
2914
|
-
ContentType: "application/zip"
|
|
2915
|
-
})
|
|
2916
|
-
);
|
|
2917
|
-
spinner.succeed(
|
|
2918
|
-
`Uploaded bundle.zip ${chalk4.gray(`\u2192 s3://${bucket}/${bundleS3Key}`)}`
|
|
2919
|
-
);
|
|
2920
|
-
await fs.remove(bundleZipPath);
|
|
2921
|
-
let sourceUploaded = false;
|
|
2922
|
-
if (!options.skipSource) {
|
|
2923
|
-
spinner.start("Looking for source directory...");
|
|
2924
|
-
const sourceDir = await findSourceDir(themeId, options.sourceDir);
|
|
2925
|
-
if (sourceDir) {
|
|
2926
|
-
spinner.succeed(`Found source at: ${sourceDir}`);
|
|
2927
|
-
spinner.start("Creating source.zip...");
|
|
2928
|
-
const sourceZipPath = path8.join(
|
|
2929
|
-
tmpDir,
|
|
2930
|
-
`${themeId}-${version}-source.zip`
|
|
2931
|
-
);
|
|
2932
|
-
await createZipFromDir(sourceDir, sourceZipPath, [
|
|
2933
|
-
"node_modules/**",
|
|
2934
|
-
"dist/**",
|
|
2935
|
-
".git/**",
|
|
2936
|
-
"*.zip",
|
|
2937
|
-
".next/**",
|
|
2938
|
-
".turbo/**"
|
|
2939
|
-
]);
|
|
2940
|
-
const sourceZipBuffer = await fs.readFile(sourceZipPath);
|
|
2941
|
-
const sourceSizeMB = (sourceZipBuffer.length / 1024 / 1024).toFixed(2);
|
|
2942
|
-
spinner.succeed(`Created source.zip (${sourceSizeMB} MB)`);
|
|
2943
|
-
spinner.start("Uploading source.zip to S3...");
|
|
2944
|
-
const sourceS3Key = `themes/${themeId}/${version}/source.zip`;
|
|
2945
|
-
await s3Client.send(
|
|
2946
|
-
new PutObjectCommand({
|
|
2947
|
-
Bucket: bucket,
|
|
2948
|
-
Key: sourceS3Key,
|
|
2949
|
-
Body: sourceZipBuffer,
|
|
2950
|
-
ContentType: "application/zip"
|
|
2951
|
-
})
|
|
2952
|
-
);
|
|
2953
|
-
spinner.succeed(
|
|
2954
|
-
`Uploaded source.zip ${chalk4.gray(`\u2192 s3://${bucket}/${sourceS3Key}`)}`
|
|
2955
|
-
);
|
|
2956
|
-
await fs.remove(sourceZipPath);
|
|
2957
|
-
sourceUploaded = true;
|
|
2958
|
-
} else {
|
|
2959
|
-
spinner.warn(
|
|
2960
|
-
chalk4.yellow("Source directory not found \u2014 skipping source.zip")
|
|
2961
|
-
);
|
|
2962
|
-
}
|
|
2963
|
-
}
|
|
2964
|
-
spinner.start("Updating latest.json pointer...");
|
|
2965
|
-
await updateLatestPointer(s3Client, bucket, themeId, version);
|
|
2966
|
-
spinner.succeed("Updated latest.json pointer");
|
|
2967
|
-
console.log();
|
|
2968
|
-
logger.success(chalk4.green.bold("Theme uploaded successfully!"));
|
|
2969
|
-
console.log();
|
|
2970
|
-
console.log(
|
|
2971
|
-
chalk4.cyan(" Theme: ") + chalk4.white(`${themeId}@${version}`)
|
|
2972
|
-
);
|
|
2973
|
-
console.log(chalk4.cyan(" Bucket: ") + chalk4.white(bucket));
|
|
2974
|
-
console.log(
|
|
2975
|
-
chalk4.cyan(" Files: ") + chalk4.white(`bundle.zip${sourceUploaded ? " + source.zip" : ""}`)
|
|
2976
|
-
);
|
|
2977
|
-
console.log(
|
|
2978
|
-
chalk4.cyan(" Path: ") + chalk4.gray(`s3://${bucket}/themes/${themeId}/${version}/`)
|
|
2979
|
-
);
|
|
2980
|
-
console.log();
|
|
2981
|
-
} catch (error) {
|
|
2982
|
-
spinner.fail(chalk4.red(`Upload failed: ${error.message}`));
|
|
2983
|
-
logger.error(error.stack || error.message);
|
|
2984
|
-
process.exit(1);
|
|
2985
|
-
}
|
|
2831
|
+
console.log(
|
|
2832
|
+
"command can no longer reach the bucket. Use `onexthm publish` instead:"
|
|
2833
|
+
);
|
|
2834
|
+
console.log();
|
|
2835
|
+
console.log(chalk4.cyan(" cd themes/your-theme"));
|
|
2836
|
+
console.log(chalk4.cyan(" onexthm login # one-time, refreshes JWT"));
|
|
2837
|
+
console.log(
|
|
2838
|
+
chalk4.cyan(" onexthm publish # builds + uploads + confirms")
|
|
2839
|
+
);
|
|
2840
|
+
console.log();
|
|
2841
|
+
console.log(
|
|
2842
|
+
"`publish` does everything this command did (build, version bump,"
|
|
2843
|
+
);
|
|
2844
|
+
console.log(
|
|
2845
|
+
"bundle + source upload) plus content-hashed asset upload, security"
|
|
2846
|
+
);
|
|
2847
|
+
console.log("scanning, and atomic version registration in one step.");
|
|
2848
|
+
console.log();
|
|
2849
|
+
process.exit(1);
|
|
2986
2850
|
}
|
|
2987
2851
|
|
|
2988
2852
|
// src/commands/download.ts
|
|
2989
2853
|
init_logger();
|
|
2990
|
-
function
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
const endpoint = process.env.MINIO_ENDPOINT || "localhost:9000";
|
|
2994
|
-
const secure = process.env.MINIO_SECURE === "true";
|
|
2995
|
-
const endpointUrl = endpoint.startsWith("http") ? endpoint : `${secure ? "https" : "http"}://${endpoint}`;
|
|
2996
|
-
return new S3Client({
|
|
2997
|
-
endpoint: endpointUrl,
|
|
2998
|
-
region: "us-east-1",
|
|
2999
|
-
credentials: {
|
|
3000
|
-
accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
|
|
3001
|
-
secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
|
|
3002
|
-
},
|
|
3003
|
-
forcePathStyle: true
|
|
3004
|
-
});
|
|
3005
|
-
}
|
|
3006
|
-
if (adapterMode === "local") {
|
|
3007
|
-
return new S3Client({
|
|
3008
|
-
endpoint: "http://localhost:4569",
|
|
3009
|
-
region: "ap-southeast-1",
|
|
3010
|
-
credentials: {
|
|
3011
|
-
accessKeyId: "S3RVER",
|
|
3012
|
-
secretAccessKey: "S3RVER"
|
|
3013
|
-
},
|
|
3014
|
-
forcePathStyle: true
|
|
3015
|
-
});
|
|
2854
|
+
function unwrapEnvelope(raw) {
|
|
2855
|
+
if (raw && typeof raw === "object" && "statusCode" in raw && "body" in raw) {
|
|
2856
|
+
return raw.body;
|
|
3016
2857
|
}
|
|
3017
|
-
return
|
|
3018
|
-
region: process.env.AWS_REGION || "ap-southeast-1"
|
|
3019
|
-
});
|
|
2858
|
+
return raw;
|
|
3020
2859
|
}
|
|
3021
|
-
function
|
|
3022
|
-
|
|
3023
|
-
|
|
2860
|
+
async function resolveLatestVersion(apiUrl, themeId) {
|
|
2861
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`;
|
|
2862
|
+
let response;
|
|
2863
|
+
try {
|
|
2864
|
+
response = await fetch(url, { cache: "no-store" });
|
|
2865
|
+
} catch (err) {
|
|
2866
|
+
throw new Error(
|
|
2867
|
+
`Network error contacting ${url}: ${err instanceof Error ? err.message : "unknown"}`
|
|
2868
|
+
);
|
|
3024
2869
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
}
|
|
3028
|
-
|
|
3029
|
-
const chunks = [];
|
|
3030
|
-
for await (const chunk of stream) {
|
|
3031
|
-
chunks.push(Buffer.from(chunk));
|
|
2870
|
+
if (!response.ok) {
|
|
2871
|
+
throw new Error(
|
|
2872
|
+
`Version lookup failed for "${themeId}" (HTTP ${response.status})`
|
|
2873
|
+
);
|
|
3032
2874
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
2875
|
+
const raw = await response.json();
|
|
2876
|
+
const data = unwrapEnvelope(raw);
|
|
2877
|
+
const latest = data?.latest_version;
|
|
2878
|
+
if (typeof latest !== "string" || latest.length === 0) {
|
|
2879
|
+
throw new Error(
|
|
2880
|
+
`Theme "${themeId}" has no published versions yet (no latest_version in response)`
|
|
2881
|
+
);
|
|
3039
2882
|
}
|
|
3040
|
-
return
|
|
2883
|
+
return latest;
|
|
3041
2884
|
}
|
|
3042
|
-
async function
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
Bucket: bucket,
|
|
3047
|
-
Key: `themes/${themeId}/latest.json`
|
|
3048
|
-
})
|
|
3049
|
-
);
|
|
3050
|
-
const body = await streamToString(response.Body);
|
|
3051
|
-
const data = JSON.parse(body);
|
|
3052
|
-
return data.version;
|
|
3053
|
-
} catch (error) {
|
|
2885
|
+
async function downloadBundleZip(apiUrl, themeId, version) {
|
|
2886
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/${encodeURIComponent(version)}/download`;
|
|
2887
|
+
const response = await fetch(url);
|
|
2888
|
+
if (!response.ok) {
|
|
3054
2889
|
throw new Error(
|
|
3055
|
-
`
|
|
2890
|
+
`Bundle download failed for "${themeId}@${version}" (HTTP ${response.status})`
|
|
3056
2891
|
);
|
|
3057
2892
|
}
|
|
2893
|
+
const contentType = response.headers.get("content-type") || "";
|
|
2894
|
+
if (contentType.includes("application/json")) {
|
|
2895
|
+
const raw = await response.json();
|
|
2896
|
+
const envelope = raw && typeof raw === "object" && "statusCode" in raw ? raw : { body: raw };
|
|
2897
|
+
const body = envelope.body;
|
|
2898
|
+
if (typeof body !== "string") {
|
|
2899
|
+
throw new Error(
|
|
2900
|
+
"Unexpected /download response shape: expected base64 string in body"
|
|
2901
|
+
);
|
|
2902
|
+
}
|
|
2903
|
+
return Buffer.from(body, "base64");
|
|
2904
|
+
}
|
|
2905
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
2906
|
+
return Buffer.from(arrayBuffer);
|
|
3058
2907
|
}
|
|
3059
2908
|
async function createCompatibilityFiles(outputDir, manifest) {
|
|
3060
2909
|
const entryFile = manifest.output?.entry || "bundle-entry.js";
|
|
@@ -3078,47 +2927,58 @@ export * from './bundle-entry.js';
|
|
|
3078
2927
|
const pkgJsonPath = path8.join(outputDir, "package.json");
|
|
3079
2928
|
await fs.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
|
|
3080
2929
|
}
|
|
3081
|
-
function showDownloadFailureHelp(themeId,
|
|
2930
|
+
function showDownloadFailureHelp(themeId, apiUrl) {
|
|
3082
2931
|
console.log();
|
|
3083
2932
|
logger.error(chalk4.red.bold("Theme download failed"));
|
|
3084
2933
|
console.log();
|
|
3085
2934
|
console.log(chalk4.yellow("Possible reasons:"));
|
|
3086
|
-
console.log(
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
console.log(
|
|
2935
|
+
console.log(
|
|
2936
|
+
chalk4.gray(" 1. Theme has not been published yet (run `onexthm publish`)")
|
|
2937
|
+
);
|
|
2938
|
+
console.log(
|
|
2939
|
+
chalk4.gray(
|
|
2940
|
+
" 2. Theme ID is wrong (typo in --theme-id or THEME_ID env var)"
|
|
2941
|
+
)
|
|
2942
|
+
);
|
|
2943
|
+
console.log(
|
|
2944
|
+
chalk4.gray(" 3. API base URL is wrong or the website-api is unreachable")
|
|
2945
|
+
);
|
|
3090
2946
|
console.log();
|
|
3091
2947
|
console.log(chalk4.cyan.bold("To fix this:"));
|
|
3092
2948
|
console.log();
|
|
3093
|
-
console.log(chalk4.white("1.
|
|
3094
|
-
console.log(chalk4.gray(` cd themes/${themeId}`));
|
|
3095
|
-
console.log(chalk4.gray(" pnpm build"));
|
|
3096
|
-
console.log(chalk4.gray(" onexthm upload"));
|
|
3097
|
-
console.log();
|
|
3098
|
-
console.log(chalk4.white("2. Verify AWS credentials are set:"));
|
|
2949
|
+
console.log(chalk4.white("1. Verify the theme is published:"));
|
|
3099
2950
|
console.log(
|
|
3100
|
-
chalk4.gray(
|
|
2951
|
+
chalk4.gray(
|
|
2952
|
+
` curl -s ${apiUrl}/website-api/themes/${themeId}/versions | jq .latest_version`
|
|
2953
|
+
)
|
|
3101
2954
|
);
|
|
3102
|
-
console.log(chalk4.gray(" - Or use AWS_PROFILE=your-profile"));
|
|
3103
|
-
console.log(chalk4.gray(" - Set AWS_REGION (e.g., ap-southeast-1)"));
|
|
3104
2955
|
console.log();
|
|
3105
|
-
console.log(chalk4.white("
|
|
3106
|
-
console.log(chalk4.gray(` Current
|
|
2956
|
+
console.log(chalk4.white("2. Check API URL configuration:"));
|
|
2957
|
+
console.log(chalk4.gray(` Current API URL: ${apiUrl}`));
|
|
3107
2958
|
console.log(
|
|
3108
|
-
chalk4.gray("
|
|
2959
|
+
chalk4.gray(" Override with NEXT_PUBLIC_API_URL or ONEXTHM_API_URL")
|
|
3109
2960
|
);
|
|
3110
2961
|
console.log();
|
|
3111
|
-
console.log(chalk4.white("
|
|
3112
|
-
console.log(
|
|
2962
|
+
console.log(chalk4.white("3. Pin a specific version (CI/production):"));
|
|
2963
|
+
console.log(
|
|
2964
|
+
chalk4.gray(` THEME_VERSION=1.2.3 onexthm download --theme-id ${themeId}`)
|
|
2965
|
+
);
|
|
3113
2966
|
console.log();
|
|
3114
2967
|
}
|
|
3115
2968
|
async function downloadCommand(options) {
|
|
3116
|
-
logger.header("Download Theme
|
|
2969
|
+
logger.header("Download Theme");
|
|
3117
2970
|
const spinner = ora("Initializing download...").start();
|
|
2971
|
+
if (options.bucket || options.environment) {
|
|
2972
|
+
spinner.stop();
|
|
2973
|
+
logger.warning(
|
|
2974
|
+
"--bucket and --environment are deprecated and ignored. Themes are now served via HTTP from the website-api Lambda."
|
|
2975
|
+
);
|
|
2976
|
+
spinner.start();
|
|
2977
|
+
}
|
|
2978
|
+
const apiUrl = getApiUrl();
|
|
3118
2979
|
try {
|
|
3119
2980
|
const themeId = options.themeId || process.env.NEXT_PUBLIC_THEME_ID || process.env.THEME_ID;
|
|
3120
|
-
const
|
|
3121
|
-
const bucket = options.bucket || getBucketName2(options.environment);
|
|
2981
|
+
const requestedVersion = options.version || process.env.THEME_VERSION || "latest";
|
|
3122
2982
|
const outputDir = options.output || "./active-theme";
|
|
3123
2983
|
if (!themeId) {
|
|
3124
2984
|
spinner.fail(
|
|
@@ -3128,12 +2988,10 @@ async function downloadCommand(options) {
|
|
|
3128
2988
|
);
|
|
3129
2989
|
process.exit(1);
|
|
3130
2990
|
}
|
|
3131
|
-
spinner.text = `
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
spinner.text = "Resolving latest version...";
|
|
3136
|
-
resolvedVersion = await resolveLatestVersion(s3Client, bucket, themeId);
|
|
2991
|
+
spinner.text = `Resolving ${themeId}@${requestedVersion}...`;
|
|
2992
|
+
let resolvedVersion = requestedVersion;
|
|
2993
|
+
if (requestedVersion === "latest") {
|
|
2994
|
+
resolvedVersion = await resolveLatestVersion(apiUrl, themeId);
|
|
3137
2995
|
spinner.succeed(
|
|
3138
2996
|
`Resolved latest version: ${chalk4.cyan(resolvedVersion)}`
|
|
3139
2997
|
);
|
|
@@ -3143,24 +3001,19 @@ async function downloadCommand(options) {
|
|
|
3143
3001
|
chalk4.yellow(
|
|
3144
3002
|
`
|
|
3145
3003
|
Warning: Resolved "latest" to ${resolvedVersion} in CI environment.
|
|
3146
|
-
For
|
|
3004
|
+
For reproducible builds, pin to a specific version:
|
|
3147
3005
|
THEME_VERSION=${resolvedVersion}
|
|
3148
3006
|
`
|
|
3149
3007
|
)
|
|
3150
3008
|
);
|
|
3151
3009
|
}
|
|
3010
|
+
} else {
|
|
3011
|
+
spinner.succeed(`Using version: ${chalk4.cyan(resolvedVersion)}`);
|
|
3152
3012
|
}
|
|
3153
3013
|
spinner.start(
|
|
3154
3014
|
`Downloading bundle.zip for ${themeId}@${resolvedVersion}...`
|
|
3155
3015
|
);
|
|
3156
|
-
const
|
|
3157
|
-
const response = await s3Client.send(
|
|
3158
|
-
new GetObjectCommand({
|
|
3159
|
-
Bucket: bucket,
|
|
3160
|
-
Key: s3Key
|
|
3161
|
-
})
|
|
3162
|
-
);
|
|
3163
|
-
const zipBuffer = await streamToBuffer(response.Body);
|
|
3016
|
+
const zipBuffer = await downloadBundleZip(apiUrl, themeId, resolvedVersion);
|
|
3164
3017
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
3165
3018
|
spinner.succeed(`Downloaded bundle.zip (${sizeMB} MB)`);
|
|
3166
3019
|
spinner.start("Extracting bundle...");
|
|
@@ -3179,7 +3032,7 @@ async function downloadCommand(options) {
|
|
|
3179
3032
|
console.log(
|
|
3180
3033
|
chalk4.cyan(" Theme: ") + chalk4.white(`${themeId}@${resolvedVersion}`)
|
|
3181
3034
|
);
|
|
3182
|
-
console.log(chalk4.cyan("
|
|
3035
|
+
console.log(chalk4.cyan(" Source: ") + chalk4.white(apiUrl));
|
|
3183
3036
|
console.log(chalk4.cyan(" Output: ") + chalk4.white(outputDir));
|
|
3184
3037
|
console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
|
|
3185
3038
|
if (manifest.counts) {
|
|
@@ -3191,82 +3044,70 @@ async function downloadCommand(options) {
|
|
|
3191
3044
|
} catch (error) {
|
|
3192
3045
|
spinner.fail(chalk4.red("Download failed"));
|
|
3193
3046
|
logger.error(error.message);
|
|
3194
|
-
const themeId = options.themeId || process.env.NEXT_PUBLIC_THEME_ID || "unknown";
|
|
3195
|
-
|
|
3196
|
-
showDownloadFailureHelp(themeId, bucket);
|
|
3047
|
+
const themeId = options.themeId || process.env.NEXT_PUBLIC_THEME_ID || process.env.THEME_ID || "unknown";
|
|
3048
|
+
showDownloadFailureHelp(themeId, apiUrl);
|
|
3197
3049
|
process.exit(1);
|
|
3198
3050
|
}
|
|
3199
3051
|
}
|
|
3200
3052
|
|
|
3201
3053
|
// src/commands/clone.ts
|
|
3202
3054
|
init_logger();
|
|
3203
|
-
function
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
const endpoint = process.env.MINIO_ENDPOINT || "localhost:9000";
|
|
3207
|
-
const secure = process.env.MINIO_SECURE === "true";
|
|
3208
|
-
const endpointUrl = endpoint.startsWith("http") ? endpoint : `${secure ? "https" : "http"}://${endpoint}`;
|
|
3209
|
-
return new S3Client({
|
|
3210
|
-
endpoint: endpointUrl,
|
|
3211
|
-
region: "us-east-1",
|
|
3212
|
-
credentials: {
|
|
3213
|
-
accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
|
|
3214
|
-
secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
|
|
3215
|
-
},
|
|
3216
|
-
forcePathStyle: true
|
|
3217
|
-
});
|
|
3218
|
-
}
|
|
3219
|
-
if (adapterMode === "local") {
|
|
3220
|
-
return new S3Client({
|
|
3221
|
-
endpoint: "http://localhost:4569",
|
|
3222
|
-
region: "ap-southeast-1",
|
|
3223
|
-
credentials: {
|
|
3224
|
-
accessKeyId: "S3RVER",
|
|
3225
|
-
secretAccessKey: "S3RVER"
|
|
3226
|
-
},
|
|
3227
|
-
forcePathStyle: true
|
|
3228
|
-
});
|
|
3229
|
-
}
|
|
3230
|
-
return new S3Client({
|
|
3231
|
-
region: process.env.AWS_REGION || "ap-southeast-1"
|
|
3232
|
-
});
|
|
3233
|
-
}
|
|
3234
|
-
function getBucketName3(env) {
|
|
3235
|
-
if (process.env.BUCKET_NAME) {
|
|
3236
|
-
return process.env.BUCKET_NAME;
|
|
3055
|
+
function unwrapEnvelope2(raw) {
|
|
3056
|
+
if (raw && typeof raw === "object" && "statusCode" in raw && "body" in raw) {
|
|
3057
|
+
return raw.body;
|
|
3237
3058
|
}
|
|
3238
|
-
|
|
3239
|
-
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3059
|
+
return raw;
|
|
3240
3060
|
}
|
|
3241
|
-
async function
|
|
3242
|
-
const
|
|
3243
|
-
|
|
3244
|
-
|
|
3061
|
+
async function resolveLatestVersion2(apiUrl, themeId) {
|
|
3062
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`;
|
|
3063
|
+
const response = await fetch(url, { cache: "no-store" });
|
|
3064
|
+
if (!response.ok) {
|
|
3065
|
+
throw new Error(
|
|
3066
|
+
`Version lookup failed for "${themeId}" (HTTP ${response.status})`
|
|
3067
|
+
);
|
|
3245
3068
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
chunks.push(Buffer.from(chunk));
|
|
3069
|
+
const raw = await response.json();
|
|
3070
|
+
const data = unwrapEnvelope2(raw);
|
|
3071
|
+
const latest = data?.latest_version;
|
|
3072
|
+
if (typeof latest !== "string" || latest.length === 0) {
|
|
3073
|
+
throw new Error(`Theme "${themeId}" has no published versions yet`);
|
|
3252
3074
|
}
|
|
3253
|
-
return
|
|
3075
|
+
return latest;
|
|
3254
3076
|
}
|
|
3255
|
-
async function
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3077
|
+
async function fetchSourceZip(apiUrl, themeId, version) {
|
|
3078
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/source?version=${encodeURIComponent(version)}`;
|
|
3079
|
+
const response = await authenticatedFetch(url, {
|
|
3080
|
+
method: "GET"
|
|
3081
|
+
});
|
|
3082
|
+
if (!response.ok) {
|
|
3083
|
+
if (response.status === 404) {
|
|
3084
|
+
throw new Error(
|
|
3085
|
+
`Source not found for ${themeId}@${version}. The theme may not have been published with source upload enabled.`
|
|
3086
|
+
);
|
|
3087
|
+
}
|
|
3088
|
+
if (response.status === 401 || response.status === 403) {
|
|
3089
|
+
throw new Error(
|
|
3090
|
+
`Not authorized to download source for "${themeId}". Run \`onexthm login\` first.`
|
|
3091
|
+
);
|
|
3092
|
+
}
|
|
3093
|
+
throw new Error(
|
|
3094
|
+
`Source URL request failed for "${themeId}@${version}" (HTTP ${response.status})`
|
|
3262
3095
|
);
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3096
|
+
}
|
|
3097
|
+
const raw = await response.json();
|
|
3098
|
+
const data = unwrapEnvelope2(raw);
|
|
3099
|
+
const presignedUrl = data?.download_url;
|
|
3100
|
+
if (typeof presignedUrl !== "string" || presignedUrl.length === 0) {
|
|
3101
|
+
throw new Error("Unexpected /source response shape: missing download_url");
|
|
3102
|
+
}
|
|
3103
|
+
const zipResponse = await fetch(presignedUrl);
|
|
3104
|
+
if (!zipResponse.ok) {
|
|
3266
3105
|
throw new Error(
|
|
3267
|
-
`
|
|
3106
|
+
`Presigned source download failed (HTTP ${zipResponse.status})`
|
|
3268
3107
|
);
|
|
3269
3108
|
}
|
|
3109
|
+
const arrayBuffer = await zipResponse.arrayBuffer();
|
|
3110
|
+
return Buffer.from(arrayBuffer);
|
|
3270
3111
|
}
|
|
3271
3112
|
function runInstall(cwd) {
|
|
3272
3113
|
return new Promise((resolve) => {
|
|
@@ -3365,15 +3206,24 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3365
3206
|
}
|
|
3366
3207
|
async function cloneCommand(themeName, options) {
|
|
3367
3208
|
logger.header("Clone Theme Source");
|
|
3209
|
+
if (options.bucket || options.environment) {
|
|
3210
|
+
logger.warning(
|
|
3211
|
+
"--bucket and --environment are deprecated and ignored. Source is now fetched via HTTP from the website-api Lambda."
|
|
3212
|
+
);
|
|
3213
|
+
}
|
|
3214
|
+
const tokens = await getValidTokens();
|
|
3215
|
+
if (!tokens) {
|
|
3216
|
+
logger.error("Not logged in. Run: onexthm login");
|
|
3217
|
+
process.exit(1);
|
|
3218
|
+
}
|
|
3368
3219
|
let newName = options.name;
|
|
3369
3220
|
if (!newName) {
|
|
3370
3221
|
newName = await promptThemeName(themeName);
|
|
3371
3222
|
}
|
|
3372
3223
|
const spinner = ora("Initializing clone...").start();
|
|
3373
3224
|
try {
|
|
3374
|
-
const
|
|
3225
|
+
const apiUrl = getApiUrl();
|
|
3375
3226
|
const outputDir = options.output || path8.resolve(process.cwd(), newName);
|
|
3376
|
-
const s3Client = getS3Client3();
|
|
3377
3227
|
if (await fs.pathExists(outputDir)) {
|
|
3378
3228
|
spinner.fail(chalk4.red(`Directory already exists: ${outputDir}`));
|
|
3379
3229
|
logger.info(
|
|
@@ -3386,28 +3236,20 @@ async function cloneCommand(themeName, options) {
|
|
|
3386
3236
|
let version = options.version || "latest";
|
|
3387
3237
|
if (version === "latest") {
|
|
3388
3238
|
spinner.text = "Resolving latest version...";
|
|
3389
|
-
version = await resolveLatestVersion2(
|
|
3239
|
+
version = await resolveLatestVersion2(apiUrl, themeName);
|
|
3390
3240
|
spinner.succeed(`Resolved latest version: ${chalk4.cyan(version)}`);
|
|
3391
3241
|
}
|
|
3392
3242
|
spinner.start(`Downloading source.zip for ${themeName}@${version}...`);
|
|
3393
|
-
const s3Key = `themes/${themeName}/${version}/source.zip`;
|
|
3394
3243
|
let zipBuffer;
|
|
3395
3244
|
try {
|
|
3396
|
-
|
|
3397
|
-
new GetObjectCommand({
|
|
3398
|
-
Bucket: bucket,
|
|
3399
|
-
Key: s3Key
|
|
3400
|
-
})
|
|
3401
|
-
);
|
|
3402
|
-
zipBuffer = await streamToBuffer2(response.Body);
|
|
3245
|
+
zipBuffer = await fetchSourceZip(apiUrl, themeName, version);
|
|
3403
3246
|
} catch (error) {
|
|
3404
|
-
spinner.fail(chalk4.red(
|
|
3247
|
+
spinner.fail(chalk4.red(error.message));
|
|
3405
3248
|
console.log();
|
|
3406
3249
|
console.log(
|
|
3407
|
-
chalk4.
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
chalk4.gray(`Upload source with: onexthm upload --theme ${themeName}`)
|
|
3250
|
+
chalk4.gray(
|
|
3251
|
+
`Verify the theme is published: curl -s ${apiUrl}/website-api/themes/${themeName}/versions`
|
|
3252
|
+
)
|
|
3411
3253
|
);
|
|
3412
3254
|
console.log();
|
|
3413
3255
|
process.exit(1);
|