@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.
- package/dist/cli.js +279 -8
- 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 =
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
2328
|
-
console.error(`Unknown template "${options.template}". Available: ${Object.keys(
|
|
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 =
|
|
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.
|
|
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
|
+
}
|