Repository: bajrangCoder/acodex-server Branch: main Commit: 281d0a37ac00 Files: 11 Total size: 14.1 KB Directory structure: gitextract_3hiurp82/ ├── .gitignore ├── README.md ├── bin/ │ └── acodex-server ├── package.json ├── src/ │ ├── helpers.ts │ ├── index.ts │ ├── terminal-server.ts │ └── untitled.html ├── tsconfig.json ├── types/ │ └── index.d.ts └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules dist ================================================ FILE: README.md ================================================ # AcodeX-Server > [!WARNING] > It is recommended to avoid using this implementation. Instead, consider using [this alternative](https://github.com/bajrangCoder/acodex_server), which is lightweight, faster, and offers several advantages. Cli for **AcodeX** Plugin ## Installation Run `npm install -g acodex-server` for installing. ``` npm install -g acodex-server ``` ## Usage Run `axs` or `acodex-server` or `acodeX-server` to start server ``` axs ``` and `axs --help` for help > More features coming soon... :) ================================================ FILE: bin/acodex-server ================================================ #!/data/data/com.termux/files/usr/bin/env node require("../dist/index.js"); ================================================ FILE: package.json ================================================ { "name": "acodex-server", "version": "1.1.7", "description": "Server of Acode AcodeX plugin", "main": "dist/index.js", "types": "types/index.d.ts", "scripts": { "start": "ts-node src/index.ts", "build": "webpack" }, "bin": { "axs": "bin/acodex-server", "acodex-server": "bin/acodex-server", "acodeX-server": "bin/acodex-server" }, "repository": { "type": "git", "url": "git+https://github.com/bajrangCoder/acodex-server.git" }, "keywords": [ "acode", "acodeX", "terminal", "server", "cli" ], "author": "Raunak Raj ", "license": "ISC", "bugs": { "url": "https://github.com/bajrangCoder/acodex-server/issues" }, "homepage": "https://github.com/bajrangCoder/acodex-server#readme", "devDependencies": { "@types/node": "^20.16.2", "@types/ws": "^8.5.12", "node-loader": "^2.0.0", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "webpack": "^5.94.0", "webpack-cli": "^5.1.4" }, "dependencies": { "@hono/node-server": "^1.12.2", "@xterm/addon-serialize": "^0.13.0", "@xterm/headless": "^5.5.0", "@xterm/xterm": "^5.5.0", "commander": "^11.1.0", "hono": "^3.12.12", "node-pty": "^1.0.0", "ws": "^8.18.0" } } ================================================ FILE: src/helpers.ts ================================================ import * as os from 'os'; interface Colors { reset: string; black: string; red: string; green: string; yellow: string; blue: string; magenta: string; cyan: string; white: string; [key: string]: string; // Index signature to allow any string key } export function coloredText(text: string | number, color: string): string { const colors: Colors = { reset: '\x1b[0m', black: '\x1b[30m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m', }; const selectedColor = colors[color] || colors.reset; return `${selectedColor}${text}${colors.reset}`; } export function getIPAddress(): string | boolean { const interfaces = os.networkInterfaces(); for (const key in interfaces) { if (interfaces.hasOwnProperty(key)) { const ifaceList = interfaces[key]; if (ifaceList) { for (const iface of ifaceList) { if (!iface.internal && iface.family === 'IPv4') { return iface.address; } } } } } return false; } ================================================ FILE: src/index.ts ================================================ #!/usr/bin/env node import { Command } from "commander"; import { startServer } from "./terminal-server"; import { getIPAddress } from "./helpers"; const program = new Command(); program .name("axs") .description("CLI of AcodeX Acode plugin") .version("1.1.6") .option("-p, --port ", "port to start the server") .option("-i, --ip", "start the server on local network (ip)") .action(options => { if (options.port && options.ip) { const ipdr = getIPAddress(); if (ipdr === false) { console.error("Failed to retrieve IP address."); return; } startServer(options.port, ipdr.toString()); } else if (options.port) { startServer(options.port); } else if (options.ip) { const ipdr = getIPAddress(); if (ipdr === false) { console.error("Failed to retrieve IP address."); return; } startServer(undefined, ipdr.toString()); } else { startServer(); } }); program.parse(); ================================================ FILE: src/terminal-server.ts ================================================ import { Hono } from "hono"; import { cors } from "hono/cors"; import { serve } from "@hono/node-server"; import WebSocket from "ws"; import * as os from "node:os"; import * as pty from "node-pty"; import http from "http"; import { Terminal } from '@xterm/headless'; import { SerializeAddon } from "@xterm/addon-serialize"; import { Session } from "../types"; import { coloredText } from "./helpers"; /** Whether to use binary transport. */ const USE_BINARY = os.platform() !== "win32"; const sessions: Record = {}; export async function startServer( port: number = 8767, host: string = "0.0.0.0" ) { const app = new Hono(); app.use("/*", cors()); const server = serve( { fetch: app.fetch, port, hostname: host, }, (info) => { console.log( `${coloredText( "AcodeX Server", "blue" )} started 🔥\n\nHost: ${coloredText( info.address === "0.0.0.0" ? "localhost" : info.address, "cyan" )}\nPort: ${coloredText(info.port, "cyan")}` ); } ); const wss = new WebSocket.Server({ noServer: true }); app.get("/", (c) => { return c.text("Hello acodeX-server is working😊..."); }); app.post("/terminals", async (c) => { try { const env = { ...process.env }; env["COLORTERM"] = "truecolor"; const { cols, rows } = await c.req.json(); if (typeof cols !== "string" || typeof rows !== "string") { throw new Error("Unexpected query args"); } const colsInt = parseInt(cols, 10); const rowsInt = parseInt(rows, 10); const term = pty.spawn( process.platform === "win32" ? "pwsh.exe" : process.env.SHELL || "bash", [], { name: "xterm-256color", cols: colsInt || 80, rows: rowsInt || 24, cwd: process.platform === "win32" ? undefined : env.HOME, env, encoding: USE_BINARY ? null : "utf8", } ); const xterm = new Terminal({ rows: rowsInt || 24, cols: colsInt || 80, allowProposedApi: true, }); const serializeAddon = new SerializeAddon(); xterm.loadAddon(serializeAddon); console.log("Created terminal with PID: " + term.pid); sessions[term.pid] = { term, xterm, serializeAddon, terminalData: "", }; sessions[term.pid].temporaryDisposable = term.onData((data: string) => { sessions[term.pid].terminalData += data; }); return c.text(term.pid.toString()); } catch (error) { console.error(error); return c.json({ error: "Failed to create terminal" }, 500); } }); app.post("/terminals/:pid/resize", async (c) => { try { const pid = parseInt(c.req.param("pid"), 10); const { cols, rows } = await c.req.json(); const colsInt = parseInt(cols, 10); const rowsInt = parseInt(rows, 10); const { term, xterm } = sessions[pid]; term.resize(colsInt, rowsInt); xterm.resize(colsInt, rowsInt); return c.json({ success: true }); } catch (error) { console.error(error); return c.json({ error: "Failed to resize terminal" }, 500); } }); server.on("upgrade", (request, socket, head) => { const pathname = new URL( request.url || "", `http://${request.headers.host}` ).pathname; if (pathname.startsWith("/terminals/")) { const pid = parseInt(pathname.split("/").pop() || "", 10); wss.handleUpgrade(request, socket, head, (ws) => { wss.emit("connection", ws, request, pid); }); } else { socket.destroy(); } }); wss.on( "connection", (ws: WebSocket, request: http.IncomingMessage, pid: number) => { try { const { term, xterm, serializeAddon, terminalData } = sessions[pid]; console.log("Connected to terminal " + term.pid); if (sessions[pid].temporaryDisposable && terminalData) { sessions[pid].temporaryDisposable?.dispose(); delete sessions[pid].temporaryDisposable; xterm.write(sessions[pid].terminalData); } ws.send(sessions[pid].terminalData); sessions[pid].dataHandler = term.onData(function ( data: string | Uint8Array ) { try { xterm.write(typeof data === "string" ? data : new Uint8Array(data)); ws.send(data); } catch (ex) { // The WebSocket is not open, ignore } }); ws.on("message", function (msg) { term.write(msg.toString()); }); ws.on("close", function () { if (sessions[pid] && sessions[pid].dataHandler) { console.log("Terminal " + pid + " is running in the background."); sessions[pid].dataHandler?.dispose(); delete sessions[pid].dataHandler; sessions[pid].terminalData = serializeAddon.serialize(); } }); } catch (error) { console.error(error); ws.close(); } } ); app.post("/terminals/:pid/terminate", async (c) => { try { const pid = parseInt(c.req.param("pid"), 10); const session = sessions[pid]; if (!session) { // Session not found console.error(`Session with PID ${pid} not found.`); return c.json({ error: `Session with PID ${pid} not found.` }, 404); } const { term, xterm, serializeAddon } = session; if (term) { if (session.dataHandler) { session.dataHandler?.dispose(); delete session.dataHandler; if (session.terminalData) { serializeAddon.dispose(); session.terminalData = serializeAddon.serialize(); } } // Kill the terminal term.kill(); let res = await new Promise((resolve) => { term.onExit(() => { // Dispose of xterm and remove the session after the terminal exits xterm.dispose(); delete sessions[pid]; console.log("Closed terminal " + pid); resolve(true); }); }); if (!res) { return c.json({ error: "Something went wrong." }, 500); } return c.json({ success: true }); } } catch (error) { console.error(error); return c.json({ error: "Failed to terminate terminal" }, 500); } }); app.post("/execute-command", async (c, next) => { try { const env = { ...process.env }; const { command, u_cwd } = await c.req.json(); if (!command) { return c.json({ error: "Command is required." }, 400); } const cwd = u_cwd ? u_cwd : process.env.HOME; // Execute the command using node-pty const term = pty.spawn( process.platform === "win32" ? "cmd.exe" : "bash", ["-c", command], { name: "xterm-256color", cols: 80, rows: 24, cwd: process.platform === "win32" ? undefined : cwd, env } ); const pattern = [ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", ].join("|"); const ansiRegex = new RegExp(pattern, undefined); const ansiRegex2 = new RegExp(pattern, "g"); let output = ""; // Listen for data events (output from the command) term.onData((data) => { output += data; }); // Listen for the process to exit let outputData = await new Promise((resolve) => { term.onExit(() => { // Send the parsed output back to the client let outputData = ansiRegex.test(output) ? output.replace(ansiRegex2, "") : output; resolve(outputData) }); }) return c.json({ output: outputData }) } catch (error) { console.error(error); return c.json({ error: "Failed to execute command" }, 500); } }); } ================================================ FILE: src/untitled.html ================================================ Document ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "esModuleInterop": true, "skipLibCheck": true, "target": "es2022", "strict": true, "module": "CommonJS", "moduleResolution": "node", "outDir": "./dist", "rootDir": "./src" }, "exclude": ["./node_modules"], "include": ["./src/**/*.ts"] } ================================================ FILE: types/index.d.ts ================================================ import { IPty, IDisposable } from 'node-pty'; import { Terminal } from 'xterm-headless'; import { SerializeAddon } from 'xterm-addon-serialize'; type Session = { term: IPty; xterm: Terminal; serializeAddon: SerializeAddon; terminalData: string; temporaryDisposable?: IDisposable; dataHandler?: IDisposable; }; ================================================ FILE: webpack.config.js ================================================ const path = require("path"); module.exports = { entry: "./src/index.ts", target: "node", mode: "production", output: { path: path.resolve(__dirname, "dist"), filename: "index.js" }, resolve: { extensions: [".ts", ".js"] }, module: { rules: [ { test: /\.ts$/, use: "ts-loader", exclude: /node_modules/ }, { test: /\.node$/, loader: "node-loader" } ] }, externals: { "node-pty": "commonjs node-pty", ws: "commonjs ws" } };