@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/cli.js
CHANGED
|
@@ -20,7 +20,6 @@ var inquirer = require('inquirer');
|
|
|
20
20
|
var archiver = require('archiver');
|
|
21
21
|
var FormData = require('form-data');
|
|
22
22
|
var fetch2 = require('node-fetch');
|
|
23
|
-
var clientS3 = require('@aws-sdk/client-s3');
|
|
24
23
|
var AdmZip = require('adm-zip');
|
|
25
24
|
var chokidar = require('chokidar');
|
|
26
25
|
var http = require('http');
|
|
@@ -3486,343 +3485,94 @@ async function deployCommand(options) {
|
|
|
3486
3485
|
|
|
3487
3486
|
// src/commands/upload.ts
|
|
3488
3487
|
init_logger();
|
|
3489
|
-
function
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
endpoint: endpointUrl,
|
|
3497
|
-
region: "us-east-1",
|
|
3498
|
-
credentials: {
|
|
3499
|
-
accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
|
|
3500
|
-
secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
|
|
3501
|
-
},
|
|
3502
|
-
forcePathStyle: true
|
|
3503
|
-
});
|
|
3504
|
-
}
|
|
3505
|
-
if (adapterMode === "local") {
|
|
3506
|
-
return new clientS3.S3Client({
|
|
3507
|
-
endpoint: "http://localhost:4569",
|
|
3508
|
-
region: "ap-southeast-1",
|
|
3509
|
-
credentials: {
|
|
3510
|
-
accessKeyId: "S3RVER",
|
|
3511
|
-
secretAccessKey: "S3RVER"
|
|
3512
|
-
},
|
|
3513
|
-
forcePathStyle: true
|
|
3514
|
-
});
|
|
3515
|
-
}
|
|
3516
|
-
return new clientS3.S3Client({
|
|
3517
|
-
region: process.env.AWS_REGION || "ap-southeast-1"
|
|
3518
|
-
});
|
|
3519
|
-
}
|
|
3520
|
-
function getBucketName(env) {
|
|
3521
|
-
if (process.env.BUCKET_NAME) {
|
|
3522
|
-
return process.env.BUCKET_NAME;
|
|
3523
|
-
}
|
|
3524
|
-
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
3525
|
-
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3526
|
-
}
|
|
3527
|
-
async function findCompiledThemeDir(themeId, version2) {
|
|
3528
|
-
const searchPaths = [path9__default.default.resolve(process.cwd(), "dist")];
|
|
3529
|
-
for (const dir of searchPaths) {
|
|
3530
|
-
if (await fs__default.default.pathExists(dir)) {
|
|
3531
|
-
const hasManifest = await fs__default.default.pathExists(path9__default.default.join(dir, "manifest.json"));
|
|
3532
|
-
const hasThemeEntry = await fs__default.default.pathExists(path9__default.default.join(dir, "bundle-entry.js")) || await fs__default.default.pathExists(path9__default.default.join(dir, "theme.config.js")) || await fs__default.default.pathExists(path9__default.default.join(dir, "index.js"));
|
|
3533
|
-
if (hasManifest || hasThemeEntry) {
|
|
3534
|
-
return dir;
|
|
3535
|
-
}
|
|
3536
|
-
}
|
|
3537
|
-
}
|
|
3538
|
-
return null;
|
|
3539
|
-
}
|
|
3540
|
-
async function readManifest() {
|
|
3541
|
-
const manifestTsPath = path9__default.default.resolve(process.cwd(), "manifest.ts");
|
|
3542
|
-
if (await fs__default.default.pathExists(manifestTsPath)) {
|
|
3543
|
-
try {
|
|
3544
|
-
const module = await import(manifestTsPath);
|
|
3545
|
-
return module.default || module;
|
|
3546
|
-
} catch (error) {
|
|
3547
|
-
logger.warning("Failed to import manifest.ts, trying package.json");
|
|
3548
|
-
}
|
|
3549
|
-
}
|
|
3550
|
-
const packageJsonPath = path9__default.default.resolve(process.cwd(), "package.json");
|
|
3551
|
-
if (await fs__default.default.pathExists(packageJsonPath)) {
|
|
3552
|
-
const pkg = await fs__default.default.readJson(packageJsonPath);
|
|
3553
|
-
return {
|
|
3554
|
-
themeId: pkg.name?.replace("@onex-themes/", "") || "unknown",
|
|
3555
|
-
version: pkg.version || "1.0.0"
|
|
3556
|
-
};
|
|
3557
|
-
}
|
|
3558
|
-
throw new Error(
|
|
3559
|
-
"No manifest.ts or package.json found. Are you in a theme directory?"
|
|
3488
|
+
async function uploadCommand(_options) {
|
|
3489
|
+
logger.header("Upload Theme to S3 \u2014 DEPRECATED");
|
|
3490
|
+
console.log();
|
|
3491
|
+
console.log(
|
|
3492
|
+
chalk4__default.default.yellow.bold(
|
|
3493
|
+
"`onexthm upload` is deprecated and no longer functional."
|
|
3494
|
+
)
|
|
3560
3495
|
);
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
const output = fs__default.default.createWriteStream(outputPath);
|
|
3565
|
-
const archive = archiver__default.default("zip", { zlib: { level: 6 } });
|
|
3566
|
-
output.on("close", () => resolve());
|
|
3567
|
-
archive.on("error", (err) => reject(err));
|
|
3568
|
-
archive.pipe(output);
|
|
3569
|
-
archive.glob("**/*", {
|
|
3570
|
-
cwd: sourceDir,
|
|
3571
|
-
dot: true,
|
|
3572
|
-
ignore: excludePatterns
|
|
3573
|
-
});
|
|
3574
|
-
archive.finalize();
|
|
3575
|
-
});
|
|
3576
|
-
}
|
|
3577
|
-
async function findSourceDir(themeId, explicitDir) {
|
|
3578
|
-
if (explicitDir) {
|
|
3579
|
-
if (await fs__default.default.pathExists(explicitDir)) return explicitDir;
|
|
3580
|
-
return null;
|
|
3581
|
-
}
|
|
3582
|
-
const searchPaths = [
|
|
3583
|
-
process.cwd(),
|
|
3584
|
-
path9__default.default.resolve(process.cwd(), `../../themes/${themeId}`),
|
|
3585
|
-
path9__default.default.resolve(process.cwd(), `../themes/${themeId}`)
|
|
3586
|
-
];
|
|
3587
|
-
const markers = ["theme.config.ts", "bundle-entry.ts"];
|
|
3588
|
-
for (const dir of searchPaths) {
|
|
3589
|
-
for (const marker of markers) {
|
|
3590
|
-
if (await fs__default.default.pathExists(path9__default.default.join(dir, marker))) {
|
|
3591
|
-
return dir;
|
|
3592
|
-
}
|
|
3593
|
-
}
|
|
3594
|
-
}
|
|
3595
|
-
return null;
|
|
3596
|
-
}
|
|
3597
|
-
async function updateLatestPointer(s3Client, bucket, themeId, version2) {
|
|
3598
|
-
const latestData = {
|
|
3599
|
-
version: version2,
|
|
3600
|
-
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3601
|
-
};
|
|
3602
|
-
await s3Client.send(
|
|
3603
|
-
new clientS3.PutObjectCommand({
|
|
3604
|
-
Bucket: bucket,
|
|
3605
|
-
Key: `themes/${themeId}/latest.json`,
|
|
3606
|
-
Body: JSON.stringify(latestData, null, 2),
|
|
3607
|
-
ContentType: "application/json"
|
|
3608
|
-
})
|
|
3496
|
+
console.log();
|
|
3497
|
+
console.log(
|
|
3498
|
+
"The platform no longer exposes themes via direct S3 access, so this"
|
|
3609
3499
|
);
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
if (!compiledDir) {
|
|
3630
|
-
spinner.fail(
|
|
3631
|
-
chalk4__default.default.red(
|
|
3632
|
-
`Compiled theme not found for ${themeId}@${version2}. Run 'onexthm build' first.`
|
|
3633
|
-
)
|
|
3634
|
-
);
|
|
3635
|
-
logger.info(chalk4__default.default.gray(`Expected location:
|
|
3636
|
-
- ./dist/`));
|
|
3637
|
-
process.exit(1);
|
|
3638
|
-
}
|
|
3639
|
-
spinner.succeed(`Found compiled theme at: ${compiledDir}`);
|
|
3640
|
-
spinner.start("Creating bundle.zip...");
|
|
3641
|
-
const tmpDir = os__default.default.tmpdir();
|
|
3642
|
-
const bundleZipPath = path9__default.default.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
|
|
3643
|
-
await createZipFromDir(compiledDir, bundleZipPath);
|
|
3644
|
-
const bundleZipBuffer = await fs__default.default.readFile(bundleZipPath);
|
|
3645
|
-
const bundleSizeMB = (bundleZipBuffer.length / 1024 / 1024).toFixed(2);
|
|
3646
|
-
spinner.succeed(`Created bundle.zip (${bundleSizeMB} MB)`);
|
|
3647
|
-
if (options.dryRun) {
|
|
3648
|
-
spinner.info(chalk4__default.default.yellow("Dry run mode \u2014 no files will be uploaded"));
|
|
3649
|
-
console.log();
|
|
3650
|
-
console.log(chalk4__default.default.gray(` bundle.zip: ${bundleSizeMB} MB`));
|
|
3651
|
-
console.log(
|
|
3652
|
-
chalk4__default.default.cyan(
|
|
3653
|
-
` S3 path: s3://${bucket}/themes/${themeId}/${version2}/bundle.zip`
|
|
3654
|
-
)
|
|
3655
|
-
);
|
|
3656
|
-
if (!options.skipSource) {
|
|
3657
|
-
const sourceDir = await findSourceDir(themeId, options.sourceDir);
|
|
3658
|
-
if (sourceDir) {
|
|
3659
|
-
console.log(chalk4__default.default.gray(` source dir: ${sourceDir}`));
|
|
3660
|
-
console.log(
|
|
3661
|
-
chalk4__default.default.cyan(
|
|
3662
|
-
` S3 path: s3://${bucket}/themes/${themeId}/${version2}/source.zip`
|
|
3663
|
-
)
|
|
3664
|
-
);
|
|
3665
|
-
} else {
|
|
3666
|
-
console.log(
|
|
3667
|
-
chalk4__default.default.yellow(" source dir: not found (source.zip will be skipped)")
|
|
3668
|
-
);
|
|
3669
|
-
}
|
|
3670
|
-
}
|
|
3671
|
-
console.log();
|
|
3672
|
-
await fs__default.default.remove(bundleZipPath);
|
|
3673
|
-
return;
|
|
3674
|
-
}
|
|
3675
|
-
spinner.start("Uploading bundle.zip to S3...");
|
|
3676
|
-
const bundleS3Key = `themes/${themeId}/${version2}/bundle.zip`;
|
|
3677
|
-
await s3Client.send(
|
|
3678
|
-
new clientS3.PutObjectCommand({
|
|
3679
|
-
Bucket: bucket,
|
|
3680
|
-
Key: bundleS3Key,
|
|
3681
|
-
Body: bundleZipBuffer,
|
|
3682
|
-
ContentType: "application/zip"
|
|
3683
|
-
})
|
|
3684
|
-
);
|
|
3685
|
-
spinner.succeed(
|
|
3686
|
-
`Uploaded bundle.zip ${chalk4__default.default.gray(`\u2192 s3://${bucket}/${bundleS3Key}`)}`
|
|
3687
|
-
);
|
|
3688
|
-
await fs__default.default.remove(bundleZipPath);
|
|
3689
|
-
let sourceUploaded = false;
|
|
3690
|
-
if (!options.skipSource) {
|
|
3691
|
-
spinner.start("Looking for source directory...");
|
|
3692
|
-
const sourceDir = await findSourceDir(themeId, options.sourceDir);
|
|
3693
|
-
if (sourceDir) {
|
|
3694
|
-
spinner.succeed(`Found source at: ${sourceDir}`);
|
|
3695
|
-
spinner.start("Creating source.zip...");
|
|
3696
|
-
const sourceZipPath = path9__default.default.join(
|
|
3697
|
-
tmpDir,
|
|
3698
|
-
`${themeId}-${version2}-source.zip`
|
|
3699
|
-
);
|
|
3700
|
-
await createZipFromDir(sourceDir, sourceZipPath, [
|
|
3701
|
-
"node_modules/**",
|
|
3702
|
-
"dist/**",
|
|
3703
|
-
".git/**",
|
|
3704
|
-
"*.zip",
|
|
3705
|
-
".next/**",
|
|
3706
|
-
".turbo/**"
|
|
3707
|
-
]);
|
|
3708
|
-
const sourceZipBuffer = await fs__default.default.readFile(sourceZipPath);
|
|
3709
|
-
const sourceSizeMB = (sourceZipBuffer.length / 1024 / 1024).toFixed(2);
|
|
3710
|
-
spinner.succeed(`Created source.zip (${sourceSizeMB} MB)`);
|
|
3711
|
-
spinner.start("Uploading source.zip to S3...");
|
|
3712
|
-
const sourceS3Key = `themes/${themeId}/${version2}/source.zip`;
|
|
3713
|
-
await s3Client.send(
|
|
3714
|
-
new clientS3.PutObjectCommand({
|
|
3715
|
-
Bucket: bucket,
|
|
3716
|
-
Key: sourceS3Key,
|
|
3717
|
-
Body: sourceZipBuffer,
|
|
3718
|
-
ContentType: "application/zip"
|
|
3719
|
-
})
|
|
3720
|
-
);
|
|
3721
|
-
spinner.succeed(
|
|
3722
|
-
`Uploaded source.zip ${chalk4__default.default.gray(`\u2192 s3://${bucket}/${sourceS3Key}`)}`
|
|
3723
|
-
);
|
|
3724
|
-
await fs__default.default.remove(sourceZipPath);
|
|
3725
|
-
sourceUploaded = true;
|
|
3726
|
-
} else {
|
|
3727
|
-
spinner.warn(
|
|
3728
|
-
chalk4__default.default.yellow("Source directory not found \u2014 skipping source.zip")
|
|
3729
|
-
);
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
spinner.start("Updating latest.json pointer...");
|
|
3733
|
-
await updateLatestPointer(s3Client, bucket, themeId, version2);
|
|
3734
|
-
spinner.succeed("Updated latest.json pointer");
|
|
3735
|
-
console.log();
|
|
3736
|
-
logger.success(chalk4__default.default.green.bold("Theme uploaded successfully!"));
|
|
3737
|
-
console.log();
|
|
3738
|
-
console.log(
|
|
3739
|
-
chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(`${themeId}@${version2}`)
|
|
3740
|
-
);
|
|
3741
|
-
console.log(chalk4__default.default.cyan(" Bucket: ") + chalk4__default.default.white(bucket));
|
|
3742
|
-
console.log(
|
|
3743
|
-
chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(`bundle.zip${sourceUploaded ? " + source.zip" : ""}`)
|
|
3744
|
-
);
|
|
3745
|
-
console.log(
|
|
3746
|
-
chalk4__default.default.cyan(" Path: ") + chalk4__default.default.gray(`s3://${bucket}/themes/${themeId}/${version2}/`)
|
|
3747
|
-
);
|
|
3748
|
-
console.log();
|
|
3749
|
-
} catch (error) {
|
|
3750
|
-
spinner.fail(chalk4__default.default.red(`Upload failed: ${error.message}`));
|
|
3751
|
-
logger.error(error.stack || error.message);
|
|
3752
|
-
process.exit(1);
|
|
3753
|
-
}
|
|
3500
|
+
console.log(
|
|
3501
|
+
"command can no longer reach the bucket. Use `onexthm publish` instead:"
|
|
3502
|
+
);
|
|
3503
|
+
console.log();
|
|
3504
|
+
console.log(chalk4__default.default.cyan(" cd themes/your-theme"));
|
|
3505
|
+
console.log(chalk4__default.default.cyan(" onexthm login # one-time, refreshes JWT"));
|
|
3506
|
+
console.log(
|
|
3507
|
+
chalk4__default.default.cyan(" onexthm publish # builds + uploads + confirms")
|
|
3508
|
+
);
|
|
3509
|
+
console.log();
|
|
3510
|
+
console.log(
|
|
3511
|
+
"`publish` does everything this command did (build, version bump,"
|
|
3512
|
+
);
|
|
3513
|
+
console.log(
|
|
3514
|
+
"bundle + source upload) plus content-hashed asset upload, security"
|
|
3515
|
+
);
|
|
3516
|
+
console.log("scanning, and atomic version registration in one step.");
|
|
3517
|
+
console.log();
|
|
3518
|
+
process.exit(1);
|
|
3754
3519
|
}
|
|
3755
3520
|
|
|
3756
3521
|
// src/commands/download.ts
|
|
3757
3522
|
init_logger();
|
|
3758
|
-
function
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
const endpoint = process.env.MINIO_ENDPOINT || "localhost:9000";
|
|
3762
|
-
const secure = process.env.MINIO_SECURE === "true";
|
|
3763
|
-
const endpointUrl = endpoint.startsWith("http") ? endpoint : `${secure ? "https" : "http"}://${endpoint}`;
|
|
3764
|
-
return new clientS3.S3Client({
|
|
3765
|
-
endpoint: endpointUrl,
|
|
3766
|
-
region: "us-east-1",
|
|
3767
|
-
credentials: {
|
|
3768
|
-
accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
|
|
3769
|
-
secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
|
|
3770
|
-
},
|
|
3771
|
-
forcePathStyle: true
|
|
3772
|
-
});
|
|
3523
|
+
function unwrapEnvelope(raw) {
|
|
3524
|
+
if (raw && typeof raw === "object" && "statusCode" in raw && "body" in raw) {
|
|
3525
|
+
return raw.body;
|
|
3773
3526
|
}
|
|
3774
|
-
|
|
3775
|
-
return new clientS3.S3Client({
|
|
3776
|
-
endpoint: "http://localhost:4569",
|
|
3777
|
-
region: "ap-southeast-1",
|
|
3778
|
-
credentials: {
|
|
3779
|
-
accessKeyId: "S3RVER",
|
|
3780
|
-
secretAccessKey: "S3RVER"
|
|
3781
|
-
},
|
|
3782
|
-
forcePathStyle: true
|
|
3783
|
-
});
|
|
3784
|
-
}
|
|
3785
|
-
return new clientS3.S3Client({
|
|
3786
|
-
region: process.env.AWS_REGION || "ap-southeast-1"
|
|
3787
|
-
});
|
|
3527
|
+
return raw;
|
|
3788
3528
|
}
|
|
3789
|
-
function
|
|
3790
|
-
|
|
3791
|
-
|
|
3529
|
+
async function resolveLatestVersion(apiUrl, themeId) {
|
|
3530
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`;
|
|
3531
|
+
let response;
|
|
3532
|
+
try {
|
|
3533
|
+
response = await fetch(url, { cache: "no-store" });
|
|
3534
|
+
} catch (err) {
|
|
3535
|
+
throw new Error(
|
|
3536
|
+
`Network error contacting ${url}: ${err instanceof Error ? err.message : "unknown"}`
|
|
3537
|
+
);
|
|
3792
3538
|
}
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
}
|
|
3796
|
-
|
|
3797
|
-
const chunks = [];
|
|
3798
|
-
for await (const chunk of stream) {
|
|
3799
|
-
chunks.push(Buffer.from(chunk));
|
|
3539
|
+
if (!response.ok) {
|
|
3540
|
+
throw new Error(
|
|
3541
|
+
`Version lookup failed for "${themeId}" (HTTP ${response.status})`
|
|
3542
|
+
);
|
|
3800
3543
|
}
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3544
|
+
const raw = await response.json();
|
|
3545
|
+
const data = unwrapEnvelope(raw);
|
|
3546
|
+
const latest = data?.latest_version;
|
|
3547
|
+
if (typeof latest !== "string" || latest.length === 0) {
|
|
3548
|
+
throw new Error(
|
|
3549
|
+
`Theme "${themeId}" has no published versions yet (no latest_version in response)`
|
|
3550
|
+
);
|
|
3807
3551
|
}
|
|
3808
|
-
return
|
|
3552
|
+
return latest;
|
|
3809
3553
|
}
|
|
3810
|
-
async function
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
Bucket: bucket,
|
|
3815
|
-
Key: `themes/${themeId}/latest.json`
|
|
3816
|
-
})
|
|
3817
|
-
);
|
|
3818
|
-
const body = await streamToString(response.Body);
|
|
3819
|
-
const data = JSON.parse(body);
|
|
3820
|
-
return data.version;
|
|
3821
|
-
} catch (error) {
|
|
3554
|
+
async function downloadBundleZip(apiUrl, themeId, version2) {
|
|
3555
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/${encodeURIComponent(version2)}/download`;
|
|
3556
|
+
const response = await fetch(url);
|
|
3557
|
+
if (!response.ok) {
|
|
3822
3558
|
throw new Error(
|
|
3823
|
-
`
|
|
3559
|
+
`Bundle download failed for "${themeId}@${version2}" (HTTP ${response.status})`
|
|
3824
3560
|
);
|
|
3825
3561
|
}
|
|
3562
|
+
const contentType = response.headers.get("content-type") || "";
|
|
3563
|
+
if (contentType.includes("application/json")) {
|
|
3564
|
+
const raw = await response.json();
|
|
3565
|
+
const envelope = raw && typeof raw === "object" && "statusCode" in raw ? raw : { body: raw };
|
|
3566
|
+
const body = envelope.body;
|
|
3567
|
+
if (typeof body !== "string") {
|
|
3568
|
+
throw new Error(
|
|
3569
|
+
"Unexpected /download response shape: expected base64 string in body"
|
|
3570
|
+
);
|
|
3571
|
+
}
|
|
3572
|
+
return Buffer.from(body, "base64");
|
|
3573
|
+
}
|
|
3574
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
3575
|
+
return Buffer.from(arrayBuffer);
|
|
3826
3576
|
}
|
|
3827
3577
|
async function createCompatibilityFiles(outputDir, manifest) {
|
|
3828
3578
|
const entryFile = manifest.output?.entry || "bundle-entry.js";
|
|
@@ -3846,47 +3596,58 @@ export * from './bundle-entry.js';
|
|
|
3846
3596
|
const pkgJsonPath = path9__default.default.join(outputDir, "package.json");
|
|
3847
3597
|
await fs__default.default.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
|
|
3848
3598
|
}
|
|
3849
|
-
function showDownloadFailureHelp(themeId,
|
|
3599
|
+
function showDownloadFailureHelp(themeId, apiUrl) {
|
|
3850
3600
|
console.log();
|
|
3851
3601
|
logger.error(chalk4__default.default.red.bold("Theme download failed"));
|
|
3852
3602
|
console.log();
|
|
3853
3603
|
console.log(chalk4__default.default.yellow("Possible reasons:"));
|
|
3854
|
-
console.log(
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
console.log(
|
|
3604
|
+
console.log(
|
|
3605
|
+
chalk4__default.default.gray(" 1. Theme has not been published yet (run `onexthm publish`)")
|
|
3606
|
+
);
|
|
3607
|
+
console.log(
|
|
3608
|
+
chalk4__default.default.gray(
|
|
3609
|
+
" 2. Theme ID is wrong (typo in --theme-id or THEME_ID env var)"
|
|
3610
|
+
)
|
|
3611
|
+
);
|
|
3612
|
+
console.log(
|
|
3613
|
+
chalk4__default.default.gray(" 3. API base URL is wrong or the website-api is unreachable")
|
|
3614
|
+
);
|
|
3858
3615
|
console.log();
|
|
3859
3616
|
console.log(chalk4__default.default.cyan.bold("To fix this:"));
|
|
3860
3617
|
console.log();
|
|
3861
|
-
console.log(chalk4__default.default.white("1.
|
|
3862
|
-
console.log(chalk4__default.default.gray(` cd themes/${themeId}`));
|
|
3863
|
-
console.log(chalk4__default.default.gray(" pnpm build"));
|
|
3864
|
-
console.log(chalk4__default.default.gray(" onexthm upload"));
|
|
3865
|
-
console.log();
|
|
3866
|
-
console.log(chalk4__default.default.white("2. Verify AWS credentials are set:"));
|
|
3618
|
+
console.log(chalk4__default.default.white("1. Verify the theme is published:"));
|
|
3867
3619
|
console.log(
|
|
3868
|
-
chalk4__default.default.gray(
|
|
3620
|
+
chalk4__default.default.gray(
|
|
3621
|
+
` curl -s ${apiUrl}/website-api/themes/${themeId}/versions | jq .latest_version`
|
|
3622
|
+
)
|
|
3869
3623
|
);
|
|
3870
|
-
console.log(chalk4__default.default.gray(" - Or use AWS_PROFILE=your-profile"));
|
|
3871
|
-
console.log(chalk4__default.default.gray(" - Set AWS_REGION (e.g., ap-southeast-1)"));
|
|
3872
3624
|
console.log();
|
|
3873
|
-
console.log(chalk4__default.default.white("
|
|
3874
|
-
console.log(chalk4__default.default.gray(` Current
|
|
3625
|
+
console.log(chalk4__default.default.white("2. Check API URL configuration:"));
|
|
3626
|
+
console.log(chalk4__default.default.gray(` Current API URL: ${apiUrl}`));
|
|
3875
3627
|
console.log(
|
|
3876
|
-
chalk4__default.default.gray("
|
|
3628
|
+
chalk4__default.default.gray(" Override with NEXT_PUBLIC_API_URL or ONEXTHM_API_URL")
|
|
3877
3629
|
);
|
|
3878
3630
|
console.log();
|
|
3879
|
-
console.log(chalk4__default.default.white("
|
|
3880
|
-
console.log(
|
|
3631
|
+
console.log(chalk4__default.default.white("3. Pin a specific version (CI/production):"));
|
|
3632
|
+
console.log(
|
|
3633
|
+
chalk4__default.default.gray(` THEME_VERSION=1.2.3 onexthm download --theme-id ${themeId}`)
|
|
3634
|
+
);
|
|
3881
3635
|
console.log();
|
|
3882
3636
|
}
|
|
3883
3637
|
async function downloadCommand(options) {
|
|
3884
|
-
logger.header("Download Theme
|
|
3638
|
+
logger.header("Download Theme");
|
|
3885
3639
|
const spinner = ora__default.default("Initializing download...").start();
|
|
3640
|
+
if (options.bucket || options.environment) {
|
|
3641
|
+
spinner.stop();
|
|
3642
|
+
logger.warning(
|
|
3643
|
+
"--bucket and --environment are deprecated and ignored. Themes are now served via HTTP from the website-api Lambda."
|
|
3644
|
+
);
|
|
3645
|
+
spinner.start();
|
|
3646
|
+
}
|
|
3647
|
+
const apiUrl = getApiUrl();
|
|
3886
3648
|
try {
|
|
3887
3649
|
const themeId = options.themeId || process.env.NEXT_PUBLIC_THEME_ID || process.env.THEME_ID;
|
|
3888
|
-
const
|
|
3889
|
-
const bucket = options.bucket || getBucketName2(options.environment);
|
|
3650
|
+
const requestedVersion = options.version || process.env.THEME_VERSION || "latest";
|
|
3890
3651
|
const outputDir = options.output || "./active-theme";
|
|
3891
3652
|
if (!themeId) {
|
|
3892
3653
|
spinner.fail(
|
|
@@ -3896,12 +3657,10 @@ async function downloadCommand(options) {
|
|
|
3896
3657
|
);
|
|
3897
3658
|
process.exit(1);
|
|
3898
3659
|
}
|
|
3899
|
-
spinner.text = `
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
spinner.text = "Resolving latest version...";
|
|
3904
|
-
resolvedVersion = await resolveLatestVersion(s3Client, bucket, themeId);
|
|
3660
|
+
spinner.text = `Resolving ${themeId}@${requestedVersion}...`;
|
|
3661
|
+
let resolvedVersion = requestedVersion;
|
|
3662
|
+
if (requestedVersion === "latest") {
|
|
3663
|
+
resolvedVersion = await resolveLatestVersion(apiUrl, themeId);
|
|
3905
3664
|
spinner.succeed(
|
|
3906
3665
|
`Resolved latest version: ${chalk4__default.default.cyan(resolvedVersion)}`
|
|
3907
3666
|
);
|
|
@@ -3911,24 +3670,19 @@ async function downloadCommand(options) {
|
|
|
3911
3670
|
chalk4__default.default.yellow(
|
|
3912
3671
|
`
|
|
3913
3672
|
Warning: Resolved "latest" to ${resolvedVersion} in CI environment.
|
|
3914
|
-
For
|
|
3673
|
+
For reproducible builds, pin to a specific version:
|
|
3915
3674
|
THEME_VERSION=${resolvedVersion}
|
|
3916
3675
|
`
|
|
3917
3676
|
)
|
|
3918
3677
|
);
|
|
3919
3678
|
}
|
|
3679
|
+
} else {
|
|
3680
|
+
spinner.succeed(`Using version: ${chalk4__default.default.cyan(resolvedVersion)}`);
|
|
3920
3681
|
}
|
|
3921
3682
|
spinner.start(
|
|
3922
3683
|
`Downloading bundle.zip for ${themeId}@${resolvedVersion}...`
|
|
3923
3684
|
);
|
|
3924
|
-
const
|
|
3925
|
-
const response = await s3Client.send(
|
|
3926
|
-
new clientS3.GetObjectCommand({
|
|
3927
|
-
Bucket: bucket,
|
|
3928
|
-
Key: s3Key
|
|
3929
|
-
})
|
|
3930
|
-
);
|
|
3931
|
-
const zipBuffer = await streamToBuffer(response.Body);
|
|
3685
|
+
const zipBuffer = await downloadBundleZip(apiUrl, themeId, resolvedVersion);
|
|
3932
3686
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
3933
3687
|
spinner.succeed(`Downloaded bundle.zip (${sizeMB} MB)`);
|
|
3934
3688
|
spinner.start("Extracting bundle...");
|
|
@@ -3947,7 +3701,7 @@ async function downloadCommand(options) {
|
|
|
3947
3701
|
console.log(
|
|
3948
3702
|
chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(`${themeId}@${resolvedVersion}`)
|
|
3949
3703
|
);
|
|
3950
|
-
console.log(chalk4__default.default.cyan("
|
|
3704
|
+
console.log(chalk4__default.default.cyan(" Source: ") + chalk4__default.default.white(apiUrl));
|
|
3951
3705
|
console.log(chalk4__default.default.cyan(" Output: ") + chalk4__default.default.white(outputDir));
|
|
3952
3706
|
console.log(chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(entries.length));
|
|
3953
3707
|
if (manifest.counts) {
|
|
@@ -3959,82 +3713,70 @@ async function downloadCommand(options) {
|
|
|
3959
3713
|
} catch (error) {
|
|
3960
3714
|
spinner.fail(chalk4__default.default.red("Download failed"));
|
|
3961
3715
|
logger.error(error.message);
|
|
3962
|
-
const themeId = options.themeId || process.env.NEXT_PUBLIC_THEME_ID || "unknown";
|
|
3963
|
-
|
|
3964
|
-
showDownloadFailureHelp(themeId, bucket);
|
|
3716
|
+
const themeId = options.themeId || process.env.NEXT_PUBLIC_THEME_ID || process.env.THEME_ID || "unknown";
|
|
3717
|
+
showDownloadFailureHelp(themeId, apiUrl);
|
|
3965
3718
|
process.exit(1);
|
|
3966
3719
|
}
|
|
3967
3720
|
}
|
|
3968
3721
|
|
|
3969
3722
|
// src/commands/clone.ts
|
|
3970
3723
|
init_logger();
|
|
3971
|
-
function
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
const endpoint = process.env.MINIO_ENDPOINT || "localhost:9000";
|
|
3975
|
-
const secure = process.env.MINIO_SECURE === "true";
|
|
3976
|
-
const endpointUrl = endpoint.startsWith("http") ? endpoint : `${secure ? "https" : "http"}://${endpoint}`;
|
|
3977
|
-
return new clientS3.S3Client({
|
|
3978
|
-
endpoint: endpointUrl,
|
|
3979
|
-
region: "us-east-1",
|
|
3980
|
-
credentials: {
|
|
3981
|
-
accessKeyId: process.env.MINIO_ACCESS_KEY || "minioadmin",
|
|
3982
|
-
secretAccessKey: process.env.MINIO_SECRET_KEY || "minioadmin"
|
|
3983
|
-
},
|
|
3984
|
-
forcePathStyle: true
|
|
3985
|
-
});
|
|
3724
|
+
function unwrapEnvelope2(raw) {
|
|
3725
|
+
if (raw && typeof raw === "object" && "statusCode" in raw && "body" in raw) {
|
|
3726
|
+
return raw.body;
|
|
3986
3727
|
}
|
|
3987
|
-
|
|
3988
|
-
return new clientS3.S3Client({
|
|
3989
|
-
endpoint: "http://localhost:4569",
|
|
3990
|
-
region: "ap-southeast-1",
|
|
3991
|
-
credentials: {
|
|
3992
|
-
accessKeyId: "S3RVER",
|
|
3993
|
-
secretAccessKey: "S3RVER"
|
|
3994
|
-
},
|
|
3995
|
-
forcePathStyle: true
|
|
3996
|
-
});
|
|
3997
|
-
}
|
|
3998
|
-
return new clientS3.S3Client({
|
|
3999
|
-
region: process.env.AWS_REGION || "ap-southeast-1"
|
|
4000
|
-
});
|
|
3728
|
+
return raw;
|
|
4001
3729
|
}
|
|
4002
|
-
function
|
|
4003
|
-
|
|
4004
|
-
|
|
3730
|
+
async function resolveLatestVersion2(apiUrl, themeId) {
|
|
3731
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`;
|
|
3732
|
+
const response = await fetch(url, { cache: "no-store" });
|
|
3733
|
+
if (!response.ok) {
|
|
3734
|
+
throw new Error(
|
|
3735
|
+
`Version lookup failed for "${themeId}" (HTTP ${response.status})`
|
|
3736
|
+
);
|
|
4005
3737
|
}
|
|
4006
|
-
const
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
for await (const chunk of stream) {
|
|
4012
|
-
chunks.push(Buffer.from(chunk));
|
|
3738
|
+
const raw = await response.json();
|
|
3739
|
+
const data = unwrapEnvelope2(raw);
|
|
3740
|
+
const latest = data?.latest_version;
|
|
3741
|
+
if (typeof latest !== "string" || latest.length === 0) {
|
|
3742
|
+
throw new Error(`Theme "${themeId}" has no published versions yet`);
|
|
4013
3743
|
}
|
|
4014
|
-
return
|
|
3744
|
+
return latest;
|
|
4015
3745
|
}
|
|
4016
|
-
async function
|
|
4017
|
-
const
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
}
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
3746
|
+
async function fetchSourceZip(apiUrl, themeId, version2) {
|
|
3747
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/source?version=${encodeURIComponent(version2)}`;
|
|
3748
|
+
const response = await authenticatedFetch(url, {
|
|
3749
|
+
method: "GET"
|
|
3750
|
+
});
|
|
3751
|
+
if (!response.ok) {
|
|
3752
|
+
if (response.status === 404) {
|
|
3753
|
+
throw new Error(
|
|
3754
|
+
`Source not found for ${themeId}@${version2}. The theme may not have been published with source upload enabled.`
|
|
3755
|
+
);
|
|
3756
|
+
}
|
|
3757
|
+
if (response.status === 401 || response.status === 403) {
|
|
3758
|
+
throw new Error(
|
|
3759
|
+
`Not authorized to download source for "${themeId}". Run \`onexthm login\` first.`
|
|
3760
|
+
);
|
|
3761
|
+
}
|
|
3762
|
+
throw new Error(
|
|
3763
|
+
`Source URL request failed for "${themeId}@${version2}" (HTTP ${response.status})`
|
|
4030
3764
|
);
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
3765
|
+
}
|
|
3766
|
+
const raw = await response.json();
|
|
3767
|
+
const data = unwrapEnvelope2(raw);
|
|
3768
|
+
const presignedUrl = data?.download_url;
|
|
3769
|
+
if (typeof presignedUrl !== "string" || presignedUrl.length === 0) {
|
|
3770
|
+
throw new Error("Unexpected /source response shape: missing download_url");
|
|
3771
|
+
}
|
|
3772
|
+
const zipResponse = await fetch(presignedUrl);
|
|
3773
|
+
if (!zipResponse.ok) {
|
|
4034
3774
|
throw new Error(
|
|
4035
|
-
`
|
|
3775
|
+
`Presigned source download failed (HTTP ${zipResponse.status})`
|
|
4036
3776
|
);
|
|
4037
3777
|
}
|
|
3778
|
+
const arrayBuffer = await zipResponse.arrayBuffer();
|
|
3779
|
+
return Buffer.from(arrayBuffer);
|
|
4038
3780
|
}
|
|
4039
3781
|
function runInstall(cwd) {
|
|
4040
3782
|
return new Promise((resolve) => {
|
|
@@ -4133,15 +3875,24 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4133
3875
|
}
|
|
4134
3876
|
async function cloneCommand(themeName, options) {
|
|
4135
3877
|
logger.header("Clone Theme Source");
|
|
3878
|
+
if (options.bucket || options.environment) {
|
|
3879
|
+
logger.warning(
|
|
3880
|
+
"--bucket and --environment are deprecated and ignored. Source is now fetched via HTTP from the website-api Lambda."
|
|
3881
|
+
);
|
|
3882
|
+
}
|
|
3883
|
+
const tokens = await getValidTokens();
|
|
3884
|
+
if (!tokens) {
|
|
3885
|
+
logger.error("Not logged in. Run: onexthm login");
|
|
3886
|
+
process.exit(1);
|
|
3887
|
+
}
|
|
4136
3888
|
let newName = options.name;
|
|
4137
3889
|
if (!newName) {
|
|
4138
3890
|
newName = await promptThemeName(themeName);
|
|
4139
3891
|
}
|
|
4140
3892
|
const spinner = ora__default.default("Initializing clone...").start();
|
|
4141
3893
|
try {
|
|
4142
|
-
const
|
|
3894
|
+
const apiUrl = getApiUrl();
|
|
4143
3895
|
const outputDir = options.output || path9__default.default.resolve(process.cwd(), newName);
|
|
4144
|
-
const s3Client = getS3Client3();
|
|
4145
3896
|
if (await fs__default.default.pathExists(outputDir)) {
|
|
4146
3897
|
spinner.fail(chalk4__default.default.red(`Directory already exists: ${outputDir}`));
|
|
4147
3898
|
logger.info(
|
|
@@ -4154,28 +3905,20 @@ async function cloneCommand(themeName, options) {
|
|
|
4154
3905
|
let version2 = options.version || "latest";
|
|
4155
3906
|
if (version2 === "latest") {
|
|
4156
3907
|
spinner.text = "Resolving latest version...";
|
|
4157
|
-
version2 = await resolveLatestVersion2(
|
|
3908
|
+
version2 = await resolveLatestVersion2(apiUrl, themeName);
|
|
4158
3909
|
spinner.succeed(`Resolved latest version: ${chalk4__default.default.cyan(version2)}`);
|
|
4159
3910
|
}
|
|
4160
3911
|
spinner.start(`Downloading source.zip for ${themeName}@${version2}...`);
|
|
4161
|
-
const s3Key = `themes/${themeName}/${version2}/source.zip`;
|
|
4162
3912
|
let zipBuffer;
|
|
4163
3913
|
try {
|
|
4164
|
-
|
|
4165
|
-
new clientS3.GetObjectCommand({
|
|
4166
|
-
Bucket: bucket,
|
|
4167
|
-
Key: s3Key
|
|
4168
|
-
})
|
|
4169
|
-
);
|
|
4170
|
-
zipBuffer = await streamToBuffer2(response.Body);
|
|
3914
|
+
zipBuffer = await fetchSourceZip(apiUrl, themeName, version2);
|
|
4171
3915
|
} catch (error) {
|
|
4172
|
-
spinner.fail(chalk4__default.default.red(
|
|
3916
|
+
spinner.fail(chalk4__default.default.red(error.message));
|
|
4173
3917
|
console.log();
|
|
4174
3918
|
console.log(
|
|
4175
|
-
chalk4__default.default.
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
chalk4__default.default.gray(`Upload source with: onexthm upload --theme ${themeName}`)
|
|
3919
|
+
chalk4__default.default.gray(
|
|
3920
|
+
`Verify the theme is published: curl -s ${apiUrl}/website-api/themes/${themeName}/versions`
|
|
3921
|
+
)
|
|
4179
3922
|
);
|
|
4180
3923
|
console.log();
|
|
4181
3924
|
process.exit(1);
|
|
@@ -4358,10 +4101,7 @@ function createDevServer(options) {
|
|
|
4358
4101
|
return;
|
|
4359
4102
|
}
|
|
4360
4103
|
if (segments.length > 1) {
|
|
4361
|
-
const fallbackPath = path9__default.default.join(
|
|
4362
|
-
assetsBase,
|
|
4363
|
-
segments.slice(1).join("/")
|
|
4364
|
-
);
|
|
4104
|
+
const fallbackPath = path9__default.default.join(assetsBase, segments.slice(1).join("/"));
|
|
4365
4105
|
if (fallbackPath.startsWith(assetsBase) && fs3__default.default.existsSync(fallbackPath)) {
|
|
4366
4106
|
serveFile(res, fallbackPath);
|
|
4367
4107
|
return;
|
|
@@ -4873,6 +4613,82 @@ async function whoamiCommand() {
|
|
|
4873
4613
|
|
|
4874
4614
|
// src/commands/publish.ts
|
|
4875
4615
|
init_logger();
|
|
4616
|
+
var MIME_MAP = {
|
|
4617
|
+
".png": "image/png",
|
|
4618
|
+
".jpg": "image/jpeg",
|
|
4619
|
+
".jpeg": "image/jpeg",
|
|
4620
|
+
".gif": "image/gif",
|
|
4621
|
+
".webp": "image/webp",
|
|
4622
|
+
".avif": "image/avif",
|
|
4623
|
+
".svg": "image/svg+xml",
|
|
4624
|
+
".ico": "image/x-icon",
|
|
4625
|
+
".bmp": "image/bmp",
|
|
4626
|
+
".woff": "font/woff",
|
|
4627
|
+
".woff2": "font/woff2",
|
|
4628
|
+
".ttf": "font/ttf",
|
|
4629
|
+
".otf": "font/otf",
|
|
4630
|
+
".eot": "application/vnd.ms-fontobject",
|
|
4631
|
+
".mp4": "video/mp4",
|
|
4632
|
+
".webm": "video/webm",
|
|
4633
|
+
".mov": "video/quicktime",
|
|
4634
|
+
".ogg": "video/ogg",
|
|
4635
|
+
".json": "application/json"
|
|
4636
|
+
};
|
|
4637
|
+
var HASH_LEN = 8;
|
|
4638
|
+
function mimeFor(filename) {
|
|
4639
|
+
const ext = path9__default.default.extname(filename).toLowerCase();
|
|
4640
|
+
return MIME_MAP[ext] || "application/octet-stream";
|
|
4641
|
+
}
|
|
4642
|
+
async function sha256Prefix(absPath, len) {
|
|
4643
|
+
const buf = await fs__default.default.readFile(absPath);
|
|
4644
|
+
return crypto__default.default.createHash("sha256").update(buf).digest("hex").slice(0, len);
|
|
4645
|
+
}
|
|
4646
|
+
function insertHashIntoName(relPath, hash) {
|
|
4647
|
+
const dir = path9__default.default.posix.dirname(relPath);
|
|
4648
|
+
const base = path9__default.default.posix.basename(relPath);
|
|
4649
|
+
const ext = path9__default.default.posix.extname(base);
|
|
4650
|
+
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
4651
|
+
const hashed = `${stem}-${hash}${ext}`;
|
|
4652
|
+
return dir === "." ? hashed : `${dir}/${hashed}`;
|
|
4653
|
+
}
|
|
4654
|
+
async function scanThemeAssets(distDir) {
|
|
4655
|
+
const assetsDir = path9__default.default.join(distDir, "theme-assets");
|
|
4656
|
+
if (!await fs__default.default.pathExists(assetsDir)) return [];
|
|
4657
|
+
const files = await glob.glob("**/*", {
|
|
4658
|
+
cwd: assetsDir,
|
|
4659
|
+
nodir: true,
|
|
4660
|
+
dot: false
|
|
4661
|
+
});
|
|
4662
|
+
const results = [];
|
|
4663
|
+
for (const rel of files) {
|
|
4664
|
+
const absPath = path9__default.default.join(assetsDir, rel);
|
|
4665
|
+
const stat = await fs__default.default.stat(absPath);
|
|
4666
|
+
if (!stat.isFile()) continue;
|
|
4667
|
+
const originalPath = rel.split(path9__default.default.sep).join("/");
|
|
4668
|
+
const hash = await sha256Prefix(absPath, HASH_LEN);
|
|
4669
|
+
const hashedPath = insertHashIntoName(originalPath, hash);
|
|
4670
|
+
const contentType = mimeFor(rel);
|
|
4671
|
+
results.push({
|
|
4672
|
+
originalPath,
|
|
4673
|
+
hashedPath,
|
|
4674
|
+
hash,
|
|
4675
|
+
size: stat.size,
|
|
4676
|
+
contentType,
|
|
4677
|
+
absPath
|
|
4678
|
+
});
|
|
4679
|
+
}
|
|
4680
|
+
results.sort((a, b) => a.originalPath.localeCompare(b.originalPath));
|
|
4681
|
+
return results;
|
|
4682
|
+
}
|
|
4683
|
+
function buildAssetMap(entries) {
|
|
4684
|
+
const map = {};
|
|
4685
|
+
for (const e of entries) {
|
|
4686
|
+
map[e.originalPath] = e.hashedPath;
|
|
4687
|
+
}
|
|
4688
|
+
return map;
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
// src/commands/publish.ts
|
|
4876
4692
|
async function publishCommand(options) {
|
|
4877
4693
|
logger.header("OneX Theme Publish");
|
|
4878
4694
|
const tokens = await getValidTokens();
|
|
@@ -5007,37 +4823,123 @@ Or use the --bump flag:
|
|
|
5007
4823
|
logger.error(error instanceof Error ? error.message : "Build error");
|
|
5008
4824
|
process.exit(1);
|
|
5009
4825
|
}
|
|
5010
|
-
|
|
4826
|
+
const distDir = path9__default.default.join(themePath, "dist");
|
|
4827
|
+
let assetEntries = [];
|
|
4828
|
+
try {
|
|
4829
|
+
assetEntries = await scanThemeAssets(distDir);
|
|
4830
|
+
if (assetEntries.length > 0) {
|
|
4831
|
+
logger.info(`Found ${assetEntries.length} theme asset file(s)`);
|
|
4832
|
+
}
|
|
4833
|
+
} catch (error) {
|
|
4834
|
+
logger.error(
|
|
4835
|
+
`Asset scan failed: ${error instanceof Error ? error.message : "unknown"}`
|
|
4836
|
+
);
|
|
4837
|
+
process.exit(1);
|
|
4838
|
+
}
|
|
4839
|
+
try {
|
|
4840
|
+
const assetMap = buildAssetMap(assetEntries);
|
|
4841
|
+
const assetMapPath = path9__default.default.join(distDir, "asset-map.json");
|
|
4842
|
+
await fs__default.default.writeFile(assetMapPath, JSON.stringify(assetMap, null, 2));
|
|
4843
|
+
} catch (error) {
|
|
4844
|
+
logger.error(
|
|
4845
|
+
`Failed to write asset-map.json: ${error instanceof Error ? error.message : "unknown"}`
|
|
4846
|
+
);
|
|
4847
|
+
process.exit(1);
|
|
4848
|
+
}
|
|
4849
|
+
logger.startSpinner("Getting upload URLs...");
|
|
5011
4850
|
let bundleUploadUrl;
|
|
5012
4851
|
let sourceUploadUrl;
|
|
4852
|
+
let assetUploads = [];
|
|
4853
|
+
let alreadyUploaded = [];
|
|
5013
4854
|
try {
|
|
5014
4855
|
const pubResponse = await authenticatedFetch(
|
|
5015
4856
|
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`,
|
|
5016
4857
|
{
|
|
5017
4858
|
method: "POST",
|
|
5018
|
-
body: JSON.stringify({
|
|
4859
|
+
body: JSON.stringify({
|
|
4860
|
+
version: version2,
|
|
4861
|
+
assets: assetEntries.map((a) => ({
|
|
4862
|
+
path: a.hashedPath,
|
|
4863
|
+
hash: a.hash,
|
|
4864
|
+
size: a.size,
|
|
4865
|
+
content_type: a.contentType
|
|
4866
|
+
}))
|
|
4867
|
+
})
|
|
5019
4868
|
}
|
|
5020
4869
|
);
|
|
5021
4870
|
const pubData = await pubResponse.json();
|
|
5022
4871
|
const pubBody = pubData.statusCode ? pubData.body : pubData;
|
|
5023
4872
|
if (!pubResponse.ok || !pubBody.bundleUploadUrl) {
|
|
5024
|
-
logger.stopSpinner(false, "Failed to get upload
|
|
4873
|
+
logger.stopSpinner(false, "Failed to get upload URLs");
|
|
5025
4874
|
logger.error(pubBody.error || "Server error");
|
|
5026
4875
|
process.exit(1);
|
|
5027
4876
|
}
|
|
5028
4877
|
bundleUploadUrl = pubBody.bundleUploadUrl;
|
|
5029
4878
|
sourceUploadUrl = pubBody.sourceUploadUrl;
|
|
5030
|
-
|
|
4879
|
+
assetUploads = pubBody.assetUploads || [];
|
|
4880
|
+
alreadyUploaded = pubBody.alreadyUploaded || [];
|
|
4881
|
+
logger.stopSpinner(
|
|
4882
|
+
true,
|
|
4883
|
+
`Upload URLs obtained (${assetUploads.length} new, ${alreadyUploaded.length} reused)`
|
|
4884
|
+
);
|
|
5031
4885
|
} catch (error) {
|
|
5032
4886
|
logger.stopSpinner(false, "Failed");
|
|
5033
4887
|
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
5034
4888
|
process.exit(1);
|
|
5035
4889
|
}
|
|
4890
|
+
if (assetUploads.length > 0) {
|
|
4891
|
+
logger.startSpinner(`Uploading ${assetUploads.length} asset(s) to S3...`);
|
|
4892
|
+
const CONCURRENCY = 8;
|
|
4893
|
+
const byHashedPath = new Map(assetEntries.map((a) => [a.hashedPath, a]));
|
|
4894
|
+
const queue = [...assetUploads];
|
|
4895
|
+
let uploaded = 0;
|
|
4896
|
+
let failed = 0;
|
|
4897
|
+
async function worker() {
|
|
4898
|
+
while (queue.length > 0) {
|
|
4899
|
+
const item = queue.shift();
|
|
4900
|
+
if (!item) break;
|
|
4901
|
+
const entry = byHashedPath.get(item.path);
|
|
4902
|
+
if (!entry) {
|
|
4903
|
+
failed++;
|
|
4904
|
+
logger.error(` \u2717 ${item.path}: no local file found`);
|
|
4905
|
+
continue;
|
|
4906
|
+
}
|
|
4907
|
+
try {
|
|
4908
|
+
const buf = await fs__default.default.promises.readFile(entry.absPath);
|
|
4909
|
+
const res = await fetch(item.upload_url, {
|
|
4910
|
+
method: "PUT",
|
|
4911
|
+
headers: {
|
|
4912
|
+
"Content-Type": entry.contentType,
|
|
4913
|
+
"x-amz-acl": "public-read"
|
|
4914
|
+
},
|
|
4915
|
+
body: buf
|
|
4916
|
+
});
|
|
4917
|
+
if (!res.ok) {
|
|
4918
|
+
throw new Error(`HTTP ${res.status}`);
|
|
4919
|
+
}
|
|
4920
|
+
uploaded++;
|
|
4921
|
+
} catch (e) {
|
|
4922
|
+
failed++;
|
|
4923
|
+
logger.error(
|
|
4924
|
+
` \u2717 ${item.path}: ${e instanceof Error ? e.message : "failed"}`
|
|
4925
|
+
);
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
await Promise.all(
|
|
4930
|
+
Array.from(
|
|
4931
|
+
{ length: Math.min(CONCURRENCY, assetUploads.length) },
|
|
4932
|
+
() => worker()
|
|
4933
|
+
)
|
|
4934
|
+
);
|
|
4935
|
+
if (failed > 0) {
|
|
4936
|
+
logger.stopSpinner(false, `${failed} asset(s) failed`);
|
|
4937
|
+
process.exit(1);
|
|
4938
|
+
}
|
|
4939
|
+
logger.stopSpinner(true, `Uploaded ${uploaded} asset(s)`);
|
|
4940
|
+
}
|
|
5036
4941
|
logger.startSpinner("Uploading bundle...");
|
|
5037
4942
|
try {
|
|
5038
|
-
const archiver3 = await import('archiver');
|
|
5039
|
-
const { createWriteStream } = await import('fs');
|
|
5040
|
-
const distDir = path9__default.default.join(themePath, "dist");
|
|
5041
4943
|
if (!fs__default.default.existsSync(distDir)) {
|
|
5042
4944
|
logger.stopSpinner(false, "No dist/ directory");
|
|
5043
4945
|
logger.error("Build the theme first: onexthm build");
|
|
@@ -5048,7 +4950,9 @@ Or use the --bump flag:
|
|
|
5048
4950
|
"bundle.zip",
|
|
5049
4951
|
"source.zip",
|
|
5050
4952
|
"preview-runtime.js",
|
|
5051
|
-
"preview-runtime.js.map"
|
|
4953
|
+
"preview-runtime.js.map",
|
|
4954
|
+
"theme-assets",
|
|
4955
|
+
"theme-assets/**"
|
|
5052
4956
|
]);
|
|
5053
4957
|
const bundleBuffer = fs__default.default.readFileSync(bundleZipPath);
|
|
5054
4958
|
const bundleRes = await fetch(bundleUploadUrl, {
|
|
@@ -5135,11 +5039,11 @@ Or use the --bump flag:
|
|
|
5135
5039
|
logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
|
|
5136
5040
|
}
|
|
5137
5041
|
async function createZip(sourceDir, outputPath, exclude) {
|
|
5138
|
-
const
|
|
5042
|
+
const archiver2 = (await import('archiver')).default;
|
|
5139
5043
|
const { createWriteStream } = await import('fs');
|
|
5140
5044
|
return new Promise((resolve, reject) => {
|
|
5141
5045
|
const output = createWriteStream(outputPath);
|
|
5142
|
-
const archive =
|
|
5046
|
+
const archive = archiver2("zip", { zlib: { level: 9 } });
|
|
5143
5047
|
output.on("close", resolve);
|
|
5144
5048
|
archive.on("error", reject);
|
|
5145
5049
|
archive.pipe(output);
|
|
@@ -5202,29 +5106,17 @@ program.command("deploy").description("Upload theme package to API server").opti
|
|
|
5202
5106
|
"-e, --environment <env>",
|
|
5203
5107
|
"Environment (production, staging, development)"
|
|
5204
5108
|
).action(deployCommand);
|
|
5205
|
-
program.command("upload").description("
|
|
5206
|
-
|
|
5207
|
-
"Environment (staging|production)",
|
|
5208
|
-
"staging"
|
|
5209
|
-
).option("--dry-run", "Show what would be uploaded without uploading").option("--skip-source", "Skip uploading source.zip").option("--source-dir <dir>", "Source directory path").action(uploadCommand);
|
|
5210
|
-
program.command("download").description("Download theme from S3 bucket").option("-t, --theme-id <id>", "Theme ID to download").option(
|
|
5109
|
+
program.command("upload").description("[deprecated] use `onexthm publish` instead").option("-t, --theme <theme>", "[deprecated] ignored").option("-b, --bucket <name>", "[deprecated] ignored").option("-v, --version <version>", "[deprecated] ignored").option("-e, --environment <env>", "[deprecated] ignored").option("--dry-run", "[deprecated] ignored").option("--skip-source", "[deprecated] ignored").option("--source-dir <dir>", "[deprecated] ignored").action(uploadCommand);
|
|
5110
|
+
program.command("download").description("Download a published theme via the website-api").option("-t, --theme-id <id>", "Theme ID to download").option(
|
|
5211
5111
|
"-v, --version <version>",
|
|
5212
5112
|
"Theme version (default: latest)",
|
|
5213
5113
|
"latest"
|
|
5214
|
-
).option("-b, --bucket <name>", "
|
|
5215
|
-
|
|
5216
|
-
"Environment (staging|production)",
|
|
5217
|
-
"staging"
|
|
5218
|
-
).option("-o, --output <dir>", "Output directory", "./active-theme").action(downloadCommand);
|
|
5219
|
-
program.command("clone").description("Clone theme source code from S3").argument("<theme-name>", "Theme to clone").option(
|
|
5114
|
+
).option("-b, --bucket <name>", "[deprecated] ignored").option("-e, --environment <env>", "[deprecated] ignored").option("-o, --output <dir>", "Output directory", "./active-theme").action(downloadCommand);
|
|
5115
|
+
program.command("clone").description("Clone theme source code via the website-api").argument("<theme-name>", "Theme to clone").option(
|
|
5220
5116
|
"-v, --version <version>",
|
|
5221
5117
|
"Theme version (default: latest)",
|
|
5222
5118
|
"latest"
|
|
5223
|
-
).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").option("-b, --bucket <name>", "
|
|
5224
|
-
"-e, --environment <env>",
|
|
5225
|
-
"Environment (staging|production)",
|
|
5226
|
-
"staging"
|
|
5227
|
-
).option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
|
|
5119
|
+
).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").option("-b, --bucket <name>", "[deprecated] ignored").option("-e, --environment <env>", "[deprecated] ignored").option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
|
|
5228
5120
|
program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
|
|
5229
5121
|
program.command("login").description("Login to OneX platform").action(loginCommand);
|
|
5230
5122
|
program.command("logout").description("Logout from OneX platform").action(logoutCommand);
|