@rodyssey/cli 0.0.9 → 0.1.1

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 +279 -8
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -2144,9 +2144,274 @@ async function create(projectName, repoUrl, templateName) {
2144
2144
  `);
2145
2145
  }
2146
2146
 
2147
+ // src/deploy.ts
2148
+ import { execSync as execSync2 } from "node:child_process";
2149
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync, unlinkSync } from "node:fs";
2150
+ import { join } from "node:path";
2151
+ var DEVELOPMENT_URL = "https://development-cms.rodyssey.ai/api/webapps/deploy";
2152
+ var STAGING_URL = "https://staging-cms.rodyssey.ai/api/webapps/deploy";
2153
+ var PRODUCTION_URL = "https://cms.rodyssey.ai/api/webapps/deploy";
2154
+ var DEPLOY_URLS = {
2155
+ development: DEVELOPMENT_URL,
2156
+ staging: STAGING_URL,
2157
+ production: PRODUCTION_URL
2158
+ };
2159
+ var BUILD_DIR = "dist";
2160
+ var ZIP_FILE = "webapp-build.zip";
2161
+ var MAX_FILES_PER_BATCH = 5;
2162
+ var MAX_SIZE_PER_BATCH = 30 * 1024 * 1024;
2163
+ function getAllFiles(dirPath, arrayOfFiles = []) {
2164
+ if (!existsSync2(dirPath))
2165
+ return arrayOfFiles;
2166
+ const files = readdirSync(dirPath);
2167
+ files.forEach(function(f) {
2168
+ const fullPath = join(dirPath, f);
2169
+ if (statSync(fullPath).isDirectory()) {
2170
+ arrayOfFiles = getAllFiles(fullPath, arrayOfFiles);
2171
+ } else {
2172
+ arrayOfFiles.push(fullPath);
2173
+ }
2174
+ });
2175
+ return arrayOfFiles;
2176
+ }
2177
+ function fileToBlob(filePath) {
2178
+ const buffer = readFileSync2(filePath);
2179
+ return new Blob([buffer]);
2180
+ }
2181
+ async function deploy(env = "development") {
2182
+ const DEPLOY_URL = DEPLOY_URLS[env];
2183
+ if (!DEPLOY_URL) {
2184
+ console.error(`❌ Unknown environment "${env}". Available: ${Object.keys(DEPLOY_URLS).join(", ")}`);
2185
+ process.exit(1);
2186
+ }
2187
+ const ASSETS_URL = DEPLOY_URL.replace("/webapps/deploy", "/webapps/assets");
2188
+ console.log(`\uD83D\uDE80 Starting deployment process for [${env}] environment...
2189
+ `);
2190
+ console.log(`\uD83D\uDCCD Deploy URL: ${DEPLOY_URL}`);
2191
+ console.log(`\uD83D\uDCCD Assets URL: ${ASSETS_URL}
2192
+ `);
2193
+ if (!process.env.DEPLOY_TOKEN) {
2194
+ console.error("❌ Error: DEPLOY_TOKEN is not set in environment variables.");
2195
+ console.info(`\uD83D\uDCA1 Please check your .env or .env.${env} file.`);
2196
+ process.exit(1);
2197
+ }
2198
+ console.log("\uD83D\uDCE6 Step 1: Building the webapp...");
2199
+ execSync2("npm run build", { stdio: "inherit" });
2200
+ console.log(`✅ Build completed
2201
+ `);
2202
+ const allFiles = getAllFiles(BUILD_DIR);
2203
+ const htmlFiles = allFiles.filter((f) => f.endsWith(".html"));
2204
+ const scriptFiles = allFiles.filter((f) => f.includes("/api/") || f.includes("/cron-jobs/") || f.endsWith("cron.config.json"));
2205
+ const heavyFiles = allFiles.filter((f) => !f.endsWith(".html") && !scriptFiles.includes(f));
2206
+ console.log(`\uD83D\uDCE4 Step 2: Uploading ${heavyFiles.length} heavy assets...`);
2207
+ if (heavyFiles.length > 0) {
2208
+ const batches = [];
2209
+ let currentBatch = [];
2210
+ let currentBatchSize = 0;
2211
+ for (const filePath of heavyFiles) {
2212
+ const size = statSync(filePath).size;
2213
+ if (currentBatch.length >= MAX_FILES_PER_BATCH || currentBatchSize + size > MAX_SIZE_PER_BATCH) {
2214
+ if (currentBatch.length > 0)
2215
+ batches.push(currentBatch);
2216
+ currentBatch = [];
2217
+ currentBatchSize = 0;
2218
+ }
2219
+ currentBatch.push(filePath);
2220
+ currentBatchSize += size;
2221
+ }
2222
+ if (currentBatch.length > 0)
2223
+ batches.push(currentBatch);
2224
+ console.log(`Divided into ${batches.length} batches.`);
2225
+ let batchIndex = 0;
2226
+ const uploadPromises = batches.map(async (batch) => {
2227
+ const formData = new FormData;
2228
+ for (const filePath of batch) {
2229
+ const relativePath = filePath.substring(BUILD_DIR.length + 1).replace(/\\/g, "/");
2230
+ formData.append(relativePath, fileToBlob(filePath), relativePath);
2231
+ }
2232
+ const response = await fetch(ASSETS_URL, {
2233
+ method: "POST",
2234
+ headers: {
2235
+ Authorization: `Bearer ${process.env.DEPLOY_TOKEN}`
2236
+ },
2237
+ body: formData
2238
+ });
2239
+ if (!response.ok) {
2240
+ const errorText = await response.text();
2241
+ throw new Error(`Asset upload failed: ${response.status} ${response.statusText}
2242
+ ${errorText}`);
2243
+ }
2244
+ batchIndex++;
2245
+ console.log(`✅ Batch ${batchIndex}/${batches.length} uploaded successfully`);
2246
+ });
2247
+ await Promise.all(uploadPromises);
2248
+ } else {
2249
+ console.log("✅ No heavy assets to upload");
2250
+ }
2251
+ console.log();
2252
+ if (scriptFiles.length > 0) {
2253
+ console.log(`\uD83D\uDCDC Step 3: Setting up ${scriptFiles.length} scripts (APIs & Crons)...`);
2254
+ const scriptsPayload = { api: {}, cron: {}, cronConfig: null };
2255
+ for (const f of scriptFiles) {
2256
+ const content = readFileSync2(f, "utf-8");
2257
+ const relativePath = f.substring(BUILD_DIR.length + 1).replace(/\\/g, "/");
2258
+ if (relativePath === "cron-jobs/cron.config.json") {
2259
+ scriptsPayload.cronConfig = JSON.parse(content);
2260
+ } else if (relativePath.startsWith("api/")) {
2261
+ const endpoint = relativePath.substring(4, relativePath.lastIndexOf(".js"));
2262
+ scriptsPayload.api[endpoint] = content;
2263
+ } else if (relativePath.startsWith("cron-jobs/")) {
2264
+ const scriptName = relativePath.substring(10);
2265
+ scriptsPayload.cron[scriptName] = content;
2266
+ }
2267
+ }
2268
+ const scriptsUrl = DEPLOY_URL.replace("/webapps/deploy", "/webapps/scripts-setup");
2269
+ const response = await fetch(scriptsUrl, {
2270
+ method: "POST",
2271
+ headers: {
2272
+ Authorization: `Bearer ${process.env.DEPLOY_TOKEN}`,
2273
+ "Content-Type": "application/json"
2274
+ },
2275
+ body: JSON.stringify(scriptsPayload)
2276
+ });
2277
+ if (!response.ok) {
2278
+ const errorText = await response.text();
2279
+ throw new Error(`Scripts setup failed: ${response.status} ${response.statusText}
2280
+ ${errorText}`);
2281
+ }
2282
+ console.log(`✅ Scripts synced successfully`);
2283
+ } else {
2284
+ console.log(`\uD83D\uDCDC Step 3: No scripts found to sync.`);
2285
+ }
2286
+ console.log();
2287
+ console.log(`\uD83D\uDDDC️ Step 4: Zipping ${htmlFiles.length} HTML files...`);
2288
+ if (htmlFiles.length === 0) {
2289
+ console.warn("⚠️ No HTML files found to zip! Deployment might fail if CMS expects an HTML file.");
2290
+ }
2291
+ const relativeHtmlFiles = htmlFiles.map((f) => f.substring(BUILD_DIR.length + 1).replace(/\\/g, "/"));
2292
+ execSync2(`cd ${BUILD_DIR} && zip ../${ZIP_FILE} ${relativeHtmlFiles.join(" ")}`, { stdio: "inherit" });
2293
+ console.log(`✅ Created ${ZIP_FILE}
2294
+ `);
2295
+ console.log("☁️ Step 5: Deploying HTML zip to server...");
2296
+ const zipBuffer = readFileSync2(ZIP_FILE);
2297
+ try {
2298
+ const response = await fetch(DEPLOY_URL, {
2299
+ method: "POST",
2300
+ headers: {
2301
+ Authorization: `Bearer ${process.env.DEPLOY_TOKEN}`,
2302
+ "Content-Type": "application/zip"
2303
+ },
2304
+ body: zipBuffer
2305
+ });
2306
+ if (!response.ok) {
2307
+ const errorText = await response.text();
2308
+ throw new Error(`Deploy failed: ${response.status} ${response.statusText}
2309
+ ${errorText}`);
2310
+ }
2311
+ const result = await response.json();
2312
+ console.log("✅ Deploy completed");
2313
+ console.log(`
2314
+ \uD83D\uDCCB Deployment result:`, result);
2315
+ } catch (error) {
2316
+ console.error("❌ Deploy failed:", error);
2317
+ throw error;
2318
+ } finally {
2319
+ if (existsSync2(ZIP_FILE)) {
2320
+ unlinkSync(ZIP_FILE);
2321
+ console.log(`
2322
+ \uD83E\uDDF9 Cleaned up ${ZIP_FILE}`);
2323
+ }
2324
+ }
2325
+ console.log(`
2326
+ ✨ Deployment successful!`);
2327
+ }
2328
+
2329
+ // src/upgrade-agent.ts
2330
+ import { execSync as execSync3 } from "node:child_process";
2331
+ import { existsSync as existsSync3 } from "node:fs";
2332
+ var TEMPLATES = {
2333
+ webapp: {
2334
+ name: "webapp (SPA)",
2335
+ repo: "https://github.com/airconcepts/webapp-template.git",
2336
+ remoteName: "template",
2337
+ checkoutFiles: ["AGENTS.md", ".agent/", "src/types/webapp.d.ts", "src/types/game-sdk.d.ts"],
2338
+ newFiles: [
2339
+ "src/exp-engine/cli.ts",
2340
+ "src/exp-engine/evaluate.ts",
2341
+ "src/exp-engine/README.md",
2342
+ "src/types/exp-engine.d.ts"
2343
+ ]
2344
+ },
2345
+ "webapp-fullstack": {
2346
+ name: "webapp (Fullstack)",
2347
+ repo: "https://github.com/airconcepts/webapp-template-fullstack.git",
2348
+ remoteName: "template",
2349
+ checkoutFiles: ["AGENTS.md", ".agent/", "app/types/webapp.d.ts", "app/types/game-sdk.d.ts"],
2350
+ newFiles: [
2351
+ "app/exp-engine/cli.ts",
2352
+ "app/exp-engine/evaluate.ts",
2353
+ "app/exp-engine/README.md",
2354
+ "app/types/exp-engine.d.ts"
2355
+ ]
2356
+ }
2357
+ };
2358
+ function detectTemplate() {
2359
+ if (existsSync3("app")) {
2360
+ console.log(`\uD83D\uDD0D Detected fullstack template (found app/ directory)
2361
+ `);
2362
+ return TEMPLATES["webapp-fullstack"];
2363
+ }
2364
+ if (existsSync3("src")) {
2365
+ console.log(`\uD83D\uDD0D Detected SPA template (found src/ directory)
2366
+ `);
2367
+ return TEMPLATES["webapp"];
2368
+ }
2369
+ console.log(`⚠️ Could not detect template type, defaulting to SPA
2370
+ `);
2371
+ return TEMPLATES["webapp"];
2372
+ }
2373
+ async function upgradeAgent() {
2374
+ const template = detectTemplate();
2375
+ try {
2376
+ console.log(`\uD83D\uDD04 Starting Agent Upgrade for ${template.name}...`);
2377
+ let remoteExists = false;
2378
+ try {
2379
+ execSync3(`git remote get-url ${template.remoteName}`, { stdio: "ignore" });
2380
+ remoteExists = true;
2381
+ } catch {}
2382
+ if (!remoteExists) {
2383
+ console.log(`➕ Adding remote '${template.remoteName}'...`);
2384
+ execSync3(`git remote add ${template.remoteName} ${template.repo}`, { stdio: "inherit" });
2385
+ } else {
2386
+ console.log(`ℹ️ Remote '${template.remoteName}' already exists.`);
2387
+ }
2388
+ console.log("⬇️ Fetching latest changes from template...");
2389
+ execSync3(`git fetch ${template.remoteName}`, { stdio: "inherit" });
2390
+ console.log("\uD83D\uDCC2 Updating agent files...");
2391
+ const checkoutList = template.checkoutFiles.join(" ");
2392
+ execSync3(`git checkout ${template.remoteName}/main -- ${checkoutList}`, { stdio: "inherit" });
2393
+ for (const file of template.newFiles) {
2394
+ if (!existsSync3(file)) {
2395
+ console.log(`\uD83D\uDCC2 Checking out ${file}...`);
2396
+ try {
2397
+ execSync3(`git checkout ${template.remoteName}/main -- ${file}`, { stdio: "inherit" });
2398
+ } catch {
2399
+ console.log(`⚠️ Failed to checkout ${file}`);
2400
+ }
2401
+ } else {
2402
+ console.log(`⏭️ Skipping ${file} (already exists)`);
2403
+ }
2404
+ }
2405
+ console.log("✅ Agent upgrade complete! Please check git status for changes.");
2406
+ } catch (error) {
2407
+ console.error("❌ Upgrade failed:", error);
2408
+ process.exit(1);
2409
+ }
2410
+ }
2411
+
2147
2412
  // src/update-game-sdk.ts
2148
2413
  import { mkdir, writeFile } from "node:fs/promises";
2149
- import { dirname, join } from "node:path";
2414
+ import { dirname, join as join2 } from "node:path";
2150
2415
  var BASE_URL = "https://development-app.rodyssey.ai";
2151
2416
  var FILES = [
2152
2417
  {
@@ -2220,7 +2485,7 @@ async function downloadDocumentation(manifest) {
2220
2485
  let failCount = 0;
2221
2486
  for (const doc of manifest.documentation) {
2222
2487
  const url = `${BASE_URL}/skills/${doc.file}`;
2223
- const path3 = join(".agent", "skills", "game-sdk", doc.file);
2488
+ const path3 = join2(".agent", "skills", "game-sdk", doc.file);
2224
2489
  const description = `${doc.title} (${doc.category})`;
2225
2490
  const success = await downloadFile(url, path3, description);
2226
2491
  if (success) {
@@ -2280,7 +2545,7 @@ async function updateGameSdk() {
2280
2545
  }
2281
2546
 
2282
2547
  // src/cli.ts
2283
- var TEMPLATES = {
2548
+ var TEMPLATES2 = {
2284
2549
  webapp: {
2285
2550
  name: "webapp",
2286
2551
  description: "Frontend-only webapp (Vite + React Router 7)",
@@ -2293,7 +2558,7 @@ var TEMPLATES = {
2293
2558
  }
2294
2559
  };
2295
2560
  async function selectTemplate() {
2296
- const entries = Object.values(TEMPLATES);
2561
+ const entries = Object.values(TEMPLATES2);
2297
2562
  console.log(`
2298
2563
  Available templates:
2299
2564
  `);
@@ -2319,23 +2584,29 @@ Available templates:
2319
2584
  });
2320
2585
  });
2321
2586
  }
2322
- program.name("@rodyssey/cli").description("Airconcepts CLI toolkit").version("0.0.5");
2587
+ program.name("@rodyssey/cli").description("Airconcepts CLI toolkit").version("0.1.0");
2323
2588
  var app = program.command("app").description("Manage webapp projects");
2324
2589
  app.command("create").argument("<project-name>", "Name of the project to create").option("-t, --template <template>", "Template to use (webapp | webapp-fullstack)").description("Create a new project from a template").action(async (projectName, options) => {
2325
2590
  let templateName;
2326
2591
  if (options.template) {
2327
- if (!(options.template in TEMPLATES)) {
2328
- console.error(`Unknown template "${options.template}". Available: ${Object.keys(TEMPLATES).join(", ")}`);
2592
+ if (!(options.template in TEMPLATES2)) {
2593
+ console.error(`Unknown template "${options.template}". Available: ${Object.keys(TEMPLATES2).join(", ")}`);
2329
2594
  process.exit(1);
2330
2595
  }
2331
2596
  templateName = options.template;
2332
2597
  } else {
2333
2598
  templateName = await selectTemplate();
2334
2599
  }
2335
- const template = TEMPLATES[templateName];
2600
+ const template = TEMPLATES2[templateName];
2336
2601
  await create(projectName, template.repo, templateName);
2337
2602
  });
2338
2603
  app.command("update-game-sdk").description("Download and update the GameSDK library, types, and documentation").action(async () => {
2339
2604
  await updateGameSdk();
2340
2605
  });
2606
+ app.command("deploy").description("Build and deploy the webapp to the server").option("-e, --env <environment>", "Target environment (development | staging | production)", "development").action(async (options) => {
2607
+ await deploy(options.env);
2608
+ });
2609
+ app.command("upgrade-agent").description("Upgrade agent files from the template repository").action(async () => {
2610
+ await upgradeAgent();
2611
+ });
2341
2612
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rodyssey/cli",
3
- "version": "0.0.9",
3
+ "version": "0.1.1",
4
4
  "description": "Scaffold new projects from airconcepts templates",
5
5
  "bin": {
6
6
  "@rodyssey/cli": "dist/cli.js"
@@ -24,4 +24,4 @@
24
24
  "peerDependencies": {
25
25
  "typescript": "^5"
26
26
  }
27
- }
27
+ }