@rodyssey/cli 0.1.6 → 0.1.9

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.
Files changed (2) hide show
  1. package/dist/cli.js +98 -48
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2071,7 +2071,7 @@ var {
2071
2071
  // package.json
2072
2072
  var package_default = {
2073
2073
  name: "@rodyssey/cli",
2074
- version: "0.1.6",
2074
+ version: "0.1.9",
2075
2075
  description: "Scaffold new projects from airconcepts templates",
2076
2076
  repository: {
2077
2077
  type: "git",
@@ -2211,7 +2211,7 @@ function storeSession(session) {
2211
2211
  function resolveSessionToken(env) {
2212
2212
  const token = process.env.CMS_TOKEN || getStoredSession(env)?.token;
2213
2213
  if (!token) {
2214
- throw new Error(`No CMS auth token found for [${env}]. Run \`ro app auth login --env ${env}\` first.`);
2214
+ throw new Error(`No CMS auth token found for [${env}]. Run \`ro auth login --env ${env}\` first.`);
2215
2215
  }
2216
2216
  return token;
2217
2217
  }
@@ -2312,7 +2312,7 @@ async function me(options) {
2312
2312
  const storedSession = getStoredSession(options.env);
2313
2313
  if (!options.remote) {
2314
2314
  if (!storedSession) {
2315
- throw new Error(`No local CMS session found for [${options.env}]. Run \`ro app auth login --env ${options.env}\` first.`);
2315
+ throw new Error(`No local CMS session found for [${options.env}]. Run \`ro auth login --env ${options.env}\` first.`);
2316
2316
  }
2317
2317
  return {
2318
2318
  env: storedSession.env,
@@ -2377,12 +2377,13 @@ function replaceInFiles(dir, filenames, search, replace) {
2377
2377
  } catch {}
2378
2378
  }
2379
2379
  }
2380
- function loadEnv(envName) {
2380
+ function loadEnv(envName, options = {}) {
2381
2381
  const files = [];
2382
2382
  if (envName) {
2383
2383
  files.push(`.env.${envName}`);
2384
2384
  }
2385
2385
  files.push(".env");
2386
+ const loaded = {};
2386
2387
  for (const file of files) {
2387
2388
  if (!existsSync2(file))
2388
2389
  continue;
@@ -2400,11 +2401,16 @@ function loadEnv(envName) {
2400
2401
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
2401
2402
  value = value.slice(1, -1);
2402
2403
  }
2403
- if (process.env[key] === undefined) {
2404
- process.env[key] = value;
2404
+ if (loaded[key] === undefined) {
2405
+ loaded[key] = value;
2405
2406
  }
2406
2407
  }
2407
2408
  }
2409
+ for (const [key, value] of Object.entries(loaded)) {
2410
+ if (options.override || process.env[key] === undefined) {
2411
+ process.env[key] = value;
2412
+ }
2413
+ }
2408
2414
  }
2409
2415
 
2410
2416
  // src/create.ts
@@ -4019,26 +4025,13 @@ async function getWebappConfig(options) {
4019
4025
  const CONFIG_URL = getConfigUrl(options);
4020
4026
  if (!CONFIG_URL)
4021
4027
  return;
4022
- ensureDeployToken(options.env);
4023
4028
  const url = new URL(CONFIG_URL);
4024
4029
  url.searchParams.set("webappId", webappId);
4025
4030
  console.log(`⚙️ Pulling webapp config for [${options.env}] environment...`);
4026
4031
  console.log(`\uD83D\uDCCD Config URL: ${url.toString()}`);
4027
4032
  console.log(`\uD83D\uDCCD Webapp ID: ${webappId}
4028
4033
  `);
4029
- const response = await fetch(url, {
4030
- method: "GET",
4031
- headers: {
4032
- Authorization: `Bearer ${process.env.DEPLOY_TOKEN}`,
4033
- Accept: "application/json"
4034
- }
4035
- });
4036
- if (!response.ok) {
4037
- const errorText = await response.text();
4038
- throw new Error(`Config fetch failed: ${response.status} ${response.statusText}
4039
- ${errorText}`);
4040
- }
4041
- const config = await response.json();
4034
+ const config = await fetchWebappConfig(options);
4042
4035
  const output = JSON.stringify(config, null, 2);
4043
4036
  if (options.out) {
4044
4037
  writeFileSync3(options.out, `${output}
@@ -4155,7 +4148,59 @@ function isAffirmative(answer) {
4155
4148
  const trimmed = answer.trim().toLowerCase();
4156
4149
  return trimmed === "" || trimmed === "y" || trimmed === "yes";
4157
4150
  }
4151
+ function isExplicitYes(answer) {
4152
+ const trimmed = answer.trim().toLowerCase();
4153
+ return trimmed === "y" || trimmed === "yes";
4154
+ }
4155
+ function assertDeployOptions(options) {
4156
+ if (options.deploy && options.skipDeploy) {
4157
+ console.error("❌ Error: Use either --deploy or --skip-deploy, not both.");
4158
+ process.exit(1);
4159
+ }
4160
+ }
4161
+ async function maybeDeployProduction(options, webappId, deployToken) {
4162
+ const deployRequested = options.deploy === true;
4163
+ const deploySkipped = options.skipDeploy === true;
4164
+ if (deploySkipped) {
4165
+ console.log(`
4166
+ ↷ Skipping production deploy.`);
4167
+ console.log(`Deploy later with:
4168
+ `);
4169
+ console.log(` ro app deploy -e production
4170
+ `);
4171
+ return;
4172
+ }
4173
+ let shouldDeploy = deployRequested;
4174
+ if (!shouldDeploy) {
4175
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
4176
+ console.log(`
4177
+ ✨ Promotion complete! Deploy to production with:
4178
+ `);
4179
+ console.log(` ro app deploy -e production
4180
+ `);
4181
+ return;
4182
+ }
4183
+ const answer = await prompt(`
4184
+ Deploy to production now? (y/N): `);
4185
+ shouldDeploy = isExplicitYes(answer);
4186
+ }
4187
+ if (!shouldDeploy) {
4188
+ console.log(`
4189
+ ✨ Promotion complete! Deploy to production with:
4190
+ `);
4191
+ console.log(` ro app deploy -e production
4192
+ `);
4193
+ return;
4194
+ }
4195
+ console.log(`
4196
+ \uD83D\uDE80 Deploying promoted webapp to production...`);
4197
+ loadEnv(PROD_ENV, { override: true });
4198
+ process.env.WEBAPP_ID = webappId;
4199
+ process.env.DEPLOY_TOKEN = deployToken;
4200
+ await deploy(PROD_ENV);
4201
+ }
4158
4202
  async function promote(options) {
4203
+ assertDeployOptions(options);
4159
4204
  loadEnv();
4160
4205
  const webappId = process.env.WEBAPP_ID;
4161
4206
  if (!webappId) {
@@ -4207,11 +4252,14 @@ async function promote(options) {
4207
4252
  console.log(`✅ Logged in to ${session.cmsUrl}`);
4208
4253
  const cmsUrl = resolveCmsUrl(PROD_ENV, options.cmsUrl);
4209
4254
  const promoteUrl = options.promoteUrl || `${cmsUrl}/api/cli/webapps/promote`;
4255
+ const promoteBody = { webappId, details };
4210
4256
  console.log(`
4211
4257
  \uD83D\uDE80 Promoting webapp to ${PROD_ENV}...`);
4212
4258
  console.log(`\uD83D\uDCCD Promote URL: ${promoteUrl}`);
4213
- console.log(`\uD83D\uDCCD Webapp ID: ${webappId}
4214
- `);
4259
+ console.log(`\uD83D\uDCCD Webapp ID: ${webappId}`);
4260
+ console.log("\uD83D\uDCE6 Promote body:");
4261
+ console.log(JSON.stringify(promoteBody, null, 2));
4262
+ console.log();
4215
4263
  const response = await fetch(promoteUrl, {
4216
4264
  method: "POST",
4217
4265
  headers: {
@@ -4219,7 +4267,7 @@ async function promote(options) {
4219
4267
  Authorization: `Bearer ${session.token}`,
4220
4268
  "Content-Type": "application/json"
4221
4269
  },
4222
- body: JSON.stringify({ webappId, details })
4270
+ body: JSON.stringify(promoteBody)
4223
4271
  });
4224
4272
  const payload = await readResponsePayload(response);
4225
4273
  if (response.status === 409) {
@@ -4241,11 +4289,7 @@ ${JSON.stringify(payload, null, 2)}`);
4241
4289
  });
4242
4290
  console.log(`✅ Wrote WEBAPP_ID and DEPLOY_TOKEN to ${PROD_ENV_FILE}`);
4243
4291
  console.log(`\uD83D\uDCCD Webapp ID: ${webappId}`);
4244
- console.log(`
4245
- ✨ Promotion complete! Deploy to production with:
4246
- `);
4247
- console.log(` ro app deploy -e production
4248
- `);
4292
+ await maybeDeployProduction(options, webappId, deployToken);
4249
4293
  }
4250
4294
 
4251
4295
  // src/upgrade-template.ts
@@ -4603,6 +4647,28 @@ function addConfigTargetOptions(command) {
4603
4647
  function addConfigSetOptions(command) {
4604
4648
  return addConfigTargetOptions(command).option("--title <title>", "Webapp title (pass the literal 'null' to clear)").option("--description <description>", "Webapp description (pass the literal 'null' to clear)").option("--cover-img <url>", "Cover image URL (pass the literal 'null' to clear)").option("--localization <json-or-file>", "Localization JSON object, path to a JSON file, or 'null' to clear").option("--details <json-or-file>", "Partial WebappDetails JSON or path to a JSON file. Sent as a delta — only include keys you want to change. Do NOT echo a full GET response here, or empty defaults like 'tags: []' will clobber real data.").option("--dry-run", "Print the request payload without sending it");
4605
4649
  }
4650
+ function addAuthCommands(parent) {
4651
+ const auth = parent.command("auth").description("Authenticate with the CMS");
4652
+ auth.command("login").description("Log in to the CMS using a browser callback").option("-e, --env <environment>", "CMS environment (local | development | staging | production)", "development").option("--cms-url <url>", "CMS base URL. Defaults to the selected environment").option("--login-url <url>", "Full browser login URL. Defaults to <cms-url>/auth/cli-login").option("--token-url <url>", "Full token exchange URL. Defaults to <cms-url>/api/auth/cli-token").option("--callback-port <port>", "Local callback port. Defaults to a random free port", parseInt).option("--timeout <seconds>", "Seconds to wait for the browser callback", parseInt, 300).option("--no-open", "Print the login URL without opening a browser").action(async (options) => {
4653
+ const session = await login({
4654
+ env: options.env,
4655
+ cmsUrl: options.cmsUrl,
4656
+ loginUrl: options.loginUrl,
4657
+ tokenUrl: options.tokenUrl,
4658
+ callbackPort: options.callbackPort,
4659
+ open: options.open,
4660
+ timeoutMs: (options.timeout ?? 300) * 1000
4661
+ });
4662
+ console.log(`✅ Logged in to CMS [${session.env}]`);
4663
+ console.log(`\uD83D\uDCCD CMS URL: ${session.cmsUrl}`);
4664
+ });
4665
+ auth.command("me").description("Show the locally stored CMS login session").option("-e, --env <environment>", "CMS environment (local | development | staging | production)", "development").option("--remote", "Call the CMS /me endpoint instead of only reading the local session").option("--cms-url <url>", "CMS base URL for --remote. Defaults to the selected environment or stored session").option("--me-url <url>", "Full me endpoint URL for --remote. Defaults to <cms-url>/api/auth/me").action(async (options) => {
4666
+ const currentUser = await me(options);
4667
+ console.log(JSON.stringify(currentUser, null, 2));
4668
+ });
4669
+ return auth;
4670
+ }
4671
+ addAuthCommands(program);
4606
4672
  app.command("create").argument("<project-name>", "Name of the project to create").option("-t, --template <template>", "Template to use (webapp | webapp-fullstack)").option("--auto", "Create a CMS webapp and write WEBAPP_ID/DEPLOY_TOKEN to .env").option("-e, --env <environment>", "CMS environment for --auto (local | development | staging | production)", "development").option("--cms-url <url>", "CMS base URL for --auto. Defaults to the selected environment").option("--create-url <url>", "Full CMS create endpoint for --auto. Defaults to <cms-url>/api/cli/webapps/create").description("Create a new project from a template").action(async (projectName, options) => {
4607
4673
  let templateName;
4608
4674
  if (options.template) {
@@ -4622,13 +4688,15 @@ app.command("create").argument("<project-name>", "Name of the project to create"
4622
4688
  createUrl: options.createUrl
4623
4689
  });
4624
4690
  });
4625
- app.command("promote").description("Promote the current webapp to production (creates a prod record with the same WEBAPP_ID)").option("--details <json-or-file>", "Full WebappDetails JSON object or path to a JSON file. Skips the source-pull and confirmation prompts when provided.").option("-y, --yes", "Auto-accept pulling the latest details from development").option("--cms-url <url>", "Production CMS base URL. Defaults to the production environment").option("--promote-url <url>", "Full CMS promote endpoint. Defaults to <cms-url>/api/cli/webapps/promote").option("--from <env>", "Source environment to pull details from (testing override). Defaults to development", "development").action(async (options) => {
4691
+ app.command("promote").description("Promote the current webapp to production (creates a prod record with the same WEBAPP_ID)").option("--details <json-or-file>", "Full WebappDetails JSON object or path to a JSON file. Skips the source-pull and confirmation prompts when provided.").option("-y, --yes", "Auto-accept pulling the latest details from development").option("--cms-url <url>", "Production CMS base URL. Defaults to the production environment").option("--promote-url <url>", "Full CMS promote endpoint. Defaults to <cms-url>/api/cli/webapps/promote").option("--from <env>", "Source environment to pull details from (testing override). Defaults to development", "development").option("--deploy", "Deploy to production immediately after promotion").option("--skip-deploy", "Do not ask to deploy after promotion").action(async (options) => {
4626
4692
  await promote({
4627
4693
  details: options.details,
4628
4694
  yes: options.yes,
4629
4695
  cmsUrl: options.cmsUrl,
4630
4696
  promoteUrl: options.promoteUrl,
4631
- from: options.from
4697
+ from: options.from,
4698
+ deploy: options.deploy,
4699
+ skipDeploy: options.skipDeploy
4632
4700
  });
4633
4701
  });
4634
4702
  app.command("update-game-sdk").description("Download and update the GameSDK library, types, and documentation").action(async () => {
@@ -4637,24 +4705,6 @@ app.command("update-game-sdk").description("Download and update the GameSDK libr
4637
4705
  app.command("deploy").description("Build and deploy the webapp to the server").option("-e, --env <environment>", "Target environment (local | development | staging | production)", "development").option("--host <host>", "Override the deploy host").option("--port <port>", "Override the deploy port", parseInt).action(async (options) => {
4638
4706
  await deploy(options.env, { host: options.host, port: options.port });
4639
4707
  });
4640
- var auth = app.command("auth").description("Authenticate with the CMS");
4641
- auth.command("login").description("Log in to the CMS using a browser callback").option("-e, --env <environment>", "CMS environment (local | development | staging | production)", "development").option("--cms-url <url>", "CMS base URL. Defaults to the selected environment").option("--login-url <url>", "Full browser login URL. Defaults to <cms-url>/auth/cli-login").option("--token-url <url>", "Full token exchange URL. Defaults to <cms-url>/api/auth/cli-token").option("--callback-port <port>", "Local callback port. Defaults to a random free port", parseInt).option("--timeout <seconds>", "Seconds to wait for the browser callback", parseInt, 300).option("--no-open", "Print the login URL without opening a browser").action(async (options) => {
4642
- const session = await login({
4643
- env: options.env,
4644
- cmsUrl: options.cmsUrl,
4645
- loginUrl: options.loginUrl,
4646
- tokenUrl: options.tokenUrl,
4647
- callbackPort: options.callbackPort,
4648
- open: options.open,
4649
- timeoutMs: (options.timeout ?? 300) * 1000
4650
- });
4651
- console.log(`✅ Logged in to CMS [${session.env}]`);
4652
- console.log(`\uD83D\uDCCD CMS URL: ${session.cmsUrl}`);
4653
- });
4654
- auth.command("me").description("Show the locally stored CMS login session").option("-e, --env <environment>", "CMS environment (local | development | staging | production)", "development").option("--remote", "Call the CMS /me endpoint instead of only reading the local session").option("--cms-url <url>", "CMS base URL for --remote. Defaults to the selected environment or stored session").option("--me-url <url>", "Full me endpoint URL for --remote. Defaults to <cms-url>/api/auth/me").action(async (options) => {
4655
- const currentUser = await me(options);
4656
- console.log(JSON.stringify(currentUser, null, 2));
4657
- });
4658
4708
  var config = app.command("config").description("Manage webapp metadata config");
4659
4709
  addConfigTargetOptions(config.command("get").description("Pull the current webapp metadata config from the CMS").option("--out <file>", "Write the config JSON to a file")).action(async (options) => {
4660
4710
  await getWebappConfig(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rodyssey/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "Scaffold new projects from airconcepts templates",
5
5
  "repository": {
6
6
  "type": "git",