@titanpl/cli 1.0.0

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/index.js ADDED
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ import { buildCommand } from "./src/commands/build.js";
8
+ import { devCommand } from "./src/commands/dev.js";
9
+ import { startCommand } from "./src/commands/start.js";
10
+ import { migrateCommand } from "./src/commands/migrate.js";
11
+ import { initCommand } from "./src/commands/init.js";
12
+
13
+ /* -------------------------------------------------------
14
+ * Resolve __dirname (ESM safe)
15
+ * ----------------------------------------------------- */
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+
19
+ /* -------------------------------------------------------
20
+ * Colors (Old Titan Theme Style)
21
+ * ----------------------------------------------------- */
22
+ const cyan = (t) => `\x1b[36m${t}\x1b[0m`;
23
+ const green = (t) => `\x1b[32m${t}\x1b[0m`;
24
+ const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
25
+ const red = (t) => `\x1b[31m${t}\x1b[0m`;
26
+ const bold = (t) => `\x1b[1m${t}\x1b[0m`;
27
+ const gray = (t) => `\x1b[90m${t}\x1b[0m`;
28
+
29
+ /* -------------------------------------------------------
30
+ * Version
31
+ * ----------------------------------------------------- */
32
+ let VERSION = "1.0.0";
33
+
34
+ try {
35
+ const pkg = JSON.parse(
36
+ fs.readFileSync(path.join(__dirname, "package.json"), "utf8")
37
+ );
38
+ VERSION = pkg.version;
39
+ } catch { }
40
+
41
+ /* -------------------------------------------------------
42
+ * Help Output
43
+ * ----------------------------------------------------- */
44
+ function help() {
45
+ console.log(`
46
+ ${bold(cyan("╭───────────────────────────────────────────────╮"))}
47
+ ${bold(cyan("│"))} 🪐 ${bold(cyan("Titan Planet"))} CLI ${gray(`v${VERSION}`.padEnd(6, ' '))} ${bold(cyan("│"))}
48
+ ${bold(cyan("╰───────────────────────────────────────────────╯"))}
49
+
50
+ ${yellow("Usage:")} ${bold("titan <command> [options]")}
51
+
52
+ ${bold("Commands:")}
53
+ ${cyan("init")} ${gray("Scaffold a new Titan project")}
54
+ ${cyan("build")} ${gray("Compile actions and build production dist")}
55
+ ${cyan("dev")} ${gray("Start the Gravity Engine in dev/watch mode")}
56
+ ${cyan("start")} ${gray("Start the production Gravity Engine")}
57
+ ${cyan("migrate")} ${gray("Migrate a legacy project to the new architecture")}
58
+
59
+ ${bold("Options:")}
60
+ ${cyan("-v, --version")} ${gray("Output the current version")}
61
+ ${cyan("-h, --help")} ${gray("Display this help message")}
62
+
63
+ ${gray(" The Titan Planet Engine runs your JS/TS server natively without Node.js. ")}
64
+ ${cyan("https://titan-docs-ez.vercel.app")}
65
+ `);
66
+ }
67
+
68
+ /* -------------------------------------------------------
69
+ * CLI Router
70
+ * ----------------------------------------------------- */
71
+ const cmd = process.argv[2];
72
+
73
+ (async () => {
74
+ try {
75
+ // -------------------------------------------------------
76
+ // Legacy Check
77
+ // -------------------------------------------------------
78
+ if (cmd !== 'migrate' && cmd !== 'init') {
79
+ const legacyCargo = path.join(process.cwd(), "server", "Cargo.toml");
80
+ if (fs.existsSync(legacyCargo)) {
81
+ console.log(yellow(`\n⚠️ This project uses legacy server architecture. Migration recommended.`));
82
+ console.log(`Please run: ${bold(cyan('titan migrate'))}\n`);
83
+ process.exit(1);
84
+ }
85
+ }
86
+
87
+ // -------------------------------------------------------
88
+ // Old tit / titan detection note
89
+ // -------------------------------------------------------
90
+ const scriptBase = path.basename(process.argv[1]);
91
+ if (scriptBase === 'tit') {
92
+ console.log(yellow(`\n⚠️ [Notice]: \`tit\` is deprecated. Please use \`titan\` instead.\n`));
93
+ }
94
+
95
+
96
+ switch (cmd) {
97
+ case "init": {
98
+ const projectName = process.argv[3];
99
+ let template = null;
100
+ const tIndex = process.argv.indexOf("-t") !== -1 ? process.argv.indexOf("-t") : process.argv.indexOf("--template");
101
+ if (tIndex !== -1 && process.argv[tIndex + 1]) {
102
+ template = process.argv[tIndex + 1];
103
+ }
104
+ await initCommand(projectName, template);
105
+ break;
106
+ }
107
+
108
+ case "build":
109
+ console.log(cyan("→ Building Titan project..."));
110
+ await buildCommand();
111
+ console.log(green("✔ Build complete"));
112
+ break;
113
+
114
+ case "dev":
115
+ await devCommand();
116
+ break;
117
+
118
+ case "start":
119
+ console.log(cyan("→ Starting Titan Server..."));
120
+ startCommand();
121
+ break;
122
+
123
+ case "migrate":
124
+ await migrateCommand();
125
+ break;
126
+
127
+ case "--version":
128
+ case "-v":
129
+ case "version":
130
+ console.log(cyan(`Titan CLI v${VERSION}`));
131
+ break;
132
+
133
+ default:
134
+ help();
135
+ }
136
+ } catch (err) {
137
+ console.error(red("✖ Titan CLI Error"));
138
+ console.error(gray(err?.message || err));
139
+ process.exit(1);
140
+ }
141
+ })();
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@titanpl/cli",
3
+ "version": "1.0.0",
4
+ "description": "The gravity and other builtins binary of TitanPl",
5
+ "keywords": [
6
+ "titanpl",
7
+ "titan",
8
+ "ezetgalaxy",
9
+ "cli"
10
+ ],
11
+ "license": "ISC",
12
+ "author": "ezetgalaxy",
13
+ "type": "module",
14
+ "main": "index.js",
15
+ "scripts": {
16
+ "test": "echo \"Error: no test specified\" && exit 1"
17
+ },
18
+ "bin": {
19
+ "titan": "./index.js"
20
+ },
21
+ "optionalDependencies": {
22
+ "@titanpl/engine-win32-x64": "26.16.0",
23
+ "@titanpl/engine-linux-x64": "26.16.0",
24
+ "@titanpl/engine-darwin-arm64": "26.16.0"
25
+ },
26
+ "dependencies": {
27
+ "@titanpl/packet": "26.16.0",
28
+ "prompts": "^2.4.2",
29
+ "commander": "^11.0.0",
30
+ "chalk": "^4.1.2"
31
+ }
32
+ }
@@ -0,0 +1,6 @@
1
+ import { build } from "@titanpl/packet";
2
+
3
+ export async function buildCommand() {
4
+ const dist = await build(process.cwd());
5
+ console.log("✔ Build complete →", dist);
6
+ }
@@ -0,0 +1,7 @@
1
+ import { startEngine } from "../engine.js";
2
+
3
+ export async function devCommand() {
4
+ // The Titan Engine now handles all compilation, watch, and routing natively.
5
+ // There is no need for JS-based watchers or Node.js process managers.
6
+ startEngine(true);
7
+ }
@@ -0,0 +1,149 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { spawn } from 'child_process';
5
+ import prompts from 'prompts';
6
+ import chalk from 'chalk';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ export function copyDir(src, dest, excludes = []) {
12
+ fs.mkdirSync(dest, { recursive: true });
13
+
14
+ for (const file of fs.readdirSync(src)) {
15
+ if (excludes.includes(file)) continue;
16
+
17
+ const srcPath = path.join(src, file);
18
+ const destPath = path.join(dest, file);
19
+
20
+ if (fs.lstatSync(srcPath).isDirectory()) {
21
+ copyDir(srcPath, destPath, excludes);
22
+ } else {
23
+ fs.copyFileSync(srcPath, destPath);
24
+ }
25
+ }
26
+ }
27
+
28
+ export async function initCommand(projectName, templateName) {
29
+ let projName = projectName;
30
+
31
+ if (!projName) {
32
+ const res = await prompts({
33
+ type: 'text',
34
+ name: 'name',
35
+ message: 'Project name:',
36
+ initial: 'my-titan-app'
37
+ });
38
+ projName = res.name;
39
+ }
40
+
41
+ if (!projName) {
42
+ console.log(chalk.red("✖ Initialization cancelled."));
43
+ process.exit(1);
44
+ }
45
+
46
+ let selectedTemplate = templateName;
47
+
48
+ if (!selectedTemplate) {
49
+ const langRes = await prompts({
50
+ type: 'select',
51
+ name: 'value',
52
+ message: 'Select language:',
53
+ choices: [
54
+ { title: 'JavaScript', value: 'js' },
55
+ { title: 'TypeScript', value: 'ts' },
56
+ ],
57
+ initial: 0
58
+ });
59
+
60
+ if (!langRes.value) {
61
+ console.log(chalk.red("✖ Operation cancelled."));
62
+ process.exit(1);
63
+ }
64
+ const lang = langRes.value;
65
+
66
+ const archRes = await prompts({
67
+ type: 'select',
68
+ name: 'value',
69
+ message: 'Select template:',
70
+ choices: [
71
+ {
72
+ title: `Standard (${lang.toUpperCase()})`,
73
+ description: `Standard Titan app with ${lang.toUpperCase()} actions`,
74
+ value: 'standard'
75
+ },
76
+ {
77
+ title: `Rust + ${lang.toUpperCase()} (Hybrid)`,
78
+ description: `High-performance Rust actions + ${lang.toUpperCase()} flexibility`,
79
+ value: 'hybrid'
80
+ }
81
+ ],
82
+ initial: 0
83
+ });
84
+
85
+ if (!archRes.value) {
86
+ console.log(chalk.red("✖ Operation cancelled."));
87
+ process.exit(1);
88
+ }
89
+ const arch = archRes.value;
90
+
91
+ if (lang === 'js') {
92
+ selectedTemplate = arch === 'standard' ? 'js' : 'rust-js';
93
+ } else {
94
+ selectedTemplate = arch === 'standard' ? 'ts' : 'rust-ts';
95
+ }
96
+ }
97
+
98
+ const target = path.resolve(process.cwd(), projName);
99
+ const templateDir = path.resolve(__dirname, '..', '..', '..', '..', 'templates', selectedTemplate);
100
+ const commonDir = path.resolve(__dirname, '..', '..', '..', '..', 'templates', 'common');
101
+
102
+ if (!fs.existsSync(templateDir) || !fs.existsSync(commonDir)) {
103
+ console.log(chalk.red(`✖ Error finding template paths.Are you in a valid Titan monorepo ? `));
104
+ process.exit(1);
105
+ }
106
+
107
+ if (fs.existsSync(target)) {
108
+ console.log(chalk.red(`✖ Directory '${projName}' already exists.`));
109
+ process.exit(1);
110
+ }
111
+
112
+ console.log(chalk.cyan(`\n→ Creating new Titan project in '${projName}'...\n`));
113
+
114
+ // 1. Copy common
115
+ copyDir(commonDir, target, ["_gitignore", "_dockerignore"]);
116
+
117
+ // 2. Copy specific template
118
+ copyDir(templateDir, target, ["_gitignore", "_dockerignore"]);
119
+
120
+ // 3. Dotfiles
121
+ const dotfiles = {
122
+ "_gitignore": ".gitignore",
123
+ "_dockerignore": ".dockerignore",
124
+ ".env": ".env"
125
+ };
126
+ for (const [srcName, destName] of Object.entries(dotfiles)) {
127
+ const src = path.join(commonDir, srcName);
128
+ const dest = path.join(target, destName);
129
+ if (fs.existsSync(src)) {
130
+ fs.copyFileSync(src, dest);
131
+ }
132
+ }
133
+
134
+ console.log(chalk.gray(` Installing dependencies...`));
135
+
136
+ await new Promise((resolve, reject) => {
137
+ const npm = spawn('npm', ['install'], {
138
+ cwd: target,
139
+ stdio: 'inherit',
140
+ shell: true
141
+ });
142
+ npm.on('error', reject);
143
+ npm.on('close', resolve);
144
+ });
145
+
146
+ console.log(chalk.green(`\n✔ Project '${projName}' created successfully!\n`));
147
+ console.log(chalk.yellow(` cd ${projName}`));
148
+ console.log(chalk.yellow(` npm run dev\n`));
149
+ }
@@ -0,0 +1,76 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export async function migrateCommand() {
5
+ const root = process.cwd();
6
+ console.log(`\n🔍 Checking project for legacy Titan architecture...`);
7
+
8
+ const serverDir = path.join(root, 'server');
9
+ const titanDir = path.join(root, 'titan');
10
+ const pkgPath = path.join(root, 'package.json');
11
+
12
+ if (!fs.existsSync(serverDir) && !fs.existsSync(titanDir)) {
13
+ console.log(`✅ This project is already using the modern Titan runtime architecture.`);
14
+ return;
15
+ }
16
+
17
+ console.log(`\n⚠️ Legacy server architecture detected. Migrating to runtime-first model...`);
18
+
19
+ // 1. Delete server/
20
+ if (fs.existsSync(serverDir)) {
21
+ console.log(` Deleting legacy server/ folder...`);
22
+ fs.rmSync(serverDir, { recursive: true, force: true });
23
+ }
24
+
25
+ // 2. Delete titan/ folder
26
+ if (fs.existsSync(titanDir)) {
27
+ console.log(` Deleting legacy titan/ runtime folder...`);
28
+ fs.rmSync(titanDir, { recursive: true, force: true });
29
+ }
30
+
31
+ // 3. Update package.json
32
+ if (fs.existsSync(pkgPath)) {
33
+ console.log(` Updating package.json...`);
34
+ try {
35
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
36
+ let modified = false;
37
+
38
+ // Update scripts
39
+ if (pkg.scripts) {
40
+ if (pkg.scripts.build && pkg.scripts.build.includes('cd server')) {
41
+ pkg.scripts.build = "titan build";
42
+ modified = true;
43
+ }
44
+ if (pkg.scripts.start && pkg.scripts.start.includes('cd server')) {
45
+ pkg.scripts.start = "titan start";
46
+ modified = true;
47
+ }
48
+ if (pkg.scripts.dev && pkg.scripts.dev.includes('titan/dev.js')) {
49
+ pkg.scripts.dev = "titan dev";
50
+ modified = true;
51
+ }
52
+ }
53
+
54
+ // Add dependencies
55
+ pkg.dependencies = pkg.dependencies || {};
56
+ if (!pkg.dependencies['@titan/native']) {
57
+ pkg.dependencies['@titan/native'] = "latest";
58
+ modified = true;
59
+ }
60
+ if (!pkg.dependencies['@titan/route']) {
61
+ pkg.dependencies['@titan/route'] = "latest";
62
+ modified = true;
63
+ }
64
+
65
+ if (modified) {
66
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
67
+ }
68
+ } catch (e) {
69
+ console.log(` ⚠️ Failed to update package.json automatically. Please do it manually.`);
70
+ }
71
+ }
72
+
73
+ console.log(`\n🎉 Migration complete!`);
74
+ console.log(` Please run 'npm install' to fetch the new dependencies.`);
75
+ console.log(` Then run 'titan dev' to start your application.\n`);
76
+ }
@@ -0,0 +1,5 @@
1
+ import { startEngine } from "../engine.js";
2
+
3
+ export function startCommand() {
4
+ startEngine(false);
5
+ }
package/src/engine.js ADDED
@@ -0,0 +1,76 @@
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { createRequire } from 'module';
5
+ import { spawn } from 'child_process';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const require = createRequire(import.meta.url);
12
+
13
+ export function getEngineBinaryPath() {
14
+ const platform = os.platform(); // 'win32', 'linux', 'darwin'
15
+ const arch = os.arch(); // 'x64', 'arm64'
16
+ const pkgName = `@titanpl/engine-${platform}-${arch}`;
17
+ const binName = platform === 'win32' ? 'titan-server.exe' : 'titan-server';
18
+
19
+ // 1. Monorepo Dev Fallback
20
+ const serverExeName = platform === 'win32' ? 'titan-server.exe' : 'titan-server';
21
+ const localFallback = path.resolve(__dirname, '..', '..', '..', 'engine', 'target', 'release', serverExeName);
22
+ if (fs.existsSync(localFallback)) {
23
+ return localFallback;
24
+ }
25
+
26
+ // 2. Resolve installed optional dependency
27
+ try {
28
+ const pkgPath = require.resolve(`${pkgName}/package.json`);
29
+ const pkgDir = path.dirname(pkgPath);
30
+ const binaryPath = path.join(pkgDir, 'bin', binName);
31
+
32
+ if (fs.existsSync(binaryPath)) {
33
+ return binaryPath;
34
+ }
35
+ throw new Error(`Corrupted installation: Binary missing at ${binaryPath}.`);
36
+ } catch (err) {
37
+ console.error(`\n[TITAN FATAL] Unsupported platform: ${platform} (${arch})`);
38
+ console.error(`Or the optional dependency '${pkgName}' failed to install.`);
39
+ console.error(`Error: ${err.message}\n`);
40
+ process.exit(1);
41
+ }
42
+ }
43
+
44
+ export function startEngine(watchMode = false) {
45
+ const distPath = path.resolve(process.cwd(), 'dist');
46
+
47
+ if (!fs.existsSync(distPath)) {
48
+ console.error("❌ 'dist/' directory not found. Please run 'titan build' first.");
49
+ process.exit(1);
50
+ }
51
+
52
+ const binaryPath = getEngineBinaryPath();
53
+
54
+ // Arguments passed to Rust backend
55
+ const args = ['run', distPath];
56
+ if (watchMode) args.push('--watch');
57
+
58
+ console.log(`🚀 Starting Titan Engine...`);
59
+
60
+ const engineProcess = spawn(binaryPath, args, {
61
+ stdio: 'inherit',
62
+ env: {
63
+ ...process.env,
64
+ TITAN_ENV: watchMode ? 'development' : 'production',
65
+ Titan_Dev: watchMode ? '1' : '0'
66
+ }
67
+ });
68
+
69
+ engineProcess.on('close', (code) => {
70
+ if (code !== 0 && code !== null && !engineProcess._isKilling) {
71
+ console.error(`\n❌ [Titan Engine died with exit code ${code}]`);
72
+ }
73
+ });
74
+
75
+ return engineProcess;
76
+ }