fix(ui): support remote working directory for external backend (#10827)
Co-authored-by: saixin <saixin.me@outlook.com>
This commit is contained in:
@@ -34,6 +34,19 @@ const i18n = defineMessages({
|
||||
defaultMessage:
|
||||
'Enter the HTTP(S) base URL. Goose checks /status and connects to /acp under this base.',
|
||||
},
|
||||
workingDir: {
|
||||
id: 'externalBackendSection.workingDir',
|
||||
defaultMessage: 'Remote Working Directory (optional)',
|
||||
},
|
||||
workingDirPlaceholder: {
|
||||
id: 'externalBackendSection.workingDirPlaceholder',
|
||||
defaultMessage: '/home/goose/workspace',
|
||||
},
|
||||
workingDirHelp: {
|
||||
id: 'externalBackendSection.workingDirHelp',
|
||||
defaultMessage:
|
||||
'Absolute path on the external backend. Leave blank to send the local working directory.',
|
||||
},
|
||||
secretKey: {
|
||||
id: 'externalBackendSection.secretKey',
|
||||
defaultMessage: 'Secret Key',
|
||||
@@ -222,6 +235,24 @@ export default function ExternalBackendSection() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="external-working-dir" className="text-text-primary text-xs">
|
||||
{intl.formatMessage(i18n.workingDir)}
|
||||
</label>
|
||||
<Input
|
||||
id="external-working-dir"
|
||||
type="text"
|
||||
placeholder={intl.formatMessage(i18n.workingDirPlaceholder)}
|
||||
value={config.workingDir || ''}
|
||||
onChange={(e) => updateField('workingDir', e.target.value)}
|
||||
onBlur={() => saveConfig(config)}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
<p className="text-xs text-text-secondary">
|
||||
{intl.formatMessage(i18n.workingDirHelp)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="external-secret" className="text-text-primary text-xs">
|
||||
{intl.formatMessage(i18n.secretKey)}
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Verbindung zu einem anderswo laufenden goose-Server herstellen (erfordert Neustart der App)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Remote-Arbeitsverzeichnis (optional)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Absoluter Pfad auf dem externen Backend. Leer lassen, um das lokale Arbeitsverzeichnis zu senden."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Schließen"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Connect to an ACP-compatible backend running elsewhere."
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Remote Working Directory (optional)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Absolute path on the external backend. Leave blank to send the local working directory."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Close"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Conéctate a un servidor goose que se ejecuta en otro lugar (requiere reiniciar la app)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Directorio de trabajo remoto (opcional)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Ruta absoluta en el backend externo. Déjelo vacío para enviar el directorio de trabajo local."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Cerrar"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Se connecter à un serveur goose exécuté ailleurs (nécessite le redémarrage de l'application)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Répertoire de travail distant (facultatif)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Chemin absolu sur le backend externe. Laissez vide pour envoyer le répertoire de travail local."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Fermer"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "कहीं और चल रहे goose सर्वर से कनेक्ट करें (ऐप पुनरारंभ की आवश्यकता है)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "रिमोट कार्य निर्देशिका (वैकल्पिक)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "बाहरी बैकएंड पर पूर्ण पथ। स्थानीय कार्य निर्देशिका भेजने के लिए खाली छोड़ दें।"
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "बंद करें"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Terhubung ke server goose yang berjalan di tempat lain (memerlukan mulai ulang aplikasi)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Direktori kerja jarak jauh (opsional)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Jalur absolut pada backend eksternal. Biarkan kosong untuk mengirim direktori kerja lokal."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Tutup"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Connettiti a un server goose in esecuzione altrove (richiede il riavvio dell'app)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Directory di lavoro remota (facoltativa)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Percorso assoluto sul backend esterno. Lascia vuoto per inviare la directory di lavoro locale."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Chiudi"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "別の場所で実行中の goose サーバーに接続します(アプリの再起動が必要)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "リモート作業ディレクトリ(任意)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "外部バックエンド上の絶対パスです。空欄の場合はローカルの作業ディレクトリを送信します。"
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "閉じる"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "다른 곳에서 실행 중인 ACP 호환 백엔드에 연결합니다."
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "원격 작업 디렉터리(선택 사항)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "외부 백엔드의 절대 경로입니다. 비워 두면 로컬 작업 디렉터리를 전송합니다."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "닫기"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Sambung ke pelayan goose yang berjalan di tempat lain (memerlukan memulakan semula aplikasi)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Direktori kerja jauh (pilihan)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Laluan mutlak pada backend luaran. Biarkan kosong untuk menghantar direktori kerja setempat."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Tutup"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Conecte-se a um servidor goose em execução em outro lugar (requer reinicialização do app)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Diretório de trabalho remoto (opcional)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Caminho absoluto no backend externo. Deixe em branco para enviar o diretório de trabalho local."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Fechar"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Подключиться к серверу goose, запущенному в другом месте (требуется перезапуск приложения)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Удалённый рабочий каталог (необязательно)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Абсолютный путь на внешнем бэкенде. Оставьте пустым, чтобы передать локальный рабочий каталог."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Закрыть"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Başka bir yerde çalışan bir goose sunucusuna bağlanın (uygulamanın yeniden başlatılmasını gerektirir)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Uzak çalışma dizini (isteğe bağlı)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Harici arka uçtaki mutlak yol. Yerel çalışma dizinini göndermek için boş bırakın."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Kapat"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "Kết nối tới một máy chủ goose đang chạy ở nơi khác (cần khởi động lại ứng dụng)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "Thư mục làm việc từ xa (tùy chọn)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "Đường dẫn tuyệt đối trên backend bên ngoài. Để trống để gửi thư mục làm việc cục bộ."
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "Đóng"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "连接到运行在其他位置的 goose 服务器(需要重启应用)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "远程工作目录(可选)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "外部后端上的绝对路径。留空时将发送本地工作目录。"
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "关闭"
|
||||
},
|
||||
|
||||
@@ -1226,6 +1226,15 @@
|
||||
"externalBackendSection.useExternalServerDescription": {
|
||||
"defaultMessage": "連線至在其他位置執行的 goose 伺服器(需要重新啟動應用程式)"
|
||||
},
|
||||
"externalBackendSection.workingDir": {
|
||||
"defaultMessage": "遠端工作目錄(選用)"
|
||||
},
|
||||
"externalBackendSection.workingDirHelp": {
|
||||
"defaultMessage": "外部後端上的絕對路徑。留空時將傳送本機工作目錄。"
|
||||
},
|
||||
"externalBackendSection.workingDirPlaceholder": {
|
||||
"defaultMessage": "/home/goose/workspace"
|
||||
},
|
||||
"goosehintsModal.close": {
|
||||
"defaultMessage": "關閉"
|
||||
},
|
||||
|
||||
+19
-3
@@ -56,6 +56,7 @@ import type { GooseApp } from './types/apps';
|
||||
import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';
|
||||
import { BLOCKED_PROTOCOLS, WEB_PROTOCOLS } from './utils/urlSecurity';
|
||||
import { buildCSP } from './utils/csp';
|
||||
import { resolveWorkingDir } from './utils/workingDir';
|
||||
|
||||
function shouldSetupUpdater(): boolean {
|
||||
// Setup updater if either the flag is enabled OR dev updates are enabled
|
||||
@@ -890,6 +891,7 @@ interface ExternalBackend {
|
||||
url: string;
|
||||
secret: string;
|
||||
certFingerprint?: string;
|
||||
workingDir?: string;
|
||||
}
|
||||
|
||||
const getExternalBackendUrlFromEnv = (): string | null => {
|
||||
@@ -936,7 +938,10 @@ const getServerSecret = (settings: Settings): string => {
|
||||
const getActiveExternalBackend = (settings: Settings): ExternalBackend | null => {
|
||||
const envBackend = getExternalBackendFromEnv();
|
||||
if (envBackend) {
|
||||
return envBackend;
|
||||
return {
|
||||
...envBackend,
|
||||
workingDir: settings.externalGoosed?.workingDir,
|
||||
};
|
||||
}
|
||||
|
||||
if (settings.externalGoosed?.enabled && settings.externalGoosed.url) {
|
||||
@@ -945,6 +950,7 @@ const getActiveExternalBackend = (settings: Settings): ExternalBackend | null =>
|
||||
url: settings.externalGoosed.url,
|
||||
secret: getServerSecret(settings),
|
||||
certFingerprint: settings.externalGoosed.certFingerprint,
|
||||
workingDir: settings.externalGoosed.workingDir,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1068,7 +1074,7 @@ const createChat = async (
|
||||
}
|
||||
|
||||
const serverSecret = externalBackend ? externalBackend.secret : GENERATED_SECRET;
|
||||
let workingDir = dir || os.homedir();
|
||||
let workingDir = resolveWorkingDir(externalBackend?.workingDir, dir, os.homedir());
|
||||
let gooseServeLease: GooseServeLease | null = null;
|
||||
|
||||
if (externalBackend) {
|
||||
@@ -3003,7 +3009,17 @@ async function appMain() {
|
||||
throw new Error('No backend lease found for launching window');
|
||||
}
|
||||
|
||||
const workingDir = app.getPath('home');
|
||||
const launchingWorkingDir = await launchingWindow.webContents
|
||||
.executeJavaScript(`window.appConfig ? window.appConfig.get('GOOSE_WORKING_DIR') : null`)
|
||||
.catch((error) => {
|
||||
console.warn('Failed to get working directory from launching window:', error);
|
||||
return undefined;
|
||||
});
|
||||
const workingDir = resolveWorkingDir(
|
||||
typeof launchingWorkingDir === 'string' ? launchingWorkingDir : undefined,
|
||||
undefined,
|
||||
app.getPath('home')
|
||||
);
|
||||
const appWindow = new BrowserWindow({
|
||||
title: formatAppName(gooseApp.name),
|
||||
width: gooseApp.width ?? 800,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { resolveWorkingDir } from '../workingDir';
|
||||
|
||||
describe('resolveWorkingDir', () => {
|
||||
it('uses the configured external backend directory when present', () => {
|
||||
expect(resolveWorkingDir(' /home/goose ', 'C:\\Users\\goose', 'C:\\Users\\goose')).toBe(
|
||||
'/home/goose'
|
||||
);
|
||||
expect(resolveWorkingDir(' ', 'C:\\work', 'C:\\Users\\goose')).toBe('C:\\work');
|
||||
expect(resolveWorkingDir(undefined, undefined, 'C:\\Users\\goose')).toBe('C:\\Users\\goose');
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,7 @@ export interface ExternalBackendConfig {
|
||||
url: string;
|
||||
secret: string;
|
||||
certFingerprint?: string;
|
||||
workingDir?: string;
|
||||
}
|
||||
|
||||
export interface KeyboardShortcuts {
|
||||
|
||||
@@ -2,3 +2,9 @@ export const getInitialWorkingDir = (): string => {
|
||||
// Fall back to initial config from app startup
|
||||
return (window.appConfig?.get('GOOSE_WORKING_DIR') as string) ?? '';
|
||||
};
|
||||
|
||||
export const resolveWorkingDir = (
|
||||
externalWorkingDir: string | undefined,
|
||||
requestedWorkingDir: string | undefined,
|
||||
homeDir: string
|
||||
): string => externalWorkingDir?.trim() || requestedWorkingDir || homeDir;
|
||||
|
||||
Reference in New Issue
Block a user