From 109ad450bc3b4987ee0ab87b331aaae1e2f38703 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Wed, 24 Dec 2025 09:59:12 +0530 Subject: [PATCH 1/4] feat: add acode cli --- src/components/terminal/terminal.js | 36 ++++++++++++ src/components/terminal/terminalManager.js | 63 ++++++++++++++++++++ src/plugins/terminal/scripts/init-alpine.sh | 64 +++++++++++++++++++++ 3 files changed, 163 insertions(+) diff --git a/src/components/terminal/terminal.js b/src/components/terminal/terminal.js index 942039812..cb3539e92 100644 --- a/src/components/terminal/terminal.js +++ b/src/components/terminal/terminal.js @@ -120,6 +120,42 @@ export default class TerminalComponent { // Handle copy/paste keybindings this.setupCopyPasteHandlers(); + + // Handle custom OSC 7777 for acode CLI commands + this.setupOscHandler(); + } + + /** + * Setup custom OSC handler for acode CLI integration + * OSC 7777 format: \e]7777;command;arg1;arg2;...\a + */ + setupOscHandler() { + // Register custom OSC handler for ID 7777 + this.terminal.parser.registerOscHandler(7777, (data) => { + const parts = data.split(";"); + const command = parts[0]; + + switch (command) { + case "open": + this.handleOscOpen(parts[1], parts[2]); + break; + default: + console.warn("Unknown OSC 7777 command:", command); + } + return true; + }); + } + + /** + * Handle OSC open command from acode CLI + * @param {string} type - "file" or "folder" + * @param {string} path - Path to open + */ + handleOscOpen(type, path) { + if (!path) return; + + // Emit event for the app to handle + this.onOscOpen?.(type, path); } /** diff --git a/src/components/terminal/terminalManager.js b/src/components/terminal/terminalManager.js index 816a1c14e..2e0a18ca6 100644 --- a/src/components/terminal/terminalManager.js +++ b/src/components/terminal/terminalManager.js @@ -9,6 +9,8 @@ import "@xterm/xterm/css/xterm.css"; import quickTools from "components/quickTools"; import toast from "components/toast"; import confirm from "dialogs/confirm"; +import openFile from "lib/openFile"; +import openFolder from "lib/openFolder"; import appSettings from "lib/settings"; import helpers from "utils/helpers"; @@ -577,6 +579,30 @@ class TerminalManager { toast(message); }; + // Handle acode CLI open commands (OSC 7777) + terminalComponent.onOscOpen = async (type, path) => { + if (!path) return; + + // Convert proot path + const fileUri = this.convertProotPath(path); + // Extract folder/file name from path + const name = path.split("/").filter(Boolean).pop() || "folder"; + + try { + if (type === "folder") { + // Open folder in sidebar + await openFolder(fileUri, { name, saveState: true, listFiles: true }); + toast(`Opened folder: ${name}`); + } else { + // Open file in editor + await openFile(fileUri, { render: true }); + } + } catch (error) { + console.error("Failed to open from terminal:", error); + toast(`Failed to open: ${path}`); + } + }; + // Store references for cleanup terminalFile._terminalId = terminalId; terminalFile.terminalComponent = terminalComponent; @@ -791,6 +817,43 @@ class TerminalManager { }); } + /** + * Convert proot internal path to app-accessible path + * @param {string} prootPath - Path from inside proot environment + * @returns {string} App filesystem path + */ + convertProotPath(prootPath) { + if (!prootPath) return prootPath; + + const packageName = window.BuildInfo?.packageName || "com.foxdebug.acode"; + const dataDir = `/data/user/0/${packageName}`; + const alpineRoot = `${dataDir}/files/alpine`; + + let convertedPath; + + if (prootPath.startsWith("/public")) { + // /public -> /data/user/0/com.foxdebug.acode/files/public + convertedPath = `file://${dataDir}/files${prootPath}`; + } else if (prootPath.startsWith("/sdcard")) { + // /sdcard + convertedPath = `file://${prootPath}`; + } else if (prootPath.startsWith("/storage")) { + // /storage + convertedPath = `file://${prootPath}`; + } else if (prootPath.startsWith("/data")) { + // /data + convertedPath = `file://${prootPath}`; + } else if (prootPath.startsWith("/")) { + // Everything else is relative to alpine root + convertedPath = `file://${alpineRoot}${prootPath}`; + } else { + convertedPath = prootPath; + } + + //console.log(`Path conversion: ${prootPath} -> ${convertedPath}`); + return convertedPath; + } + shouldConfirmTerminalClose() { const settings = appSettings?.value?.terminalSettings; if (settings && settings.confirmTabClose === false) { diff --git a/src/plugins/terminal/scripts/init-alpine.sh b/src/plugins/terminal/scripts/init-alpine.sh index 1f051f06c..b99302a3a 100644 --- a/src/plugins/terminal/scripts/init-alpine.sh +++ b/src/plugins/terminal/scripts/init-alpine.sh @@ -65,6 +65,70 @@ Working with packages: EOF fi + # Create acode CLI tool + if [ ! -e "$PREFIX/alpine/usr/local/bin/acode" ]; then + mkdir -p "$PREFIX/alpine/usr/local/bin" + cat <<'ACODE_CLI' > "$PREFIX/alpine/usr/local/bin/acode" +#!/bin/bash +# acode - Open files/folders in Acode editor +# Uses OSC escape sequences to communicate with the Acode terminal + +usage() { + echo "Usage: acode [file/folder...]" + echo "" + echo "Open files or folders in Acode editor." + echo "" + echo "Examples:" + echo " acode file.txt # Open a file" + echo " acode . # Open current folder" + echo " acode ~/project # Open a folder" + echo " acode -h, --help # Show this help" +} + +get_abs_path() { + local path="$1" + if [[ "$path" == /* ]]; then + realpath "$path" 2>/dev/null || echo "$path" + else + realpath "$path" 2>/dev/null || echo "$(pwd)/$path" + fi +} + +open_in_acode() { + local path=$(get_abs_path "$1") + local type="file" + [[ -d "$path" ]] && type="folder" + + # Send OSC 7777 escape sequence: \e]7777;cmd;type;path\a + # The terminal component will intercept and handle this + printf '\e]7777;open;%s;%s\a' "$type" "$path" +} + +if [[ $# -eq 0 ]]; then + open_in_acode "." + exit 0 +fi + +for arg in "$@"; do + case "$arg" in + -h|--help) + usage + exit 0 + ;; + *) + if [[ -e "$arg" ]]; then + open_in_acode "$arg" + else + echo "Error: '$arg' does not exist" >&2 + exit 1 + fi + ;; + esac +done +ACODE_CLI + chmod +x "$PREFIX/alpine/usr/local/bin/acode" + fi + # Create initrc if it doesn't exist #initrc runs in bash so we can use bash features if [ ! -e "$PREFIX/alpine/initrc" ]; then From 976ae0270271da38eeb64d725bb78de132b2f951 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Wed, 24 Dec 2025 10:05:07 +0530 Subject: [PATCH 2/4] Update src/plugins/terminal/scripts/init-alpine.sh Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/plugins/terminal/scripts/init-alpine.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/terminal/scripts/init-alpine.sh b/src/plugins/terminal/scripts/init-alpine.sh index b99302a3a..eee2e1c8b 100644 --- a/src/plugins/terminal/scripts/init-alpine.sh +++ b/src/plugins/terminal/scripts/init-alpine.sh @@ -87,11 +87,16 @@ usage() { get_abs_path() { local path="$1" - if [[ "$path" == /* ]]; then - realpath "$path" 2>/dev/null || echo "$path" - else - realpath "$path" 2>/dev/null || echo "$(pwd)/$path" + local abs_path + abs_path=$(realpath -- "$path" 2>/dev/null) + if [[ $? -ne 0 ]]; then + if [[ "$path" == /* ]]; then + abs_path="$path" + else + abs_path="$PWD/$path" + fi fi + echo "$abs_path" } open_in_acode() { From 029f5d4a929020404793c1f7dd23b5e29e702d6f Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Wed, 24 Dec 2025 10:07:45 +0530 Subject: [PATCH 3/4] feat: simplify terminal path handling --- src/components/terminal/terminalManager.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/terminal/terminalManager.js b/src/components/terminal/terminalManager.js index 2e0a18ca6..d2ca901d5 100644 --- a/src/components/terminal/terminalManager.js +++ b/src/components/terminal/terminalManager.js @@ -834,14 +834,11 @@ class TerminalManager { if (prootPath.startsWith("/public")) { // /public -> /data/user/0/com.foxdebug.acode/files/public convertedPath = `file://${dataDir}/files${prootPath}`; - } else if (prootPath.startsWith("/sdcard")) { - // /sdcard - convertedPath = `file://${prootPath}`; - } else if (prootPath.startsWith("/storage")) { - // /storage - convertedPath = `file://${prootPath}`; - } else if (prootPath.startsWith("/data")) { - // /data + } else if ( + prootPath.startsWith("/sdcard") || + prootPath.startsWith("/storage") || + prootPath.startsWith("/data") + ) { convertedPath = `file://${prootPath}`; } else if (prootPath.startsWith("/")) { // Everything else is relative to alpine root From 581a4a8a99f41cdaabaea2f15d1617a8d5936280 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Wed, 24 Dec 2025 10:12:54 +0530 Subject: [PATCH 4/4] feat: enhance terminal OSC handler for paths with semicolons --- src/components/terminal/terminal.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/components/terminal/terminal.js b/src/components/terminal/terminal.js index cb3539e92..39ede08c1 100644 --- a/src/components/terminal/terminal.js +++ b/src/components/terminal/terminal.js @@ -131,14 +131,30 @@ export default class TerminalComponent { */ setupOscHandler() { // Register custom OSC handler for ID 7777 + // Format: command;arg1;arg2;... where arg2 (path) may contain semicolons this.terminal.parser.registerOscHandler(7777, (data) => { - const parts = data.split(";"); - const command = parts[0]; + const firstSemi = data.indexOf(";"); + if (firstSemi === -1) { + console.warn("Invalid OSC 7777 format:", data); + return true; + } + + const command = data.substring(0, firstSemi); + const rest = data.substring(firstSemi + 1); switch (command) { - case "open": - this.handleOscOpen(parts[1], parts[2]); + case "open": { + // Format: open;type;path (path may contain semicolons) + const secondSemi = rest.indexOf(";"); + if (secondSemi === -1) { + console.warn("Invalid OSC 7777 open format:", data); + return true; + } + const type = rest.substring(0, secondSemi); + const path = rest.substring(secondSemi + 1); + this.handleOscOpen(type, path); break; + } default: console.warn("Unknown OSC 7777 command:", command); }