-
-
Notifications
You must be signed in to change notification settings - Fork 88
Add TRAMP support for remote agent execution #205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ad23fea
377f2e3
7c4a4cb
641c1dd
fca216e
6a8ec83
d77b753
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| .agent-shell/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| ;;; agent-shell-tramp.el --- TRAMP support for agent-shell -*- lexical-binding: t; -*- | ||
|
|
||
| ;; Copyright (C) 2024 Alvaro Ramirez | ||
|
|
||
| ;; Author: Alvaro Ramirez https://xenodium.com | ||
| ;; URL: https://github.com/xenodium/agent-shell | ||
|
|
||
| ;; This package is free software; you can redistribute it and/or modify | ||
| ;; it under the terms of the GNU General Public License as published by | ||
| ;; the Free Software Foundation; either version 3, or (at your option) | ||
| ;; any later version. | ||
|
|
||
| ;; This package is distributed in the hope that it will be useful, | ||
| ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| ;; GNU General Public License for more details. | ||
|
|
||
| ;; You should have received a copy of the GNU General Public License | ||
| ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| ;;; Commentary: | ||
| ;; | ||
| ;; This file provides TRAMP support for agent-shell, allowing agents | ||
| ;; to run on remote hosts accessed via TRAMP. | ||
| ;; | ||
| ;; TRAMP support works automatically when `default-directory' is a | ||
| ;; TRAMP path (e.g., /ssh:host:/path). The agent runs on the remote | ||
| ;; host via Emacs' file-handler mechanism. | ||
| ;; | ||
|
|
||
| ;;; Code: | ||
|
|
||
| (declare-function agent-shell-cwd "agent-shell") | ||
|
|
||
| (declare-function tramp-tramp-file-p "tramp") | ||
| (declare-function tramp-dissect-file-name "tramp") | ||
| (declare-function tramp-file-name-host "tramp") | ||
| (declare-function tramp-file-name-localname "tramp") | ||
| (declare-function tramp-make-tramp-file-name "tramp") | ||
|
|
||
| (defun agent-shell--resolve-tramp-path (path) | ||
| "Resolve PATH between TRAMP format and remote-local format. | ||
|
|
||
| For example: | ||
| - /ssh:host:/project/README.md => /project/README.md | ||
| - /project/README.md => /ssh:host:/project/README.md" | ||
| (require 'tramp) | ||
| (let* ((cwd (agent-shell-cwd)) | ||
| (tramp-vec (and (tramp-tramp-file-p cwd) | ||
| (tramp-dissect-file-name cwd)))) | ||
| (cond | ||
| ;; Path is already a TRAMP path - strip the prefix for the agent | ||
| ((tramp-tramp-file-p path) | ||
| (tramp-file-name-localname (tramp-dissect-file-name path))) | ||
| ;; Path is a remote-local path - add TRAMP prefix for Emacs | ||
| (tramp-vec | ||
| (tramp-make-tramp-file-name tramp-vec path)) | ||
| ;; Not in a TRAMP context | ||
| (t path)))) | ||
|
|
||
| (defun agent-shell--tramp-transcript-dir (cwd) | ||
| "Return local transcript directory for TRAMP CWD. | ||
| Returns nil if CWD is not a TRAMP path." | ||
| (when (and (fboundp 'tramp-tramp-file-p) | ||
| (tramp-tramp-file-p cwd)) | ||
| (require 'tramp) | ||
| (let* ((vec (tramp-dissect-file-name cwd)) | ||
| (host (tramp-file-name-host vec)) | ||
| (localname (tramp-file-name-localname vec)) | ||
| (safe-path (replace-regexp-in-string "/" "_" (string-trim localname "/" "/")))) | ||
| (expand-file-name (format ".agent-shell/transcripts/%s/%s" host safe-path) | ||
| (expand-file-name "~"))))) | ||
|
|
||
| (provide 'agent-shell-tramp) | ||
| ;;; agent-shell-tramp.el ends here |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,11 +115,14 @@ When non-nil, user message sections are expanded." | |
| :type 'boolean | ||
| :group 'agent-shell) | ||
|
|
||
| (defcustom agent-shell-path-resolver-function nil | ||
| (autoload 'agent-shell--resolve-tramp-path "agent-shell-tramp") | ||
|
|
||
| (defcustom agent-shell-path-resolver-function #'agent-shell--resolve-tramp-path | ||
| "Function for resolving remote paths on the local file-system, and vice versa. | ||
|
|
||
| Expects a function that takes the path as its single argument, and | ||
| returns the resolved path. Set to nil to disable mapping." | ||
| returns the resolved path. The default handles TRAMP paths automatically. | ||
| Set to nil or #\\='identity to disable path resolution." | ||
| :type 'function | ||
| :group 'agent-shell) | ||
|
|
||
|
|
@@ -128,7 +131,10 @@ returns the resolved path. Set to nil to disable mapping." | |
|
|
||
| When non-nil, both the agent command and shell commands will be | ||
| executed using this runner. Can be a list of strings or a function | ||
| that takes a buffer and returns a list. | ||
| that takes a buffer and returns a list (or nil for local execution). | ||
|
|
||
| Note: TRAMP remote execution is handled automatically via Emacs' | ||
| file-handler mechanism and does not require this setting. | ||
|
|
||
| Example for static devcontainer: | ||
| \\='(\"devcontainer\" \"exec\" \"--workspace-folder\" \".\") | ||
|
|
@@ -146,7 +152,7 @@ Example for per-session containers: | |
| (if (string-match \"project-a\" (buffer-name buffer)) | ||
| \\='(\"docker\" \"exec\" \"project-a-dev\" \"--\") | ||
| \\='(\"docker\" \"exec\" \"project-b-dev\" \"--\")))" | ||
| :type '(choice (repeat string) function) | ||
| :type '(choice (const nil) (repeat string) function) | ||
| :group 'agent-shell) | ||
|
|
||
| (defcustom agent-shell-section-functions nil | ||
|
|
@@ -1175,6 +1181,18 @@ If the buffer's file has changed, prompt the user to reload it." | |
| "Resolve PATH using `agent-shell-path-resolver-function'." | ||
| (funcall (or agent-shell-path-resolver-function #'identity) path)) | ||
|
|
||
| ;; TRAMP support is in agent-shell-tramp.el | ||
| (declare-function agent-shell--tramp-transcript-dir "agent-shell-tramp") | ||
|
|
||
| (defun agent-shell--local-temp-directory () | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: you might want to consider naming this |
||
| "Return a local temporary directory, even when `default-directory' is remote. | ||
| This ensures temp files (like cached icons) are always stored locally." | ||
| (if (and (fboundp 'tramp-tramp-file-p) | ||
| (tramp-tramp-file-p default-directory)) | ||
| ;; When in a TRAMP buffer, use Emacs-standard cache directory | ||
| (locate-user-emacs-file "agent-shell/cache/") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be just Further down in lines 2213 and 2240, you're |
||
| (temporary-file-directory))) | ||
|
|
||
| (defun agent-shell--get-devcontainer-workspace-path (cwd) | ||
| "Return devcontainer workspaceFolder for CWD, or default value if none found. | ||
|
|
||
|
|
@@ -2190,7 +2208,9 @@ Icon names starting with https:// are downloaded directly from that location." | |
| url)) | ||
| ;; For lobe-icons names, use the original filename | ||
| (file-name-nondirectory url))) | ||
| (cache-dir (file-name-concat (temporary-file-directory) "agent-shell" mode)) | ||
| ;; Always use local temp directory, even when default-directory is remote | ||
| (local-temp-dir (agent-shell--local-temp-directory)) | ||
| (cache-dir (file-name-concat local-temp-dir "agent-shell" mode)) | ||
| (cache-path (expand-file-name filename cache-dir))) | ||
| (unless (file-exists-p cache-path) | ||
| (make-directory cache-dir t) | ||
|
|
@@ -2215,7 +2235,9 @@ Return file path of the generated SVG." | |
| (let* ((icon-text (char-to-string (string-to-char icon-name))) | ||
| (mode (if (eq (frame-parameter nil 'background-mode) 'dark) "dark" "light")) | ||
| (filename (format "%s-%s.svg" icon-name width)) | ||
| (cache-dir (file-name-concat (temporary-file-directory) "agent-shell" mode)) | ||
| ;; Always use local temp directory, even when default-directory is remote | ||
| (local-temp-dir (agent-shell--local-temp-directory)) | ||
| (cache-dir (file-name-concat local-temp-dir "agent-shell" mode)) | ||
| (cache-path (expand-file-name filename cache-dir)) | ||
| (font-size (* 0.7 width)) | ||
| (x (/ width 2)) | ||
|
|
@@ -4158,8 +4180,13 @@ When nil, transcript saving is disabled." | |
|
|
||
| For example: | ||
|
|
||
| project/.agent-shell/transcripts/." | ||
| (let* ((dir (expand-file-name ".agent-shell/transcripts" (agent-shell-cwd))) | ||
| project/.agent-shell/transcripts/. | ||
|
|
||
| For TRAMP paths, saves locally in ~/.agent-shell/transcripts/<host>/<remote-path>/." | ||
| (let* ((cwd (agent-shell-cwd)) | ||
| (dir (or (agent-shell--tramp-transcript-dir cwd) | ||
| ;; Local paths use project root as before | ||
| (expand-file-name ".agent-shell/transcripts" cwd))) | ||
| (filename (format-time-string "%F-%H-%M-%S.md")) | ||
| (filepath (expand-file-name filename dir))) | ||
| filepath)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a thought: since
agent-shell--resolve-tramp-pathdoes nothing for non-tramp paths (so is save to call in all cases), you might want to hardcode calling it intoagent-shell--resolve-path, probably even beforefuncall-ingagent-shell-path-resolver-function. That way, TRAMP support would be entirely transparent.