@onexapis/cli 1.1.64 → 1.1.65
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 +267 -105
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +267 -105
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +10 -17
- package/dist/index.d.ts +10 -17
- package/dist/index.js +67 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +67 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/templates/default/.env.example +1 -1
package/dist/cli.js
CHANGED
|
@@ -1807,21 +1807,43 @@ function getValidCategories() {
|
|
|
1807
1807
|
];
|
|
1808
1808
|
}
|
|
1809
1809
|
var AUTH_DIR = path9__default.default.join(os__default.default.homedir(), ".onexthm");
|
|
1810
|
-
var
|
|
1811
|
-
|
|
1812
|
-
|
|
1810
|
+
var ENV_URLS = {
|
|
1811
|
+
dev: "https://platform-dev.onexeos.com",
|
|
1812
|
+
prod: "https://platform-staging.onexeos.com"
|
|
1813
|
+
};
|
|
1814
|
+
function getAuthFile(env = "dev") {
|
|
1815
|
+
const newFile = path9__default.default.join(AUTH_DIR, `auth-${env}.json`);
|
|
1816
|
+
if (env === "dev") {
|
|
1817
|
+
const legacyFile = path9__default.default.join(AUTH_DIR, "auth.json");
|
|
1818
|
+
if (fs__default.default.existsSync(legacyFile) && !fs__default.default.existsSync(newFile)) {
|
|
1819
|
+
try {
|
|
1820
|
+
fs__default.default.moveSync(legacyFile, newFile);
|
|
1821
|
+
} catch {
|
|
1822
|
+
try {
|
|
1823
|
+
fs__default.default.copySync(legacyFile, newFile);
|
|
1824
|
+
fs__default.default.removeSync(legacyFile);
|
|
1825
|
+
} catch {
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return newFile;
|
|
1831
|
+
}
|
|
1832
|
+
function getApiUrl(env = "dev") {
|
|
1833
|
+
return process.env.ONEXTHM_API_URL || ENV_URLS[env];
|
|
1813
1834
|
}
|
|
1814
|
-
async function saveAuthTokens(tokens) {
|
|
1835
|
+
async function saveAuthTokens(tokens, env = "dev") {
|
|
1815
1836
|
await fs__default.default.ensureDir(AUTH_DIR);
|
|
1816
1837
|
const key = getMachineKey();
|
|
1817
1838
|
const data = JSON.stringify(tokens);
|
|
1818
1839
|
const encrypted = encrypt(data, key);
|
|
1819
|
-
await fs__default.default.writeFile(
|
|
1840
|
+
await fs__default.default.writeFile(getAuthFile(env), encrypted, "utf-8");
|
|
1820
1841
|
}
|
|
1821
|
-
function loadAuthTokens() {
|
|
1842
|
+
function loadAuthTokens(env = "dev") {
|
|
1822
1843
|
try {
|
|
1823
|
-
|
|
1824
|
-
|
|
1844
|
+
const file = getAuthFile(env);
|
|
1845
|
+
if (!fs__default.default.existsSync(file)) return null;
|
|
1846
|
+
const encrypted = fs__default.default.readFileSync(file, "utf-8");
|
|
1825
1847
|
const key = getMachineKey();
|
|
1826
1848
|
const data = decrypt(encrypted, key);
|
|
1827
1849
|
return JSON.parse(data);
|
|
@@ -1829,34 +1851,34 @@ function loadAuthTokens() {
|
|
|
1829
1851
|
return null;
|
|
1830
1852
|
}
|
|
1831
1853
|
}
|
|
1832
|
-
async function clearAuthTokens() {
|
|
1854
|
+
async function clearAuthTokens(env = "dev") {
|
|
1833
1855
|
try {
|
|
1834
|
-
await fs__default.default.remove(
|
|
1856
|
+
await fs__default.default.remove(getAuthFile(env));
|
|
1835
1857
|
} catch {
|
|
1836
1858
|
}
|
|
1837
1859
|
}
|
|
1838
1860
|
function isTokenExpired(tokens) {
|
|
1839
1861
|
return Date.now() / 1e3 > tokens.expiresAt - 300;
|
|
1840
1862
|
}
|
|
1841
|
-
async function getValidTokens() {
|
|
1842
|
-
const tokens = loadAuthTokens();
|
|
1863
|
+
async function getValidTokens(env = "dev") {
|
|
1864
|
+
const tokens = loadAuthTokens(env);
|
|
1843
1865
|
if (!tokens) return null;
|
|
1844
1866
|
if (!isTokenExpired(tokens)) return tokens;
|
|
1845
1867
|
try {
|
|
1846
|
-
const apiUrl = getApiUrl();
|
|
1868
|
+
const apiUrl = getApiUrl(env);
|
|
1847
1869
|
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
1848
1870
|
method: "POST",
|
|
1849
1871
|
headers: { "Content-Type": "application/json" },
|
|
1850
1872
|
body: JSON.stringify({ refresh_token: tokens.refreshToken })
|
|
1851
1873
|
});
|
|
1852
1874
|
if (!response.ok) {
|
|
1853
|
-
await clearAuthTokens();
|
|
1875
|
+
await clearAuthTokens(env);
|
|
1854
1876
|
return null;
|
|
1855
1877
|
}
|
|
1856
1878
|
const data = await response.json();
|
|
1857
1879
|
const body = data.statusCode ? data.body : data;
|
|
1858
1880
|
if (!body.IdToken) {
|
|
1859
|
-
await clearAuthTokens();
|
|
1881
|
+
await clearAuthTokens(env);
|
|
1860
1882
|
return null;
|
|
1861
1883
|
}
|
|
1862
1884
|
const refreshed = {
|
|
@@ -1865,17 +1887,19 @@ async function getValidTokens() {
|
|
|
1865
1887
|
idToken: body.IdToken,
|
|
1866
1888
|
expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
|
|
1867
1889
|
};
|
|
1868
|
-
await saveAuthTokens(refreshed);
|
|
1890
|
+
await saveAuthTokens(refreshed, env);
|
|
1869
1891
|
return refreshed;
|
|
1870
1892
|
} catch {
|
|
1871
|
-
await clearAuthTokens();
|
|
1893
|
+
await clearAuthTokens(env);
|
|
1872
1894
|
return null;
|
|
1873
1895
|
}
|
|
1874
1896
|
}
|
|
1875
|
-
async function authenticatedFetch(url, init) {
|
|
1876
|
-
const tokens = await getValidTokens();
|
|
1897
|
+
async function authenticatedFetch(url, init, env = "dev") {
|
|
1898
|
+
const tokens = await getValidTokens(env);
|
|
1877
1899
|
if (!tokens) {
|
|
1878
|
-
throw new Error(
|
|
1900
|
+
throw new Error(
|
|
1901
|
+
`Not logged in to ${env} environment. Run: onexthm login --env ${env}`
|
|
1902
|
+
);
|
|
1879
1903
|
}
|
|
1880
1904
|
const headers = new Headers(init?.headers);
|
|
1881
1905
|
headers.set("Authorization", `Bearer ${tokens.idToken}`);
|
|
@@ -1959,7 +1983,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
1959
1983
|
}
|
|
1960
1984
|
if (!options.yes) {
|
|
1961
1985
|
try {
|
|
1962
|
-
const apiUrl = getApiUrl();
|
|
1986
|
+
const apiUrl = getApiUrl(options.env ?? "dev");
|
|
1963
1987
|
const controller = new AbortController();
|
|
1964
1988
|
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
1965
1989
|
const response = await fetch(
|
|
@@ -3908,9 +3932,7 @@ function showDownloadFailureHelp(themeId, apiUrl) {
|
|
|
3908
3932
|
console.log();
|
|
3909
3933
|
console.log(chalk4__default.default.white("2. Check API URL configuration:"));
|
|
3910
3934
|
console.log(chalk4__default.default.gray(` Current API URL: ${apiUrl}`));
|
|
3911
|
-
console.log(
|
|
3912
|
-
chalk4__default.default.gray(" Override with NEXT_PUBLIC_API_URL or ONEXTHM_API_URL")
|
|
3913
|
-
);
|
|
3935
|
+
console.log(chalk4__default.default.gray(" Override with ONEXTHM_API_URL env var if needed"));
|
|
3914
3936
|
console.log();
|
|
3915
3937
|
console.log(chalk4__default.default.white("3. Pin a specific version (CI/production):"));
|
|
3916
3938
|
console.log(
|
|
@@ -3920,15 +3942,17 @@ function showDownloadFailureHelp(themeId, apiUrl) {
|
|
|
3920
3942
|
}
|
|
3921
3943
|
async function downloadCommand(options) {
|
|
3922
3944
|
logger.header("Download Theme");
|
|
3945
|
+
const env = options.env ?? "dev";
|
|
3946
|
+
const apiUrl = getApiUrl(env);
|
|
3947
|
+
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
3923
3948
|
const spinner = ora__default.default("Initializing download...").start();
|
|
3924
|
-
if (options.bucket
|
|
3949
|
+
if (options.bucket) {
|
|
3925
3950
|
spinner.stop();
|
|
3926
3951
|
logger.warning(
|
|
3927
|
-
"--bucket
|
|
3952
|
+
"--bucket is deprecated and ignored. Themes are now served via HTTP from the website-api Lambda."
|
|
3928
3953
|
);
|
|
3929
3954
|
spinner.start();
|
|
3930
3955
|
}
|
|
3931
|
-
const apiUrl = getApiUrl();
|
|
3932
3956
|
try {
|
|
3933
3957
|
const themeId = options.themeId || process.env.NEXT_PUBLIC_THEME_ID || process.env.THEME_ID;
|
|
3934
3958
|
const requestedVersion = options.version || process.env.THEME_VERSION || "latest";
|
|
@@ -3985,6 +4009,7 @@ async function downloadCommand(options) {
|
|
|
3985
4009
|
console.log(
|
|
3986
4010
|
chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(`${themeId}@${resolvedVersion}`)
|
|
3987
4011
|
);
|
|
4012
|
+
console.log(chalk4__default.default.cyan(" Env: ") + chalk4__default.default.white(env));
|
|
3988
4013
|
console.log(chalk4__default.default.cyan(" Source: ") + chalk4__default.default.white(apiUrl));
|
|
3989
4014
|
console.log(chalk4__default.default.cyan(" Output: ") + chalk4__default.default.white(outputDir));
|
|
3990
4015
|
console.log(chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(entries.length));
|
|
@@ -4027,11 +4052,9 @@ async function resolveLatestVersion2(apiUrl, themeId) {
|
|
|
4027
4052
|
}
|
|
4028
4053
|
return latest;
|
|
4029
4054
|
}
|
|
4030
|
-
async function fetchSourceZip(apiUrl, themeId, version2) {
|
|
4055
|
+
async function fetchSourceZip(apiUrl, themeId, version2, env) {
|
|
4031
4056
|
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/source?version=${encodeURIComponent(version2)}`;
|
|
4032
|
-
const response = await authenticatedFetch(url, {
|
|
4033
|
-
method: "GET"
|
|
4034
|
-
});
|
|
4057
|
+
const response = await authenticatedFetch(url, { method: "GET" }, env);
|
|
4035
4058
|
if (!response.ok) {
|
|
4036
4059
|
if (response.status === 404) {
|
|
4037
4060
|
throw new Error(
|
|
@@ -4040,7 +4063,7 @@ async function fetchSourceZip(apiUrl, themeId, version2) {
|
|
|
4040
4063
|
}
|
|
4041
4064
|
if (response.status === 401 || response.status === 403) {
|
|
4042
4065
|
throw new Error(
|
|
4043
|
-
`Not authorized to download source for "${themeId}". Run \`onexthm login\` first.`
|
|
4066
|
+
`Not authorized to download source for "${themeId}". Run \`onexthm login --env ${env}\` first.`
|
|
4044
4067
|
);
|
|
4045
4068
|
}
|
|
4046
4069
|
throw new Error(
|
|
@@ -4159,14 +4182,19 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4159
4182
|
}
|
|
4160
4183
|
async function cloneCommand(themeName, options) {
|
|
4161
4184
|
logger.header("Clone Theme Source");
|
|
4162
|
-
|
|
4185
|
+
const env = options.env ?? "dev";
|
|
4186
|
+
const apiUrl = getApiUrl(env);
|
|
4187
|
+
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
4188
|
+
if (options.bucket) {
|
|
4163
4189
|
logger.warning(
|
|
4164
|
-
"--bucket
|
|
4190
|
+
"--bucket is deprecated and ignored. Source is now fetched via HTTP from the website-api Lambda."
|
|
4165
4191
|
);
|
|
4166
4192
|
}
|
|
4167
|
-
const tokens = await getValidTokens();
|
|
4193
|
+
const tokens = await getValidTokens(env);
|
|
4168
4194
|
if (!tokens) {
|
|
4169
|
-
logger.error(
|
|
4195
|
+
logger.error(
|
|
4196
|
+
`Not logged in to ${env} environment. Run: onexthm login --env ${env}`
|
|
4197
|
+
);
|
|
4170
4198
|
process.exit(1);
|
|
4171
4199
|
}
|
|
4172
4200
|
let newName = options.name;
|
|
@@ -4175,7 +4203,6 @@ async function cloneCommand(themeName, options) {
|
|
|
4175
4203
|
}
|
|
4176
4204
|
const spinner = ora__default.default("Initializing clone...").start();
|
|
4177
4205
|
try {
|
|
4178
|
-
const apiUrl = getApiUrl();
|
|
4179
4206
|
const outputDir = options.output || path9__default.default.resolve(process.cwd(), newName);
|
|
4180
4207
|
if (await fs__default.default.pathExists(outputDir)) {
|
|
4181
4208
|
spinner.fail(chalk4__default.default.red(`Directory already exists: ${outputDir}`));
|
|
@@ -4195,7 +4222,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4195
4222
|
spinner.start(`Downloading source.zip for ${themeName}@${version2}...`);
|
|
4196
4223
|
let zipBuffer;
|
|
4197
4224
|
try {
|
|
4198
|
-
zipBuffer = await fetchSourceZip(apiUrl, themeName, version2);
|
|
4225
|
+
zipBuffer = await fetchSourceZip(apiUrl, themeName, version2, env);
|
|
4199
4226
|
} catch (error) {
|
|
4200
4227
|
spinner.fail(chalk4__default.default.red(error.message));
|
|
4201
4228
|
console.log();
|
|
@@ -4229,7 +4256,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4229
4256
|
[
|
|
4230
4257
|
"# API Configuration (enables real data in preview)",
|
|
4231
4258
|
"# Get your Company ID from the OneX dashboard",
|
|
4232
|
-
|
|
4259
|
+
`NEXT_PUBLIC_API_URL=${apiUrl}`,
|
|
4233
4260
|
"NEXT_PUBLIC_COMPANY_ID=",
|
|
4234
4261
|
""
|
|
4235
4262
|
].join("\n")
|
|
@@ -4282,6 +4309,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4282
4309
|
console.log(
|
|
4283
4310
|
chalk4__default.default.cyan(" Source: ") + chalk4__default.default.gray(`${themeName}@${version2}`)
|
|
4284
4311
|
);
|
|
4312
|
+
console.log(chalk4__default.default.cyan(" Env: ") + chalk4__default.default.white(env));
|
|
4285
4313
|
console.log(chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(newName));
|
|
4286
4314
|
console.log(chalk4__default.default.cyan(" Location: ") + chalk4__default.default.white(outputDir));
|
|
4287
4315
|
console.log(chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(entries.length));
|
|
@@ -4558,9 +4586,13 @@ async function configCommand() {
|
|
|
4558
4586
|
|
|
4559
4587
|
// src/commands/login.ts
|
|
4560
4588
|
init_logger();
|
|
4561
|
-
async function loginCommand() {
|
|
4589
|
+
async function loginCommand(options = {}) {
|
|
4590
|
+
const env = options.env ?? "dev";
|
|
4591
|
+
const apiUrl = getApiUrl(env);
|
|
4562
4592
|
logger.header("OneX Theme Developer Login");
|
|
4563
|
-
|
|
4593
|
+
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
4594
|
+
logger.newLine();
|
|
4595
|
+
const existing = loadAuthTokens(env);
|
|
4564
4596
|
if (existing) {
|
|
4565
4597
|
logger.info(`Already logged in as: ${existing.user.email}`);
|
|
4566
4598
|
const { relogin } = await inquirer__default.default.prompt([
|
|
@@ -4589,7 +4621,6 @@ async function loginCommand() {
|
|
|
4589
4621
|
]);
|
|
4590
4622
|
logger.startSpinner("Logging in...");
|
|
4591
4623
|
try {
|
|
4592
|
-
const apiUrl = getApiUrl();
|
|
4593
4624
|
const response = await fetch(`${apiUrl}/auth/login`, {
|
|
4594
4625
|
method: "POST",
|
|
4595
4626
|
headers: { "Content-Type": "application/json" },
|
|
@@ -4624,15 +4655,18 @@ async function loginCommand() {
|
|
|
4624
4655
|
userId: claims.sub
|
|
4625
4656
|
}
|
|
4626
4657
|
};
|
|
4627
|
-
await saveAuthTokens(tokens);
|
|
4658
|
+
await saveAuthTokens(tokens, env);
|
|
4628
4659
|
logger.stopSpinner(true, "Logged in!");
|
|
4629
4660
|
logger.newLine();
|
|
4630
|
-
logger.info(`
|
|
4631
|
-
|
|
4661
|
+
logger.info(` Environment: ${env}`);
|
|
4662
|
+
logger.info(` Email: ${tokens.user.email}`);
|
|
4663
|
+
if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
|
|
4632
4664
|
if (tokens.user.companyId)
|
|
4633
|
-
logger.info(` Company:
|
|
4665
|
+
logger.info(` Company: ${tokens.user.companyId}`);
|
|
4634
4666
|
logger.newLine();
|
|
4635
|
-
logger.success(
|
|
4667
|
+
logger.success(
|
|
4668
|
+
`Token stored securely in ~/.onexthm/auth-${env}.json (encrypted)`
|
|
4669
|
+
);
|
|
4636
4670
|
} catch (error) {
|
|
4637
4671
|
logger.stopSpinner(false, "Login failed");
|
|
4638
4672
|
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
@@ -4642,32 +4676,37 @@ async function loginCommand() {
|
|
|
4642
4676
|
|
|
4643
4677
|
// src/commands/logout.ts
|
|
4644
4678
|
init_logger();
|
|
4645
|
-
async function logoutCommand() {
|
|
4646
|
-
const
|
|
4679
|
+
async function logoutCommand(options = {}) {
|
|
4680
|
+
const env = options.env ?? "dev";
|
|
4681
|
+
const tokens = loadAuthTokens(env);
|
|
4647
4682
|
if (!tokens) {
|
|
4648
|
-
logger.info(
|
|
4683
|
+
logger.info(`Not logged in to ${env} environment.`);
|
|
4649
4684
|
return;
|
|
4650
4685
|
}
|
|
4651
|
-
await clearAuthTokens();
|
|
4652
|
-
logger.success(`Logged out (was: ${tokens.user.email})`);
|
|
4686
|
+
await clearAuthTokens(env);
|
|
4687
|
+
logger.success(`Logged out of ${env} (was: ${tokens.user.email})`);
|
|
4653
4688
|
}
|
|
4654
4689
|
|
|
4655
4690
|
// src/commands/whoami.ts
|
|
4656
4691
|
init_logger();
|
|
4657
|
-
async function whoamiCommand() {
|
|
4658
|
-
const
|
|
4692
|
+
async function whoamiCommand(options = {}) {
|
|
4693
|
+
const env = options.env ?? "dev";
|
|
4694
|
+
const tokens = loadAuthTokens(env);
|
|
4659
4695
|
if (!tokens) {
|
|
4660
|
-
logger.error(
|
|
4696
|
+
logger.error(
|
|
4697
|
+
`Not logged in to ${env} environment. Run: onexthm login --env ${env}`
|
|
4698
|
+
);
|
|
4661
4699
|
process.exit(1);
|
|
4662
4700
|
}
|
|
4663
4701
|
const expired = isTokenExpired(tokens);
|
|
4664
4702
|
logger.header("OneX Theme Developer");
|
|
4665
|
-
logger.info(`
|
|
4666
|
-
|
|
4703
|
+
logger.info(` Environment: ${env} (${getApiUrl(env)})`);
|
|
4704
|
+
logger.info(` Email: ${tokens.user.email}`);
|
|
4705
|
+
if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
|
|
4667
4706
|
if (tokens.user.companyId)
|
|
4668
|
-
logger.info(` Company:
|
|
4707
|
+
logger.info(` Company: ${tokens.user.companyId}`);
|
|
4669
4708
|
logger.info(
|
|
4670
|
-
` Status:
|
|
4709
|
+
` Status: ${expired ? "\u26A0 Token expired (will auto-refresh)" : "\u2713 Active"}`
|
|
4671
4710
|
);
|
|
4672
4711
|
}
|
|
4673
4712
|
|
|
@@ -4762,10 +4801,15 @@ function buildAssetMap(entries) {
|
|
|
4762
4801
|
|
|
4763
4802
|
// src/commands/publish.ts
|
|
4764
4803
|
async function publishCommand(options) {
|
|
4804
|
+
const env = options.env ?? "dev";
|
|
4765
4805
|
logger.header("OneX Theme Publish");
|
|
4766
|
-
|
|
4806
|
+
logger.info(`Environment: ${env} (${getApiUrl(env)})`);
|
|
4807
|
+
logger.newLine();
|
|
4808
|
+
const tokens = await getValidTokens(env);
|
|
4767
4809
|
if (!tokens) {
|
|
4768
|
-
logger.error(
|
|
4810
|
+
logger.error(
|
|
4811
|
+
`Not logged in to ${env} environment. Run: onexthm login --env ${env}`
|
|
4812
|
+
);
|
|
4769
4813
|
process.exit(1);
|
|
4770
4814
|
}
|
|
4771
4815
|
logger.info(`Logged in as: ${tokens.user.email}`);
|
|
@@ -4816,7 +4860,7 @@ async function publishCommand(options) {
|
|
|
4816
4860
|
logger.info(`Theme: ${themeId}`);
|
|
4817
4861
|
logger.info(`Version: ${version2}`);
|
|
4818
4862
|
logger.newLine();
|
|
4819
|
-
const apiUrl = getApiUrl();
|
|
4863
|
+
const apiUrl = getApiUrl(env);
|
|
4820
4864
|
logger.startSpinner("Registering theme...");
|
|
4821
4865
|
try {
|
|
4822
4866
|
const regResponse = await authenticatedFetch(
|
|
@@ -4833,7 +4877,8 @@ async function publishCommand(options) {
|
|
|
4833
4877
|
tags: pkg.keywords || [],
|
|
4834
4878
|
thumbnail_url: pkg.onex?.thumbnail || ""
|
|
4835
4879
|
})
|
|
4836
|
-
}
|
|
4880
|
+
},
|
|
4881
|
+
env
|
|
4837
4882
|
);
|
|
4838
4883
|
const regData = await regResponse.json();
|
|
4839
4884
|
const regBody = regData.statusCode ? regData.body : regData;
|
|
@@ -4855,7 +4900,8 @@ async function publishCommand(options) {
|
|
|
4855
4900
|
try {
|
|
4856
4901
|
const checkResponse = await authenticatedFetch(
|
|
4857
4902
|
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/exists`,
|
|
4858
|
-
{ method: "GET" }
|
|
4903
|
+
{ method: "GET" },
|
|
4904
|
+
env
|
|
4859
4905
|
);
|
|
4860
4906
|
const checkData = await checkResponse.json();
|
|
4861
4907
|
const checkBody = checkData.statusCode ? checkData.body : checkData;
|
|
@@ -4920,7 +4966,7 @@ Or use the --bump flag:
|
|
|
4920
4966
|
logger.startSpinner(`Uploading ${videoAssets.length} video(s)...`);
|
|
4921
4967
|
try {
|
|
4922
4968
|
for (const video of videoAssets) {
|
|
4923
|
-
const url = await uploadVideoMultipart(apiUrl, themeId, video);
|
|
4969
|
+
const url = await uploadVideoMultipart(apiUrl, themeId, video, env);
|
|
4924
4970
|
videoUrls[video.originalPath] = url;
|
|
4925
4971
|
}
|
|
4926
4972
|
logger.stopSpinner(true, `Uploaded ${videoAssets.length} video(s)`);
|
|
@@ -4962,7 +5008,8 @@ Or use the --bump flag:
|
|
|
4962
5008
|
content_type: a.contentType
|
|
4963
5009
|
}))
|
|
4964
5010
|
})
|
|
4965
|
-
}
|
|
5011
|
+
},
|
|
5012
|
+
env
|
|
4966
5013
|
);
|
|
4967
5014
|
const pubData = await pubResponse.json();
|
|
4968
5015
|
const pubBody = pubData.statusCode ? pubData.body : pubData;
|
|
@@ -4988,6 +5035,12 @@ Or use the --bump flag:
|
|
|
4988
5035
|
}
|
|
4989
5036
|
if (assetUploads.length > 0) {
|
|
4990
5037
|
logger.startSpinner(`Uploading ${assetUploads.length} asset(s) to S3...`);
|
|
5038
|
+
if (assetUploads[0]) {
|
|
5039
|
+
logger.log(
|
|
5040
|
+
` [debug] sample presigned PUT URL: ${assetUploads[0].upload_url}`
|
|
5041
|
+
);
|
|
5042
|
+
logger.log(` [debug] sample s3_key: ${assetUploads[0].s3_key}`);
|
|
5043
|
+
}
|
|
4991
5044
|
const CONCURRENCY = 8;
|
|
4992
5045
|
const byHashedPath = new Map(regularAssets.map((a) => [a.hashedPath, a]));
|
|
4993
5046
|
const queue = [...assetUploads];
|
|
@@ -5014,6 +5067,13 @@ Or use the --bump flag:
|
|
|
5014
5067
|
body: buf
|
|
5015
5068
|
});
|
|
5016
5069
|
if (!res.ok) {
|
|
5070
|
+
if (failed === 0) {
|
|
5071
|
+
const errBody = await res.text().catch(() => "(unreadable)");
|
|
5072
|
+
logger.log(` [debug] PUT ${item.upload_url}`);
|
|
5073
|
+
logger.log(
|
|
5074
|
+
` [debug] response ${res.status} ${res.statusText}: ${errBody.slice(0, 500)}`
|
|
5075
|
+
);
|
|
5076
|
+
}
|
|
5017
5077
|
throw new Error(`HTTP ${res.status}`);
|
|
5018
5078
|
}
|
|
5019
5079
|
uploaded++;
|
|
@@ -5098,7 +5158,8 @@ Or use the --bump flag:
|
|
|
5098
5158
|
try {
|
|
5099
5159
|
const confirmResponse = await authenticatedFetch(
|
|
5100
5160
|
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/confirm`,
|
|
5101
|
-
{ method: "POST" }
|
|
5161
|
+
{ method: "POST" },
|
|
5162
|
+
env
|
|
5102
5163
|
);
|
|
5103
5164
|
const confirmData = await confirmResponse.json();
|
|
5104
5165
|
const confirmBody = confirmData.statusCode ? confirmData.body : confirmData;
|
|
@@ -5144,9 +5205,9 @@ Or use the --bump flag:
|
|
|
5144
5205
|
}
|
|
5145
5206
|
logger.newLine();
|
|
5146
5207
|
logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
|
|
5147
|
-
await uploadThumbnail(apiUrl, themeId, themePath, distDir);
|
|
5208
|
+
await uploadThumbnail(apiUrl, themeId, themePath, distDir, env);
|
|
5148
5209
|
}
|
|
5149
|
-
async function uploadThumbnail(apiUrl, themeId, themePath, distDir) {
|
|
5210
|
+
async function uploadThumbnail(apiUrl, themeId, themePath, distDir, env = "dev") {
|
|
5150
5211
|
const THUMBNAIL_CANDIDATES = [
|
|
5151
5212
|
{ file: "thumbnail.png", mime: "image/png" },
|
|
5152
5213
|
{ file: "thumbnail.jpg", mime: "image/jpeg" },
|
|
@@ -5181,9 +5242,19 @@ async function uploadThumbnail(apiUrl, themeId, themePath, distDir) {
|
|
|
5181
5242
|
}
|
|
5182
5243
|
}
|
|
5183
5244
|
logger.startSpinner("Uploading thumbnail...");
|
|
5245
|
+
const imageUploadUrl = `${apiUrl}/media/images/upload`;
|
|
5246
|
+
const imageRequestBody = {
|
|
5247
|
+
prefix: `themes/${themeId}`,
|
|
5248
|
+
image: imageBase64 ? `${imageBase64.slice(0, 60)}... [${Math.round(imageBase64.length * 3 / 4 / 1024)} KB]` : null,
|
|
5249
|
+
name: "thumbnail.png"
|
|
5250
|
+
};
|
|
5251
|
+
logger.log(` \u2192 POST ${imageUploadUrl}`);
|
|
5252
|
+
logger.log(
|
|
5253
|
+
` \u2192 body: ${JSON.stringify({ ...imageRequestBody, image: imageBase64 ? `<base64 ${mimeType} truncated>` : null })}`
|
|
5254
|
+
);
|
|
5184
5255
|
try {
|
|
5185
5256
|
const uploadRes = await authenticatedFetch(
|
|
5186
|
-
|
|
5257
|
+
imageUploadUrl,
|
|
5187
5258
|
{
|
|
5188
5259
|
method: "POST",
|
|
5189
5260
|
body: JSON.stringify({
|
|
@@ -5191,30 +5262,56 @@ async function uploadThumbnail(apiUrl, themeId, themePath, distDir) {
|
|
|
5191
5262
|
image: imageBase64,
|
|
5192
5263
|
name: "thumbnail.png"
|
|
5193
5264
|
})
|
|
5194
|
-
}
|
|
5265
|
+
},
|
|
5266
|
+
env
|
|
5195
5267
|
);
|
|
5196
|
-
|
|
5268
|
+
logger.log(` \u2190 HTTP ${uploadRes.status} ${uploadRes.statusText}`);
|
|
5269
|
+
const uploadRawText = await uploadRes.text();
|
|
5270
|
+
logger.log(` \u2190 raw response: ${uploadRawText.slice(0, 500)}`);
|
|
5271
|
+
let uploadData;
|
|
5272
|
+
try {
|
|
5273
|
+
uploadData = JSON.parse(uploadRawText);
|
|
5274
|
+
} catch {
|
|
5275
|
+
throw new Error(
|
|
5276
|
+
`Image upload returned non-JSON (HTTP ${uploadRes.status}): ${uploadRawText.slice(0, 200)}`
|
|
5277
|
+
);
|
|
5278
|
+
}
|
|
5197
5279
|
const uploadBody = uploadData.statusCode ? uploadData.body : uploadData;
|
|
5198
5280
|
if (!uploadRes.ok || !uploadBody.url) {
|
|
5199
|
-
throw new Error(
|
|
5281
|
+
throw new Error(
|
|
5282
|
+
`Image upload failed \u2014 HTTP ${uploadRes.status}: ${uploadBody.error || uploadBody.message || JSON.stringify(uploadBody).slice(0, 300)}`
|
|
5283
|
+
);
|
|
5200
5284
|
}
|
|
5285
|
+
const patchUrl = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}`;
|
|
5286
|
+
logger.log(` \u2192 PATCH ${patchUrl}`);
|
|
5201
5287
|
const patchRes = await authenticatedFetch(
|
|
5202
|
-
|
|
5288
|
+
patchUrl,
|
|
5203
5289
|
{
|
|
5204
5290
|
method: "PATCH",
|
|
5205
5291
|
body: JSON.stringify({ thumbnail_url: uploadBody.url })
|
|
5206
|
-
}
|
|
5292
|
+
},
|
|
5293
|
+
env
|
|
5207
5294
|
);
|
|
5295
|
+
logger.log(` \u2190 HTTP ${patchRes.status} ${patchRes.statusText}`);
|
|
5208
5296
|
if (!patchRes.ok) {
|
|
5209
|
-
const
|
|
5210
|
-
|
|
5211
|
-
|
|
5297
|
+
const patchRawText = await patchRes.text();
|
|
5298
|
+
logger.log(` \u2190 raw response: ${patchRawText.slice(0, 500)}`);
|
|
5299
|
+
let patchBody = {};
|
|
5300
|
+
try {
|
|
5301
|
+
patchBody = JSON.parse(patchRawText);
|
|
5302
|
+
} catch {
|
|
5303
|
+
}
|
|
5304
|
+
patchBody = patchBody.statusCode ? patchBody.body : patchBody;
|
|
5305
|
+
throw new Error(
|
|
5306
|
+
`Thumbnail patch failed \u2014 HTTP ${patchRes.status}: ${patchBody.error || patchBody.message || patchRawText.slice(0, 200)}`
|
|
5307
|
+
);
|
|
5212
5308
|
}
|
|
5213
5309
|
logger.stopSpinner(true, "Thumbnail set");
|
|
5214
5310
|
} catch (err) {
|
|
5215
|
-
logger.stopSpinner(false, "Thumbnail upload
|
|
5311
|
+
logger.stopSpinner(false, "Thumbnail upload failed");
|
|
5312
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
5216
5313
|
logger.info(
|
|
5217
|
-
|
|
5314
|
+
"Theme published successfully. Thumbnail can be updated later."
|
|
5218
5315
|
);
|
|
5219
5316
|
}
|
|
5220
5317
|
}
|
|
@@ -5278,25 +5375,40 @@ async function findFreePort(start) {
|
|
|
5278
5375
|
srv.on("error", () => resolve(findFreePort(start + 1)));
|
|
5279
5376
|
});
|
|
5280
5377
|
}
|
|
5281
|
-
async function uploadVideoMultipart(apiUrl, themeId, video) {
|
|
5378
|
+
async function uploadVideoMultipart(apiUrl, themeId, video, env = "dev") {
|
|
5282
5379
|
const fileName = path9__default.default.basename(video.originalPath);
|
|
5380
|
+
const videoInitUrl = `${apiUrl}/media/videos/multipart/init`;
|
|
5381
|
+
const videoInitBody = {
|
|
5382
|
+
file_name: fileName,
|
|
5383
|
+
content_type: video.contentType,
|
|
5384
|
+
file_size: video.size,
|
|
5385
|
+
prefix: `themes/${themeId}/assets`
|
|
5386
|
+
};
|
|
5387
|
+
logger.log(` \u2192 POST ${videoInitUrl}`);
|
|
5388
|
+
logger.log(` \u2192 body: ${JSON.stringify(videoInitBody)}`);
|
|
5283
5389
|
const initRes = await authenticatedFetch(
|
|
5284
|
-
|
|
5390
|
+
videoInitUrl,
|
|
5285
5391
|
{
|
|
5286
5392
|
method: "POST",
|
|
5287
|
-
body: JSON.stringify(
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
file_size: video.size,
|
|
5291
|
-
prefix: `themes/${themeId}/assets`
|
|
5292
|
-
})
|
|
5293
|
-
}
|
|
5393
|
+
body: JSON.stringify(videoInitBody)
|
|
5394
|
+
},
|
|
5395
|
+
env
|
|
5294
5396
|
);
|
|
5295
|
-
|
|
5397
|
+
logger.log(` \u2190 HTTP ${initRes.status} ${initRes.statusText}`);
|
|
5398
|
+
const initRawText = await initRes.text();
|
|
5399
|
+
logger.log(` \u2190 raw response: ${initRawText.slice(0, 500)}`);
|
|
5400
|
+
let initData;
|
|
5401
|
+
try {
|
|
5402
|
+
initData = JSON.parse(initRawText);
|
|
5403
|
+
} catch {
|
|
5404
|
+
throw new Error(
|
|
5405
|
+
`Video init returned non-JSON (HTTP ${initRes.status}): ${initRawText.slice(0, 200)}`
|
|
5406
|
+
);
|
|
5407
|
+
}
|
|
5296
5408
|
const initBody = initData.statusCode ? initData.body : initData;
|
|
5297
5409
|
if (!initRes.ok || !initBody.upload_id) {
|
|
5298
5410
|
throw new Error(
|
|
5299
|
-
`Init multipart failed for ${fileName}: ${initBody.error ||
|
|
5411
|
+
`Init multipart failed for ${fileName} \u2014 HTTP ${initRes.status}: ${initBody.error || initBody.message || JSON.stringify(initBody).slice(0, 300)}`
|
|
5300
5412
|
);
|
|
5301
5413
|
}
|
|
5302
5414
|
const { upload_id, file_key, chunk_size, chunk_urls } = initBody;
|
|
@@ -5331,25 +5443,47 @@ async function uploadVideoMultipart(apiUrl, themeId, video) {
|
|
|
5331
5443
|
)
|
|
5332
5444
|
);
|
|
5333
5445
|
parts.sort((a, b) => a.part_number - b.part_number);
|
|
5446
|
+
const videoCompleteUrl = `${apiUrl}/media/videos/multipart/complete`;
|
|
5447
|
+
logger.log(` \u2192 POST ${videoCompleteUrl}`);
|
|
5448
|
+
logger.log(
|
|
5449
|
+
` \u2192 body: ${JSON.stringify({ upload_id, file_key, parts: `[${parts.length} parts]` })}`
|
|
5450
|
+
);
|
|
5334
5451
|
const completeRes = await authenticatedFetch(
|
|
5335
|
-
|
|
5452
|
+
videoCompleteUrl,
|
|
5336
5453
|
{
|
|
5337
5454
|
method: "POST",
|
|
5338
5455
|
body: JSON.stringify({ upload_id, file_key, parts })
|
|
5339
|
-
}
|
|
5456
|
+
},
|
|
5457
|
+
env
|
|
5340
5458
|
);
|
|
5341
|
-
|
|
5459
|
+
logger.log(` \u2190 HTTP ${completeRes.status} ${completeRes.statusText}`);
|
|
5460
|
+
const completeRawText = await completeRes.text();
|
|
5461
|
+
logger.log(` \u2190 raw response: ${completeRawText.slice(0, 500)}`);
|
|
5462
|
+
let completeData;
|
|
5463
|
+
try {
|
|
5464
|
+
completeData = JSON.parse(completeRawText);
|
|
5465
|
+
} catch {
|
|
5466
|
+
throw new Error(
|
|
5467
|
+
`Video complete returned non-JSON (HTTP ${completeRes.status}): ${completeRawText.slice(0, 200)}`
|
|
5468
|
+
);
|
|
5469
|
+
}
|
|
5342
5470
|
const completeBody = completeData.statusCode ? completeData.body : completeData;
|
|
5343
5471
|
if (!completeRes.ok || !completeBody.url) {
|
|
5344
5472
|
try {
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5473
|
+
const abortUrl = `${apiUrl}/media/videos/multipart/abort`;
|
|
5474
|
+
logger.log(` \u2192 POST ${abortUrl} (cleanup)`);
|
|
5475
|
+
await authenticatedFetch(
|
|
5476
|
+
abortUrl,
|
|
5477
|
+
{
|
|
5478
|
+
method: "POST",
|
|
5479
|
+
body: JSON.stringify({ upload_id, file_key })
|
|
5480
|
+
},
|
|
5481
|
+
env
|
|
5482
|
+
);
|
|
5349
5483
|
} catch {
|
|
5350
5484
|
}
|
|
5351
5485
|
throw new Error(
|
|
5352
|
-
`Complete multipart failed for ${fileName}: ${completeBody.error ||
|
|
5486
|
+
`Complete multipart failed for ${fileName} \u2014 HTTP ${completeRes.status}: ${completeBody.error || completeBody.message || JSON.stringify(completeBody).slice(0, 300)}`
|
|
5353
5487
|
);
|
|
5354
5488
|
}
|
|
5355
5489
|
return completeBody.url;
|
|
@@ -5612,7 +5746,11 @@ program.command("init").description("Create a new OneX theme project").argument(
|
|
|
5612
5746
|
"-t, --template <template>",
|
|
5613
5747
|
"Template to use (default, minimal)",
|
|
5614
5748
|
"default"
|
|
5615
|
-
).option("--no-install", "Skip installing dependencies").option("--git", "Initialize git repository").option("-y, --yes", "Skip prompts and use defaults").
|
|
5749
|
+
).option("--no-install", "Skip installing dependencies").option("--git", "Initialize git repository").option("-y, --yes", "Skip prompts and use defaults").option(
|
|
5750
|
+
"--env <env>",
|
|
5751
|
+
"Target environment: dev or prod (default: dev)",
|
|
5752
|
+
"dev"
|
|
5753
|
+
).action(initCommand);
|
|
5616
5754
|
program.command("create:section").alias("cs").description("Create a new section").argument("<name>", "Name of the section (e.g., hero, features)").option("-t, --theme <theme>", "Theme to create section in").option(
|
|
5617
5755
|
"-c, --category <category>",
|
|
5618
5756
|
"Section category (headers, content, footers)"
|
|
@@ -5639,16 +5777,36 @@ program.command("download").description("Download a published theme via the webs
|
|
|
5639
5777
|
"-v, --version <version>",
|
|
5640
5778
|
"Theme version (default: latest)",
|
|
5641
5779
|
"latest"
|
|
5642
|
-
).option(
|
|
5780
|
+
).option(
|
|
5781
|
+
"--env <env>",
|
|
5782
|
+
"Target environment: dev or prod (default: dev)",
|
|
5783
|
+
"dev"
|
|
5784
|
+
).option("-b, --bucket <name>", "[deprecated] ignored").option("-o, --output <dir>", "Output directory", "./active-theme").action(downloadCommand);
|
|
5643
5785
|
program.command("clone").description("Clone theme source code via the website-api").argument("<theme-name>", "Theme to clone").option(
|
|
5644
5786
|
"-v, --version <version>",
|
|
5645
5787
|
"Theme version (default: latest)",
|
|
5646
5788
|
"latest"
|
|
5647
|
-
).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").option(
|
|
5789
|
+
).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").option(
|
|
5790
|
+
"--env <env>",
|
|
5791
|
+
"Target environment: dev or prod (default: dev)",
|
|
5792
|
+
"dev"
|
|
5793
|
+
).option("-b, --bucket <name>", "[deprecated] ignored").option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
|
|
5648
5794
|
program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
|
|
5649
|
-
program.command("login").description("Login to OneX platform").
|
|
5650
|
-
|
|
5651
|
-
|
|
5795
|
+
program.command("login").description("Login to OneX platform").option(
|
|
5796
|
+
"--env <env>",
|
|
5797
|
+
"Target environment: dev or prod (default: dev)",
|
|
5798
|
+
"dev"
|
|
5799
|
+
).action(loginCommand);
|
|
5800
|
+
program.command("logout").description("Logout from OneX platform").option(
|
|
5801
|
+
"--env <env>",
|
|
5802
|
+
"Target environment: dev or prod (default: dev)",
|
|
5803
|
+
"dev"
|
|
5804
|
+
).action(logoutCommand);
|
|
5805
|
+
program.command("whoami").description("Show current logged-in developer").option(
|
|
5806
|
+
"--env <env>",
|
|
5807
|
+
"Target environment: dev or prod (default: dev)",
|
|
5808
|
+
"dev"
|
|
5809
|
+
).action(whoamiCommand);
|
|
5652
5810
|
var mcpCmd = program.command("mcp").description("Manage MCP server registration and AI-context files");
|
|
5653
5811
|
mcpCmd.command("setup").description(
|
|
5654
5812
|
"Install .mcp.json + CLAUDE.md + AGENTS.md + .cursorrules into the current theme"
|
|
@@ -5660,6 +5818,10 @@ mcpCmd.command("doctor").description("Diagnose MCP setup in the current theme di
|
|
|
5660
5818
|
program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").option(
|
|
5661
5819
|
"--bump <type>",
|
|
5662
5820
|
"Auto-bump version before publish (patch|minor|major)"
|
|
5821
|
+
).option(
|
|
5822
|
+
"--env <env>",
|
|
5823
|
+
"Target environment: dev or prod (default: dev)",
|
|
5824
|
+
"dev"
|
|
5663
5825
|
).action(publishCommand);
|
|
5664
5826
|
program.configureOutput({
|
|
5665
5827
|
writeErr: (str) => process.stderr.write(chalk4__default.default.red(str))
|