mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-05-19 13:58:37 -04:00
Merge remote-tracking branch 'origin/21.x-release'
This commit is contained in:
@@ -36,12 +36,6 @@ public class AppBeaconCache {
|
||||
control.setNonInteractive();
|
||||
control.start();
|
||||
|
||||
var d = control.getShellDialect().getDumbMode();
|
||||
if (!d.supportsAnyPossibleInteraction()) {
|
||||
control.close();
|
||||
d.throwIfUnsupported();
|
||||
}
|
||||
|
||||
if (existing.isEmpty()) {
|
||||
AppBeaconServer.get().getCache().getShellSessions().add(new BeaconShellSession(ref.get(), control));
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ public class AppMcpServer {
|
||||
.resources(true, true)
|
||||
.tools(true)
|
||||
.prompts(false)
|
||||
.completions()
|
||||
.build())
|
||||
.instructions(AppPrefs.get().mcpAdditionalContext().getValue())
|
||||
.build();
|
||||
|
||||
var readOnlyTools = new ArrayList<McpServerFeatures.SyncToolSpecification>();
|
||||
|
||||
@@ -37,7 +37,7 @@ public interface McpToolHandler
|
||||
.isError(true)
|
||||
.build();
|
||||
} catch (Throwable e) {
|
||||
ErrorEventFactory.fromThrowable(e).handle();
|
||||
ErrorEventFactory.fromThrowable(e).omit().handle();
|
||||
return McpSchema.CallToolResult.builder()
|
||||
.addTextContent(e.getMessage())
|
||||
.isError(true)
|
||||
|
||||
@@ -3,10 +3,7 @@ package io.xpipe.app.beacon.mcp;
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.core.AppExtensionManager;
|
||||
import io.xpipe.app.core.AppNames;
|
||||
import io.xpipe.app.ext.ConnectionFileSystem;
|
||||
import io.xpipe.app.ext.FileEntry;
|
||||
import io.xpipe.app.ext.FileInfo;
|
||||
import io.xpipe.app.ext.SingletonSessionStore;
|
||||
import io.xpipe.app.ext.*;
|
||||
import io.xpipe.app.process.ScriptHelper;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.app.process.TerminalInitScriptConfig;
|
||||
@@ -350,7 +347,7 @@ public final class McpTools {
|
||||
var shellStore = req.getShellStoreRef(system);
|
||||
var shellSession = AppBeaconServer.get().getCache().getOrStart(shellStore);
|
||||
|
||||
var out = shellSession.getControl().command(command).readStdoutOrThrow();
|
||||
var out = ProcessControlProvider.get().executeMcpCommand(shellSession.getControl(), command);
|
||||
var formatted = CommandDialog.formatOutput(out);
|
||||
|
||||
return McpSchema.CallToolResult.builder()
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.xpipe.app.terminal.TerminalDockBrowserComp;
|
||||
import io.xpipe.app.terminal.TerminalDockView;
|
||||
import io.xpipe.app.terminal.TerminalView;
|
||||
import io.xpipe.app.terminal.WindowsTerminalType;
|
||||
import io.xpipe.app.util.GlobalTimer;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.application.Platform;
|
||||
@@ -25,6 +26,7 @@ import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
@@ -36,6 +38,7 @@ public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
|
||||
private final BooleanProperty opened = new SimpleBooleanProperty();
|
||||
private TerminalView.Listener listener;
|
||||
private ObservableBooleanValue viewActive;
|
||||
private boolean closed;
|
||||
|
||||
public BrowserTerminalDockTabModel(
|
||||
BrowserAbstractSessionModel<?> browserModel,
|
||||
@@ -147,6 +150,14 @@ public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
GlobalTimer.scheduleUntil(Duration.ofMillis(300), false, () -> {
|
||||
if (viewActive.get()) {
|
||||
dockModel.clearDeadTerminals();
|
||||
dockModel.updateCustomBounds();
|
||||
}
|
||||
return closed;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,6 +166,7 @@ public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
|
||||
TerminalView.get().removeListener(listener);
|
||||
}
|
||||
dockModel.onClose();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package io.xpipe.app.browser.menu.impl;
|
||||
|
||||
import io.xpipe.app.browser.file.BrowserEntry;
|
||||
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
|
||||
import io.xpipe.app.browser.menu.BrowserMenuCategory;
|
||||
import io.xpipe.app.browser.menu.BrowserMenuLeafProvider;
|
||||
import io.xpipe.app.comp.RegionBuilder;
|
||||
import io.xpipe.app.comp.base.ModalOverlay;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.FileKind;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.core.OsType;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.TextField;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GradleRunMenuProvider implements BrowserMenuLeafProvider {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
|
||||
if (model.getFileSystem().getShell().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entries.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entries.getFirst().getRawFileEntry().getKind() != FileKind.FILE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OsType.Any osType = model.getFileSystem().getShell().orElseThrow().getOsType();
|
||||
var ext = switch (osType) {
|
||||
case OsType.Windows ignored -> "gradlew.bat";
|
||||
default -> "gradlew";
|
||||
};
|
||||
|
||||
if (!entries.getFirst().getFileName().equalsIgnoreCase(ext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValue<String> getName(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
|
||||
return AppI18n.observable("runTask");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserMenuCategory getCategory() {
|
||||
return BrowserMenuCategory.CUSTOM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelGraphic getIcon() {
|
||||
return new LabelGraphic.IconGraphic("mdi2e-elephant");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
|
||||
var tasks = new SimpleStringProperty();
|
||||
var modal = ModalOverlay.of(
|
||||
"gradleTasks",
|
||||
RegionBuilder.of(() -> {
|
||||
var creationName = new TextField();
|
||||
creationName.textProperty().bindBidirectional(tasks);
|
||||
return creationName;
|
||||
})
|
||||
.prefWidth(350));
|
||||
modal.withDefaultButtons(() -> {
|
||||
var fixedTasks = tasks.getValue();
|
||||
if (fixedTasks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parent = entries.getFirst().getRawFileEntry().getPath().getParent();
|
||||
var command = model.getFileSystem().getShell().orElseThrow().command(CommandBuilder.of()
|
||||
.add("sh")
|
||||
.addFile(entries.getFirst().getRawFileEntry().getPath())
|
||||
.add(fixedTasks)
|
||||
);
|
||||
|
||||
model.openTerminalAsync(fixedTasks, parent, command, true);
|
||||
});
|
||||
modal.show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -89,13 +89,19 @@ public class IntegratedTextAreaComp extends RegionStructureBuilder<AnchorPane, I
|
||||
.bind(Bindings.createIntegerBinding(
|
||||
() -> {
|
||||
var val = value.getValue() != null ? value.getValue() : "";
|
||||
var count = (int) val.lines().count() + (val.endsWith("\n") ? 1 : 0);
|
||||
var valCount = (int) val.lines().count() + (val.endsWith("\n") ? 1 : 0);
|
||||
|
||||
var promptVal = struc.getTextArea().getPromptText() != null ? struc.getTextArea().getPromptText() : "";
|
||||
var promptValCount = (int) promptVal.lines().count() + (promptVal.endsWith("\n") ? 1 : 0);
|
||||
|
||||
var count = Math.max(valCount, promptValCount);
|
||||
// Somehow the handling of trailing newlines is weird
|
||||
// This makes the handling better for JavaFX text areas
|
||||
count++;
|
||||
return Math.max(1, count);
|
||||
},
|
||||
value));
|
||||
value,
|
||||
struc.getTextArea().promptTextProperty()));
|
||||
});
|
||||
var textAreaStruc = textArea.buildStructure();
|
||||
var copyButton = createOpenButton();
|
||||
|
||||
@@ -49,10 +49,11 @@ public class SideMenuBarComp extends RegionBuilder<VBox> {
|
||||
|
||||
if (e.action() != null) {
|
||||
e.action().run();
|
||||
return;
|
||||
}
|
||||
|
||||
value.setValue(e);
|
||||
if (e.comp() != null) {
|
||||
value.setValue(e);
|
||||
}
|
||||
});
|
||||
b.describe(d -> d.name(e.name()));
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.xpipe.app.hub.comp.StoreLayoutComp;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.platform.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefsComp;
|
||||
import io.xpipe.app.terminal.TerminalDockHubManager;
|
||||
import io.xpipe.app.update.AppDistributionType;
|
||||
import io.xpipe.app.util.*;
|
||||
|
||||
@@ -117,7 +118,9 @@ public class AppLayoutModel {
|
||||
AppI18n.observable("connections"),
|
||||
new LabelGraphic.IconGraphic("mdi2c-connection"),
|
||||
new StoreLayoutComp(),
|
||||
null,
|
||||
() -> {
|
||||
TerminalDockHubManager.get().hideDock();
|
||||
},
|
||||
new KeyCodeCombination(KeyCode.DIGIT1, KeyCombination.SHORTCUT_DOWN)),
|
||||
new Entry(
|
||||
AppI18n.observable("browser"),
|
||||
|
||||
@@ -58,6 +58,8 @@ public abstract class ProcessControlProvider {
|
||||
|
||||
public abstract ShellDialect getEffectiveLocalDialect();
|
||||
|
||||
public abstract String executeMcpCommand(ShellControl sc, String command) throws Exception;
|
||||
|
||||
public ShellDialect getNextFallbackDialect() {
|
||||
var av = getAvailableLocalDialects();
|
||||
var index = av.indexOf(getEffectiveLocalDialect());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.xpipe.app.hub.comp;
|
||||
|
||||
import io.xpipe.app.comp.SimpleRegionBuilder;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
|
||||
import javafx.application.Platform;
|
||||
@@ -23,6 +24,7 @@ public class StoreEntryBatchSelectComp extends SimpleRegionBuilder {
|
||||
protected Region createSimple() {
|
||||
var selfUpdate = new SimpleBooleanProperty(false);
|
||||
var cb = new CheckBox();
|
||||
externalUpdate(cb);
|
||||
cb.setAllowIndeterminate(true);
|
||||
cb.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
BooleanScope.executeExclusive(selfUpdate, () -> {
|
||||
@@ -64,6 +66,13 @@ public class StoreEntryBatchSelectComp extends SimpleRegionBuilder {
|
||||
}
|
||||
|
||||
private void externalUpdate(CheckBox checkBox) {
|
||||
if (section.getWrapper() != null && section.getWrapper().getEntry().getValidity() == DataStoreEntry.Validity.LOAD_FAILED) {
|
||||
checkBox.setSelected(false);
|
||||
checkBox.setIndeterminate(false);
|
||||
checkBox.setDisable(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var isSelected = section.getWrapper() == null
|
||||
? checkBox.isSelected()
|
||||
: StoreViewState.get().isBatchModeSelected(section.getWrapper());
|
||||
|
||||
@@ -189,6 +189,9 @@ public class StoreViewState {
|
||||
|
||||
public void selectBatchMode(StoreSection section) {
|
||||
var wrapper = section.getWrapper();
|
||||
if (wrapper != null && wrapper.getEntry().getValidity() == DataStoreEntry.Validity.LOAD_FAILED) {
|
||||
return;
|
||||
}
|
||||
if (wrapper != null && !batchModeSelectionSet.contains(wrapper)) {
|
||||
batchModeSelection.getList().add(wrapper);
|
||||
}
|
||||
@@ -199,6 +202,9 @@ public class StoreViewState {
|
||||
|
||||
public void unselectBatchMode(StoreSection section) {
|
||||
var wrapper = section.getWrapper();
|
||||
if (wrapper != null && wrapper.getEntry().getValidity() == DataStoreEntry.Validity.LOAD_FAILED) {
|
||||
return;
|
||||
}
|
||||
if (wrapper != null) {
|
||||
batchModeSelection.getList().remove(wrapper);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,11 @@ public class ErrorEventFactory {
|
||||
b.expected();
|
||||
}
|
||||
|
||||
if (OsType.ofLocal() == OsType.WINDOWS && t.getMessage().contains("The cloud file provider is not running")) {
|
||||
b.description("The OneDrive cloud file provider is not running. Verify that your cloud storage is working and you are logged in.");
|
||||
b.expected();
|
||||
}
|
||||
|
||||
if (t instanceof AccessDeniedException) {
|
||||
b.expected();
|
||||
}
|
||||
|
||||
@@ -73,9 +73,29 @@ public class NativeWinWindowControl {
|
||||
}
|
||||
|
||||
public void removeBorders() {
|
||||
var rect = getBounds();
|
||||
|
||||
var style = User32.INSTANCE.GetWindowLong(windowHandle, User32.GWL_STYLE);
|
||||
var mod = style & ~(User32.WS_CAPTION | User32.WS_THICKFRAME | User32.WS_MAXIMIZEBOX);
|
||||
User32.INSTANCE.SetWindowLong(windowHandle, User32.GWL_STYLE, mod);
|
||||
|
||||
User32.INSTANCE.SetWindowPos(windowHandle, null, rect.getX(), rect.getY(), rect.getW() + 1, rect.getH(),
|
||||
User32.SWP_NOACTIVATE | User32.SWP_NOMOVE | User32.SWP_NOZORDER);
|
||||
User32.INSTANCE.SetWindowPos(windowHandle, null, rect.getX(), rect.getY(), rect.getW(), rect.getH(),
|
||||
User32.SWP_NOACTIVATE | User32.SWP_NOMOVE | User32.SWP_NOZORDER);
|
||||
}
|
||||
|
||||
public void restoreBorders() {
|
||||
var rect = getBounds();
|
||||
|
||||
var style = User32.INSTANCE.GetWindowLong(windowHandle, User32.GWL_STYLE);
|
||||
var mod = style | User32.WS_CAPTION | User32.WS_THICKFRAME | User32.WS_MAXIMIZEBOX;
|
||||
User32.INSTANCE.SetWindowLong(windowHandle, User32.GWL_STYLE, mod);
|
||||
|
||||
User32.INSTANCE.SetWindowPos(windowHandle, null, rect.getX(), rect.getY(), rect.getW() + 1, rect.getH(),
|
||||
User32.SWP_NOACTIVATE | User32.SWP_NOMOVE | User32.SWP_NOZORDER);
|
||||
User32.INSTANCE.SetWindowPos(windowHandle, null, rect.getX(), rect.getY(), rect.getW(), rect.getH(),
|
||||
User32.SWP_NOACTIVATE | User32.SWP_NOMOVE | User32.SWP_NOZORDER);
|
||||
}
|
||||
|
||||
public void takeOwnership(WinDef.HWND owner) {
|
||||
|
||||
@@ -113,12 +113,13 @@ public enum PlatformState {
|
||||
}
|
||||
}
|
||||
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
// This is primarily intended to fix Windows unified stage transparency issues
|
||||
// (https://bugs.openjdk.org/browse/JDK-8329382)
|
||||
// But apparently it can also occur without a custom stage on Windows
|
||||
System.setProperty("prism.forceUploadingPainter", "true");
|
||||
}
|
||||
// This issue is now fixed in 27-ea+4
|
||||
// if (SystemUtils.IS_OS_WINDOWS) {
|
||||
// This is primarily intended to fix Windows unified stage transparency issues
|
||||
// (https://bugs.openjdk.org/browse/JDK-8329382)
|
||||
// But apparently it can also occur without a custom stage on Windows
|
||||
// System.setProperty("prism.forceUploadingPainter", "true");
|
||||
// }
|
||||
|
||||
if (AppPrefs.get() != null
|
||||
&& AppPrefs.get().disableHardwareAcceleration().get()) {
|
||||
|
||||
@@ -125,6 +125,12 @@ public final class AppPrefs {
|
||||
.key("enableMcpMutationTools")
|
||||
.valueClass(Boolean.class)
|
||||
.build());
|
||||
final StringProperty mcpAdditionalContext = map(Mapping.builder()
|
||||
.property(new GlobalStringProperty(null))
|
||||
.key("mcpAdditionalContext")
|
||||
.valueClass(String.class)
|
||||
.requiresRestart(true)
|
||||
.build());
|
||||
final BooleanProperty dontAutomaticallyStartVmSshServer =
|
||||
mapVaultShared(new GlobalBooleanProperty(false), "dontAutomaticallyStartVmSshServer", Boolean.class, false);
|
||||
final BooleanProperty dontAcceptNewHostKeys =
|
||||
@@ -582,6 +588,10 @@ public final class AppPrefs {
|
||||
return enableMcpMutationTools;
|
||||
}
|
||||
|
||||
public ObservableValue<String> mcpAdditionalContext() {
|
||||
return mcpAdditionalContext;
|
||||
}
|
||||
|
||||
public ObservableBooleanValue pinLocalMachineOnStartup() {
|
||||
return pinLocalMachineOnStartup;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface ExternalEditorType extends PrefsChoiceValue {
|
||||
|
||||
ExternalEditorType NOTEPAD = new WindowsType() {
|
||||
|
||||
@Override
|
||||
@@ -280,6 +279,70 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||
|
||||
LinuxPathType KIRO_LINUX = new LinuxPathType("app.kiro", "kiro", "https://kiro.dev/");
|
||||
|
||||
LinuxType NEOVIM_LINUX = new LinuxType("app.neovim", "nvim", "https://neovim.io/", null) {
|
||||
@Override
|
||||
public void launch(Path file) throws Exception {
|
||||
TerminalLaunch.builder()
|
||||
.title(file.toString())
|
||||
.localScript(sc -> new ShellScript(CommandBuilder.of()
|
||||
.addFile(getExecutable())
|
||||
.addFile(file.toString())
|
||||
.buildFull(sc)))
|
||||
.logIfEnabled(false)
|
||||
.preferTabs(false)
|
||||
.pauseOnExit(false)
|
||||
.launch();
|
||||
}
|
||||
};
|
||||
|
||||
WindowsType NEOVIM_WINDOWS = new WindowsType() {
|
||||
@Override
|
||||
public String getId() {
|
||||
return "app.neovim";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean detach() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecutable() {
|
||||
return "nvim";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getWebsite() {
|
||||
return "https://neovim.io/";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<Path> determineInstallation() {
|
||||
var programFiles = AppSystemInfo.ofWindows().getProgramFiles().resolve("Neovim", "bin").resolve("nvim.exe");
|
||||
if (Files.exists(programFiles)) {
|
||||
return Optional.of(programFiles);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(Path file) throws Exception {
|
||||
TerminalLaunch.builder()
|
||||
.title(file.toString())
|
||||
.localScript(sc -> new ShellScript(CommandBuilder.of()
|
||||
.addFile(findExecutable().toString())
|
||||
.addFile(file)
|
||||
.buildFull(sc)))
|
||||
.logIfEnabled(false)
|
||||
.preferTabs(false)
|
||||
.pauseOnExit(false)
|
||||
.launch();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
WindowsType ZED_WINDOWS = new WindowsType() {
|
||||
|
||||
@Override
|
||||
@@ -356,6 +419,21 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||
ExternalEditorType WINDSURF_MACOS = new MacOsEditor("app.windsurf", "Windsurf", "https://windsurf.com/editor");
|
||||
ExternalEditorType KIRO_MACOS = new MacOsEditor("app.kiro", "Kiro", "https://kiro.dev/");
|
||||
ExternalEditorType TRAE_MACOS = new MacOsEditor("app.trae", "Trae", "https://www.trae.ai/");
|
||||
ExternalEditorType NEOVIM_MACOS = new MacOsEditor("app.neovim", "Neovim", "https://neovim.io/") {
|
||||
@Override
|
||||
public void launch(Path file) throws Exception {
|
||||
TerminalLaunch.builder()
|
||||
.title(file.toString())
|
||||
.localScript(sc -> new ShellScript(CommandBuilder.of()
|
||||
.addFile("nvim")
|
||||
.addFile(file.toString())
|
||||
.buildFull(sc)))
|
||||
.logIfEnabled(false)
|
||||
.preferTabs(false)
|
||||
.pauseOnExit(false)
|
||||
.launch();
|
||||
}
|
||||
};
|
||||
ExternalEditorType CUSTOM = new ExternalEditorType() {
|
||||
|
||||
@Override
|
||||
@@ -380,6 +458,7 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||
.localScript(sc -> new ShellScript(command.buildFull(sc)))
|
||||
.logIfEnabled(false)
|
||||
.preferTabs(false)
|
||||
.pauseOnExit(false)
|
||||
.launch();
|
||||
} else {
|
||||
ExternalApplicationHelper.startAsync(command);
|
||||
@@ -422,7 +501,8 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||
VSCODE_INSIDERS_WINDOWS,
|
||||
VSCODE_WINDOWS,
|
||||
NOTEPADPLUSPLUS,
|
||||
NOTEPAD);
|
||||
NOTEPAD,
|
||||
NEOVIM_WINDOWS);
|
||||
List<GenericPathType> LINUX_EDITORS = List.of(
|
||||
ExternalEditorType.WINDSURF_LINUX,
|
||||
ExternalEditorType.KIRO_LINUX,
|
||||
@@ -435,6 +515,7 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||
PLUMA,
|
||||
LEAFPAD,
|
||||
MOUSEPAD,
|
||||
NEOVIM_LINUX,
|
||||
GNOME,
|
||||
ExternalEditorType.COSMIC_EDIT,
|
||||
ExternalEditorType.WESTON_EDITOR,
|
||||
@@ -451,6 +532,7 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||
VSCODE_MACOS,
|
||||
SUBLIME_MACOS,
|
||||
ZED_MACOS,
|
||||
NEOVIM_MACOS,
|
||||
TEXT_EDIT);
|
||||
List<ExternalEditorType> CROSS_PLATFORM_EDITORS = List.of(FLEET, INTELLIJ, PYCHARM, WEBSTORM, CLION);
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
package io.xpipe.app.prefs;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.comp.BaseRegionBuilder;
|
||||
import io.xpipe.app.comp.RegionBuilder;
|
||||
import io.xpipe.app.comp.base.IntegratedTextAreaComp;
|
||||
import io.xpipe.app.comp.base.TextAreaComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.AppNames;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
import javafx.scene.control.TextArea;
|
||||
|
||||
public class McpCategory extends AppPrefsCategory {
|
||||
|
||||
@@ -22,26 +30,11 @@ public class McpCategory extends AppPrefsCategory {
|
||||
return new LabelGraphic.IconGraphic("mdi2c-chat-processing-outline");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseRegionBuilder<?, ?> create() {
|
||||
private ObservableValue<String> createMcpConfig(String format) {
|
||||
var prefs = AppPrefs.get();
|
||||
|
||||
var mcpConfig = Bindings.createStringBinding(
|
||||
return Bindings.createStringBinding(
|
||||
() -> {
|
||||
var template = """
|
||||
{
|
||||
"mcpServers": {
|
||||
"%s": {
|
||||
"type": "streamable-http",
|
||||
"url": "http://localhost:%s/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer %s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
return template.formatted(
|
||||
return format.formatted(
|
||||
AppNames.ofCurrent().getKebapName(),
|
||||
AppBeaconServer.get().getPort(),
|
||||
prefs.apiKey().get() != null
|
||||
@@ -50,22 +43,110 @@ public class McpCategory extends AppPrefsCategory {
|
||||
.strip();
|
||||
},
|
||||
prefs.apiKey());
|
||||
var mcpConfigProp = new SimpleStringProperty();
|
||||
mcpConfigProp.bind(mcpConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseRegionBuilder<?, ?> create() {
|
||||
var prefs = AppPrefs.get();
|
||||
|
||||
var vsCodeTemplate = createMcpConfig("""
|
||||
{
|
||||
"servers": {
|
||||
"%s": {
|
||||
"type": "http",
|
||||
"url": "http://localhost:%s/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer %s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
var cursorTemplate = createMcpConfig("""
|
||||
{
|
||||
"mcpServers": {
|
||||
"%s": {
|
||||
"type": "streamable-http",
|
||||
"url": "http://localhost:%s/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer %s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
var warpTemplate = createMcpConfig("""
|
||||
{
|
||||
"%s": {
|
||||
"serverUrl": "http://localhost:%s/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer %s"
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
var tabComp = RegionBuilder.of(() -> {
|
||||
var vsCode = new TextArea();
|
||||
vsCode.setEditable(false);
|
||||
vsCode.textProperty().bind(vsCodeTemplate);
|
||||
vsCode.setPrefRowCount(12);
|
||||
var vsCodeTab = new Tab();
|
||||
vsCodeTab.textProperty().bind(AppI18n.observable("vscode"));
|
||||
vsCodeTab.setContent(vsCode);
|
||||
vsCodeTab.setClosable(false);
|
||||
|
||||
var cursor = new TextArea();
|
||||
cursor.setEditable(false);
|
||||
cursor.textProperty().bind(cursorTemplate);
|
||||
cursor.setPrefRowCount(12);
|
||||
var cursorTab = new Tab();
|
||||
cursorTab.textProperty().bind(AppI18n.observable("cursor"));
|
||||
cursorTab.setContent(cursor);
|
||||
cursorTab.setClosable(false);
|
||||
|
||||
var warp = new TextArea();
|
||||
warp.setEditable(false);
|
||||
warp.textProperty().bind(warpTemplate);
|
||||
warp.setPrefRowCount(12);
|
||||
var warpTab = new Tab();
|
||||
warpTab.textProperty().bind(AppI18n.observable("warp"));
|
||||
warpTab.setContent(warp);
|
||||
warpTab.setClosable(false);
|
||||
|
||||
var claude = new TextArea();
|
||||
claude.setEditable(false);
|
||||
claude.textProperty().bind(vsCodeTemplate);
|
||||
claude.setPrefRowCount(12);
|
||||
var claudeTab = new Tab();
|
||||
claudeTab.textProperty().bind(AppI18n.observable("claude"));
|
||||
claudeTab.setContent(claude);
|
||||
claudeTab.setClosable(false);
|
||||
|
||||
var tabPane = new TabPane();
|
||||
tabPane.getTabs().addAll(vsCodeTab, cursorTab, warpTab, claudeTab);
|
||||
return tabPane;
|
||||
});
|
||||
|
||||
return new OptionsBuilder()
|
||||
.addTitle("mcpServer")
|
||||
.sub(new OptionsBuilder()
|
||||
.pref(prefs.enableMcpServer)
|
||||
.addToggle(prefs.enableMcpServer)
|
||||
.nameAndDescription("mcpClientConfigurationDetails")
|
||||
.addComp(tabComp)
|
||||
.hide(prefs.enableMcpServer.not())
|
||||
.pref(prefs.enableMcpMutationTools)
|
||||
.addToggle(prefs.enableMcpMutationTools)
|
||||
.nameAndDescription("mcpClientConfigurationDetails")
|
||||
.addComp(new TextAreaComp(mcpConfigProp).applyStructure(struc -> {
|
||||
struc.getTextArea().setEditable(false);
|
||||
struc.getTextArea().setPrefRowCount(12);
|
||||
.hide(prefs.enableMcpServer.not())
|
||||
.pref(prefs.mcpAdditionalContext)
|
||||
.addComp(new IntegratedTextAreaComp(prefs.mcpAdditionalContext, false, "prompt", new SimpleStringProperty("txt")).applyStructure(structure -> {
|
||||
structure.getTextArea().promptTextProperty().bind(AppI18n.observable("mcpAdditionalContextSample"));
|
||||
}))
|
||||
.hide(prefs.enableMcpServer.not()))
|
||||
.hide(prefs.enableMcpServer.not())
|
||||
)
|
||||
.buildComp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ public class TerminalCategory extends AppPrefsCategory {
|
||||
"If you can read this, the terminal integration works", false)))
|
||||
.preferTabs(false)
|
||||
.logIfEnabled(false)
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.launch();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ public class TroubleshootCategory extends AppPrefsCategory {
|
||||
var script = AppInstallation.ofCurrent().getDaemonDebugScriptPath();
|
||||
TerminalLaunch.builder()
|
||||
.title(AppNames.ofCurrent().getName() + " Debug")
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.localScript(sc -> new ShellScript(
|
||||
sc.getShellDialect().runScriptCommand(sc, script.toString())))
|
||||
.launch();
|
||||
|
||||
@@ -92,7 +92,7 @@ public class BitwardenPasswordManager implements PasswordManager {
|
||||
.localScript(script)
|
||||
.logIfEnabled(false)
|
||||
.preferTabs(false)
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.launch();
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class DashlanePasswordManager implements PasswordManager {
|
||||
.title("Dashlane login")
|
||||
.localScript(script)
|
||||
.logIfEnabled(false)
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.launch();
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package io.xpipe.app.pwman;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
import io.xpipe.app.process.*;
|
||||
import io.xpipe.app.secret.SecretManager;
|
||||
import io.xpipe.app.secret.SecretPromptStrategy;
|
||||
@@ -13,7 +15,7 @@ import io.xpipe.app.util.AskpassAlert;
|
||||
import io.xpipe.core.*;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@@ -23,12 +25,16 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@JsonTypeName("keeper")
|
||||
@Getter
|
||||
@@ -37,11 +43,347 @@ import java.util.UUID;
|
||||
@Jacksonized
|
||||
public class KeeperPasswordManager implements PasswordManager {
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public interface KeeperAuth {
|
||||
|
||||
static List<Class<?>> getClasses() {
|
||||
var l = new ArrayList<Class<?>>();
|
||||
l.add(None.class);
|
||||
l.add(Sms.class);
|
||||
l.add(AuthenticatorApp.class);
|
||||
l.add(SecurityKey.class);
|
||||
l.add(Other.class);
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
default List<String> getTotpDurationValues() {
|
||||
var values = List.of("login", "12_hours", "24_hours", "30_days", "forever");
|
||||
return values;
|
||||
}
|
||||
|
||||
String constructKeeperInput(KeeperPasswordManager passwordManager) throws Exception;
|
||||
|
||||
Duration getCacheDuration();
|
||||
|
||||
Duration getCommandTimeout();
|
||||
|
||||
String cleanMessage(String output);
|
||||
|
||||
@JsonTypeName("sms")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class Sms implements KeeperAuth {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(Property<Sms> p) {
|
||||
var duration = new SimpleStringProperty(p.getValue().getTotpDuration());
|
||||
return new OptionsBuilder()
|
||||
.name("keeperTotpDuration")
|
||||
.description(AppI18n.observable(
|
||||
"keeperTotpDurationDescription", "login | 12_hours | 24_hours | 30_days | forever"))
|
||||
.addString(duration)
|
||||
.bind(
|
||||
() -> {
|
||||
return Sms.builder()
|
||||
.totpDuration(duration.get())
|
||||
.build();
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
String totpDuration;
|
||||
|
||||
private int getTotpDurationIndex() {
|
||||
var values = getTotpDurationValues();
|
||||
var index = totpDuration != null ? values.indexOf(totpDuration) : -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
private void sendInitialSms() throws Exception {
|
||||
var sc = getOrStartShell();
|
||||
var b = CommandBuilder.of()
|
||||
.add(getExecutable(), "get")
|
||||
.addLiteral("test");
|
||||
var file = sc.getSystemTemporaryDirectory().join("keeper" + Math.abs(new Random().nextInt()) + ".txt");
|
||||
var input = """
|
||||
|
||||
1
|
||||
-
|
||||
q
|
||||
""";
|
||||
sc.view().writeTextFile(file, input);
|
||||
|
||||
var fullCommand = CommandBuilder.of()
|
||||
.add(sc.getShellDialect() == ShellDialects.CMD ? "type" : "cat")
|
||||
.addFile(file)
|
||||
.add("|")
|
||||
.add(b);
|
||||
sc.command(fullCommand).sensitive().execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String constructKeeperInput(KeeperPasswordManager passwordManager) throws Exception {
|
||||
sendInitialSms();
|
||||
|
||||
var index = getTotpDurationIndex();
|
||||
if (passwordManager.isHasCompletedRequestInSession() && index > 0) {
|
||||
var input = """
|
||||
|
||||
1
|
||||
|
||||
""";
|
||||
return input;
|
||||
} else {
|
||||
var totp = AskpassAlert.queryRaw("Enter Keeper Commander SMS Code", null, true);
|
||||
if (totp.getState() != SecretQueryState.NORMAL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var input = """
|
||||
|
||||
1%s
|
||||
%s
|
||||
|
||||
""".formatted(
|
||||
index != -1 ? "\n" + getTotpDurationValues().get(index) : "",
|
||||
totp.getSecret().getSecretValue());
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCacheDuration() {
|
||||
return getTotpDurationIndex() < 1 ? Duration.ofDays(1) : Duration.ofSeconds(30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCommandTimeout() {
|
||||
return Duration.ofSeconds(25);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cleanMessage(String output) {
|
||||
return output
|
||||
.replaceFirst("""
|
||||
Select your 2FA method:
|
||||
1. Send SMS Code.+
|
||||
q. Cancel login
|
||||
""", "")
|
||||
.replace(" Invalid entry, additional factors of authentication shown may be configured if not currently enabled.", "")
|
||||
.replace("""
|
||||
2FA Code Duration: Require Every Login.
|
||||
To change duration: 2fa_duration=login|12_hours|24_hours|30_days|forever
|
||||
""", "");
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("authenticatorApp")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class AuthenticatorApp implements KeeperAuth {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(Property<AuthenticatorApp> p) {
|
||||
var duration = new SimpleStringProperty(p.getValue().getTotpDuration());
|
||||
return new OptionsBuilder()
|
||||
.name("keeperTotpDuration")
|
||||
.description(AppI18n.observable(
|
||||
"keeperTotpDurationDescription", "login | 12_hours | 24_hours | 30_days | forever"))
|
||||
.addString(duration)
|
||||
.bind(
|
||||
() -> {
|
||||
return AuthenticatorApp.builder()
|
||||
.totpDuration(duration.get())
|
||||
.build();
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
String totpDuration;
|
||||
|
||||
private int getTotpDurationIndex() {
|
||||
var values = getTotpDurationValues();
|
||||
var index = totpDuration != null ? values.indexOf(totpDuration) : -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
|
||||
var index = getTotpDurationIndex();
|
||||
if (passwordManager.isHasCompletedRequestInSession() && index > 0) {
|
||||
var input = """
|
||||
|
||||
1
|
||||
|
||||
""";
|
||||
return input;
|
||||
} else {
|
||||
var totp = AskpassAlert.queryRaw("Enter Keeper 2FA Code", null, true);
|
||||
if (totp.getState() != SecretQueryState.NORMAL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var input = """
|
||||
|
||||
1%s
|
||||
%s
|
||||
|
||||
""".formatted(
|
||||
index != -1 ? "\n" + getTotpDurationValues().get(index) : "",
|
||||
totp.getSecret().getSecretValue());
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCacheDuration() {
|
||||
return getTotpDurationIndex() < 1 ? Duration.ofDays(1) : Duration.ofSeconds(30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCommandTimeout() {
|
||||
return Duration.ofSeconds(25);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cleanMessage(String output) {
|
||||
return output.replace("""
|
||||
Select your 2FA method:
|
||||
1. TOTP (Google and Microsoft Authenticator) \s
|
||||
q. Cancel login
|
||||
""", "")
|
||||
.replace(
|
||||
"""
|
||||
Selection: Invalid entry, additional factors of authentication shown may be configured if not currently enabled.
|
||||
Selection:\s
|
||||
2FA Code Duration: Require Every Login.
|
||||
To change duration: 2fa_duration=login|12_hours|24_hours|30_days|forever
|
||||
""", "")
|
||||
.replace(
|
||||
"""
|
||||
This account requires 2FA Authentication
|
||||
|
||||
1. TOTP (Google and Microsoft Authenticator) \s
|
||||
q. Quit login attempt and return to Commander prompt
|
||||
""", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JsonTypeName("securityKey")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class SecurityKey implements KeeperAuth {
|
||||
|
||||
@Override
|
||||
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
|
||||
var input = """
|
||||
|
||||
1
|
||||
|
||||
""";
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCacheDuration() {
|
||||
return Duration.ofDays(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCommandTimeout() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cleanMessage(String output) {
|
||||
return output.replace("""
|
||||
Select your 2FA method:
|
||||
1. WebAuthN (FIDO2 Security Key) \s
|
||||
q. Cancel login
|
||||
""", "")
|
||||
.replace(" Invalid entry, additional factors of authentication shown may be configured if not currently enabled.", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonTypeName("other")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class Other implements KeeperAuth {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static String getOptionsNameKey() {
|
||||
return "keeperOtherAuth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCommandTimeout() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cleanMessage(String output) {
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
|
||||
var input = """
|
||||
|
||||
1
|
||||
|
||||
""";
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCacheDuration() {
|
||||
return Duration.ofDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("none")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class None implements KeeperAuth {
|
||||
|
||||
@Override
|
||||
public Duration getCommandTimeout() {
|
||||
return Duration.ofSeconds(25);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cleanMessage(String output) {
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
|
||||
var input = """
|
||||
|
||||
1
|
||||
|
||||
""";
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getCacheDuration() {
|
||||
return Duration.ofSeconds(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final UUID KEEPER_PASSWORD_ID = UUID.randomUUID();
|
||||
private static ShellControl SHELL;
|
||||
private final Boolean mfa;
|
||||
private final String totpDuration;
|
||||
|
||||
private final KeeperAuth twoFactorAuth;
|
||||
@JsonIgnore
|
||||
private boolean hasCompletedRequestInSession;
|
||||
|
||||
@@ -53,28 +395,27 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
return SHELL;
|
||||
}
|
||||
|
||||
private String getExecutable(ShellControl sc) {
|
||||
private static String getExecutable() {
|
||||
return OsType.ofLocal() == OsType.WINDOWS ? "keeper-commander" : "keeper";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(Property<KeeperPasswordManager> p) {
|
||||
var mfa = new SimpleBooleanProperty(
|
||||
p.getValue().getMfa() != null ? p.getValue().getMfa() : false);
|
||||
var duration = new SimpleStringProperty(p.getValue().getTotpDuration());
|
||||
var mfa = new SimpleObjectProperty<>(p.getValue().getTwoFactorAuth() != null ? p.getValue().getTwoFactorAuth() : new KeeperAuth.None());
|
||||
|
||||
var choice = OptionsChoiceBuilder.builder()
|
||||
.allowNull(false)
|
||||
.available(KeeperAuth.getClasses())
|
||||
.property(mfa)
|
||||
.build();
|
||||
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("keeperUseMfa")
|
||||
.addToggle(mfa)
|
||||
.name("keeperTotpDuration")
|
||||
.description(AppI18n.observable(
|
||||
"keeperTotpDurationDescription", "login | 12_hours | 24_hours | 30_days | forever"))
|
||||
.addString(duration)
|
||||
.hide(mfa.not())
|
||||
.nameAndDescription("keeper2fa")
|
||||
.sub(choice.build(), mfa)
|
||||
.bind(
|
||||
() -> {
|
||||
return KeeperPasswordManager.builder()
|
||||
.mfa(mfa.get())
|
||||
.totpDuration(duration.get())
|
||||
.twoFactorAuth(mfa.get())
|
||||
.build();
|
||||
},
|
||||
p);
|
||||
@@ -100,12 +441,12 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
if (!sc.view().fileExists(config)) {
|
||||
var script = ShellScript.lines(
|
||||
sc.getShellDialect().getEchoCommand("Log in into your Keeper account from the CLI:", false),
|
||||
getExecutable(sc) + " login");
|
||||
getExecutable() + " login");
|
||||
TerminalLaunch.builder()
|
||||
.title("Keeper login")
|
||||
.localScript(script)
|
||||
.logIfEnabled(false)
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.launch();
|
||||
return null;
|
||||
}
|
||||
@@ -128,41 +469,19 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
}
|
||||
|
||||
var b = CommandBuilder.of()
|
||||
.add(getExecutable(sc), "get")
|
||||
.add(getExecutable(), "get")
|
||||
.addLiteral(key)
|
||||
.add("--format", "json", "--unmask")
|
||||
.add("--password")
|
||||
.addLiteral(r.getSecretValue());
|
||||
FilePath file = sc.getSystemTemporaryDirectory().join("keeper" + Math.abs(new Random().nextInt()) + ".txt");
|
||||
if (mfa != null && mfa) {
|
||||
var index = getTotpDurationIndex();
|
||||
if (hasCompletedRequestInSession && index > 0) {
|
||||
var input = """
|
||||
|
||||
1
|
||||
|
||||
""";
|
||||
sc.view().writeTextFile(file, input);
|
||||
} else {
|
||||
var totp = AskpassAlert.queryRaw("Enter Keeper 2FA Code", null, true);
|
||||
if (totp.getState() != SecretQueryState.NORMAL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var input = """
|
||||
|
||||
1%s
|
||||
%s
|
||||
|
||||
""".formatted(
|
||||
index != -1 ? "\n" + getTotpDurationValues().get(index) : "",
|
||||
totp.getSecret().getSecretValue());
|
||||
sc.view().writeTextFile(file, input);
|
||||
}
|
||||
} else {
|
||||
var input = "\n";
|
||||
sc.view().writeTextFile(file, input);
|
||||
var effectiveTwoFactor = twoFactorAuth != null ? twoFactorAuth : new KeeperAuth.None();
|
||||
var input = effectiveTwoFactor.constructKeeperInput(this);
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
sc.view().writeTextFile(file, input);
|
||||
|
||||
var fullB = CommandBuilder.of()
|
||||
.add(sc.getShellDialect() == ShellDialects.CMD ? "type" : "cat")
|
||||
@@ -171,7 +490,11 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
.add(b);
|
||||
var queryCommand = sc.command(fullB);
|
||||
queryCommand.sensitive();
|
||||
queryCommand.killOnTimeout(CountDown.of().start(25_000));
|
||||
|
||||
if (effectiveTwoFactor.getCommandTimeout() != null) {
|
||||
var timeout = effectiveTwoFactor.getCommandTimeout().toMillis();
|
||||
queryCommand.killOnTimeout(CountDown.of().start(timeout));
|
||||
}
|
||||
|
||||
var result = queryCommand.readStdoutAndStderr();
|
||||
var exitCode = queryCommand.getExitCode();
|
||||
@@ -179,26 +502,11 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
sc.view().deleteFileIfPossible(file);
|
||||
|
||||
var out = result[0]
|
||||
.replace("\r\n", "\n")
|
||||
.replace("""
|
||||
Select your 2FA method:
|
||||
1. TOTP (Google and Microsoft Authenticator) \s
|
||||
q. Cancel login
|
||||
""", "")
|
||||
.replace("""
|
||||
Selection: Invalid entry, additional factors of authentication shown may be configured if not currently enabled.
|
||||
Selection:\s
|
||||
2FA Code Duration: Require Every Login.
|
||||
To change duration: 2fa_duration=login|12_hours|24_hours|30_days|forever
|
||||
""", "")
|
||||
.replace("""
|
||||
This account requires 2FA Authentication
|
||||
|
||||
1. TOTP (Google and Microsoft Authenticator) \s
|
||||
q. Quit login attempt and return to Commander prompt
|
||||
""", "")
|
||||
.replace("Selection:", "")
|
||||
.replace("\r\n", "\n");
|
||||
out = effectiveTwoFactor.cleanMessage(out);
|
||||
out = out.replace("Selection:", "")
|
||||
.strip();
|
||||
|
||||
var err = result[1]
|
||||
.replace("\r\n", "\n")
|
||||
.replace("EOF when reading a line", "")
|
||||
@@ -211,6 +519,8 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
}
|
||||
|
||||
var outPrefix = jsonStart <= 0 ? out : out.substring(0, jsonStart);
|
||||
outPrefix = outPrefix.lines().filter(s -> !s.isBlank()).map(s -> s.strip()).collect(Collectors.joining("\n"));
|
||||
|
||||
var outJson = jsonStart <= 0
|
||||
? (jsonEnd != -1 ? out.substring(0, jsonEnd) : out)
|
||||
: (jsonEnd != -1 ? out.substring(jsonStart, jsonEnd) : out.substring(jsonStart));
|
||||
@@ -297,17 +607,6 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getTotpDurationValues() {
|
||||
var values = List.of("login", "12_hours", "24_hours", "30_days", "forever");
|
||||
return values;
|
||||
}
|
||||
|
||||
private int getTotpDurationIndex() {
|
||||
var values = getTotpDurationValues();
|
||||
var index = totpDuration != null ? values.indexOf(totpDuration) : -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyPlaceholder() {
|
||||
return "Record UID";
|
||||
@@ -320,6 +619,7 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
|
||||
@Override
|
||||
public Duration getCacheDuration() {
|
||||
return (mfa != null && mfa && getTotpDurationIndex() < 1) ? Duration.ofDays(10) : Duration.ofSeconds(30);
|
||||
var effectiveTwoFactor = twoFactorAuth != null ? twoFactorAuth : new KeeperAuth.None();
|
||||
return effectiveTwoFactor.getCacheDuration();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class LastpassPasswordManager implements PasswordManager {
|
||||
.title("LastPass login")
|
||||
.localScript(script)
|
||||
.logIfEnabled(false)
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.launch();
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -12,7 +12,7 @@ public class DataStorageQuery {
|
||||
var narrow = found.stream()
|
||||
.filter(dataStoreEntry -> dataStoreEntry.getName().equalsIgnoreCase(connection))
|
||||
.toList();
|
||||
if (narrow.size() == 1) {
|
||||
if (narrow.size() >= 1) {
|
||||
return narrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,14 @@ public abstract class ControllableTerminalSession extends TerminalView.TerminalS
|
||||
protected Rect lastBounds;
|
||||
protected boolean customBounds;
|
||||
|
||||
protected ControllableTerminalSession(ProcessHandle terminalProcess) {
|
||||
super(terminalProcess);
|
||||
protected ControllableTerminalSession(ProcessHandle terminalProcess, ExternalTerminalType terminalType) {
|
||||
super(terminalProcess, terminalType);
|
||||
}
|
||||
|
||||
public abstract void own();
|
||||
|
||||
public abstract void disown();
|
||||
|
||||
public abstract void removeBorders();
|
||||
|
||||
public abstract void show();
|
||||
|
||||
public abstract void minimize();
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.xpipe.app.platform.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.util.GlobalTimer;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
@@ -108,7 +109,11 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
var show = new EventHandler<WindowEvent>() {
|
||||
@Override
|
||||
public void handle(WindowEvent event) {
|
||||
update(stack);
|
||||
GlobalTimer.delay(() -> {
|
||||
Platform.runLater(() -> {
|
||||
update(stack);
|
||||
});
|
||||
}, Duration.ofMillis(100));
|
||||
}
|
||||
};
|
||||
var hide = new EventHandler<WindowEvent>() {
|
||||
@@ -117,6 +122,16 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
model.onClose();
|
||||
}
|
||||
};
|
||||
var scale = new ChangeListener<Number>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
|
||||
GlobalTimer.delay(() -> {
|
||||
Platform.runLater(() -> {
|
||||
update(stack);
|
||||
});
|
||||
}, Duration.ofMillis(500));
|
||||
}
|
||||
};
|
||||
|
||||
var parent = new AtomicReference<Parent>();
|
||||
stack.sceneProperty().subscribe(scene -> {
|
||||
@@ -128,6 +143,7 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
s.iconifiedProperty().removeListener(iconified);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
s.outputScaleXProperty().addListener(scale);
|
||||
if (parent.get() != null) {
|
||||
parent.get().boundsInParentProperty().removeListener(bounds);
|
||||
parent.set(null);
|
||||
@@ -140,6 +156,7 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
s.iconifiedProperty().addListener(iconified);
|
||||
s.addEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.addEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
s.outputScaleXProperty().removeListener(scale);
|
||||
// As in practice this node is wrapped in another stack pane
|
||||
// We have to listen to the parent bounds to actually receive bounds changes
|
||||
stack.getParent().boundsInParentProperty().addListener(bounds);
|
||||
@@ -150,7 +167,7 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
}
|
||||
|
||||
private void update(Region region) {
|
||||
if (region.getScene() == null || region.getScene().getWindow() == null) {
|
||||
if (region.getScene() == null || region.getScene().getWindow() == null || NativeWinWindowControl.MAIN_WINDOW == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,11 +178,26 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
|
||||
var scene = region.getScene();
|
||||
var windowRect = NativeWinWindowControl.MAIN_WINDOW.getBounds();
|
||||
var x = windowRect.getX() + ((bounds.getMinX() + p.getLeft() + scene.getX()) * sx);
|
||||
var y = windowRect.getY() + ((bounds.getMinY() + p.getTop() + scene.getY()) * sy);
|
||||
if (windowRect.getX() == 0.0 && windowRect.getY() == 0.0 && windowRect.getW() == 0 && windowRect.getH() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var xPadding = ((bounds.getMinX() + p.getLeft() + scene.getX()) * sx);
|
||||
var yPadding = ((bounds.getMinY() + p.getTop() + scene.getY()) * sy);
|
||||
var x = windowRect.getX() + xPadding;
|
||||
var y = windowRect.getY() + yPadding;
|
||||
var w = (bounds.getWidth() * sx) - p.getRight() - p.getLeft();
|
||||
var h = (bounds.getHeight() * sy) - p.getBottom() - p.getTop();
|
||||
|
||||
if (x + w > windowRect.getX() + windowRect.getW()) {
|
||||
x = windowRect.getX() + 10;
|
||||
w = windowRect.getW() - 20;
|
||||
}
|
||||
if (y + h > windowRect.getY() + windowRect.getH()) {
|
||||
y = windowRect.getY() + 10;
|
||||
h = windowRect.getH() - 20;
|
||||
}
|
||||
|
||||
model.resizeView(
|
||||
(int) Math.round(x),
|
||||
(int) Math.round(y),
|
||||
|
||||
@@ -56,7 +56,7 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
Platform.runLater(() -> {
|
||||
update(stack);
|
||||
});
|
||||
}, Duration.ofMillis(100));
|
||||
}, Duration.ofMillis(500));
|
||||
}
|
||||
};
|
||||
var update = new ChangeListener<Number>() {
|
||||
@@ -143,11 +143,22 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
return;
|
||||
}
|
||||
|
||||
var x = windowRect.getX() + ((bounds.getMinX() + p.getLeft() + scene.getX()) * sx);
|
||||
var y = windowRect.getY() + ((bounds.getMinY() + p.getTop() + scene.getY()) * sy);
|
||||
var xPadding = ((bounds.getMinX() + p.getLeft() + scene.getX()) * sx);
|
||||
var yPadding = ((bounds.getMinY() + p.getTop() + scene.getY()) * sy);
|
||||
var x = windowRect.getX() + xPadding;
|
||||
var y = windowRect.getY() + yPadding;
|
||||
var w = (bounds.getWidth() * sx) - p.getRight() - p.getLeft();
|
||||
var h = (bounds.getHeight() * sy) - p.getBottom() - p.getTop();
|
||||
|
||||
if (x + w > windowRect.getX() + windowRect.getW()) {
|
||||
x = windowRect.getX() + 10;
|
||||
w = windowRect.getW() - 20;
|
||||
}
|
||||
if (y + h > windowRect.getY() + windowRect.getH()) {
|
||||
y = windowRect.getY() + 10;
|
||||
h = windowRect.getH() - 20;
|
||||
}
|
||||
|
||||
model.resizeView(
|
||||
(int) Math.round(x),
|
||||
(int) Math.round(y),
|
||||
|
||||
@@ -110,9 +110,9 @@ public class TerminalDockHubManager {
|
||||
private final AppLayoutModel.QueueEntry queueEntry = new AppLayoutModel.QueueEntry(
|
||||
AppI18n.observable("toggleTerminalDock"), new LabelGraphic.NodeGraphic(() -> {
|
||||
var inner = new FontIcon();
|
||||
inner.iconCodeProperty().bind(PlatformThread.sync(Bindings.createObjectBinding(() -> {
|
||||
return detached.get() || minimized.get() || !showing.get() ? MaterialDesignC.CONSOLE_LINE : MaterialDesignC.CONSOLE;
|
||||
}, detached, minimized, showing)));
|
||||
inner.iconCodeProperty().bind(Bindings.createObjectBinding(() -> {
|
||||
return detached.get() || minimized.get() ? MaterialDesignC.CONSOLE_LINE : MaterialDesignC.CONSOLE;
|
||||
}, detached, minimized));
|
||||
inner.getStyleClass().add("graphic");
|
||||
inner.getStyleClass().add("terminal-dock-button");
|
||||
return inner;
|
||||
@@ -186,15 +186,11 @@ public class TerminalDockHubManager {
|
||||
return;
|
||||
}
|
||||
|
||||
var term = AppPrefs.get().terminalType().getValue();
|
||||
var term = controllable.get().getTerminalType();
|
||||
if (term instanceof TrackableTerminalType t) {
|
||||
if (t.getDockMode() == TerminalDockMode.UNSUPPORTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.getDockMode() == TerminalDockMode.BORDERLESS) {
|
||||
controllable.get().removeBorders();
|
||||
}
|
||||
}
|
||||
|
||||
var dock = !detached.get();
|
||||
|
||||
@@ -64,7 +64,7 @@ public class TerminalDockView {
|
||||
}
|
||||
}
|
||||
|
||||
if (terminal.isCustomBounds()) {
|
||||
if (!wasCustom && terminal.isCustomBounds()) {
|
||||
terminal.disown();
|
||||
}
|
||||
});
|
||||
@@ -72,6 +72,8 @@ public class TerminalDockView {
|
||||
|
||||
public synchronized void trackTerminal(ControllableTerminalSession terminal, boolean dock) {
|
||||
if (viewActive && dock && viewBounds != null) {
|
||||
terminal.own();
|
||||
|
||||
// Bring main window to foreground since initial launch
|
||||
NativeWinWindowControl.MAIN_WINDOW.activate();
|
||||
|
||||
@@ -83,7 +85,7 @@ public class TerminalDockView {
|
||||
|
||||
// Bring terminal window in front of main window
|
||||
terminal.focus();
|
||||
|
||||
|
||||
terminal.updatePosition(windowBoundsFunction.apply(viewBounds));
|
||||
updateCustomBounds();
|
||||
}
|
||||
@@ -231,7 +233,7 @@ public class TerminalDockView {
|
||||
});
|
||||
}
|
||||
|
||||
public void resizeView(int x, int y, int w, int h) {
|
||||
public synchronized void resizeView(int x, int y, int w, int h) {
|
||||
if (w < 100 || h < 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class TerminalLaunch {
|
||||
boolean logIfEnabled = true;
|
||||
|
||||
@Builder.Default
|
||||
boolean alwaysKeepOpen = false;
|
||||
boolean pauseOnExit = AppPrefs.get().terminalAlwaysPauseOnExit().getValue();
|
||||
|
||||
ExternalTerminalType terminal;
|
||||
|
||||
@@ -68,8 +68,7 @@ public class TerminalLaunch {
|
||||
getFullTitle(),
|
||||
directory,
|
||||
request != null ? request : UUID.randomUUID(),
|
||||
logIfEnabled,
|
||||
alwaysKeepOpen,
|
||||
logIfEnabled, pauseOnExit,
|
||||
command);
|
||||
TerminalLauncher.open(List.of(pane), preferTabs, type);
|
||||
}
|
||||
|
||||
@@ -143,8 +143,6 @@ public class TerminalLauncher {
|
||||
config.getProcessControl() instanceof ShellControl
|
||||
? type.additionalInitCommands()
|
||||
: TerminalInitFunction.none());
|
||||
var alwaysPromptRestart = config.isAlwaysKeepOpen()
|
||||
|| AppPrefs.get().terminalAlwaysPauseOnExit().getValue();
|
||||
TerminalLauncherManager.submitAsync(
|
||||
config.getRequest(), config.getProcessControl(), terminalConfig, config.getDirectory(), latch);
|
||||
var effectivePreferTabs =
|
||||
@@ -152,7 +150,7 @@ public class TerminalLauncher {
|
||||
|
||||
var paneIndex = configs.indexOf(config);
|
||||
var paneConfig = TerminalPaneConfiguration.create(
|
||||
config.getRequest(), entry, config.getTitle(), paneIndex, effectivePreferTabs, alwaysPromptRestart);
|
||||
config.getRequest(), entry, config.getTitle(), paneIndex, effectivePreferTabs, config.isAlwaysKeepOpen());
|
||||
paneList.add(paneConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,9 +144,10 @@ public class TerminalView {
|
||||
}
|
||||
|
||||
private Optional<TerminalSession> createTerminalSession(ProcessHandle terminalProcess) {
|
||||
var type = AppPrefs.get().terminalType().getValue();
|
||||
return switch (OsType.ofLocal()) {
|
||||
case OsType.Linux ignored -> Optional.of(new TerminalSession(terminalProcess));
|
||||
case OsType.MacOs ignored -> Optional.of(new TerminalSession(terminalProcess));
|
||||
case OsType.Linux ignored -> Optional.of(new TerminalSession(terminalProcess, type));
|
||||
case OsType.MacOs ignored -> Optional.of(new TerminalSession(terminalProcess, type));
|
||||
case OsType.Windows ignored -> {
|
||||
var controls = NativeWinWindowControl.byPid(terminalProcess.pid());
|
||||
if (controls.isEmpty()) {
|
||||
@@ -158,7 +159,7 @@ public class TerminalView {
|
||||
}
|
||||
}
|
||||
|
||||
yield Optional.of(new WindowsTerminalSession(terminalProcess, controls.getFirst()));
|
||||
yield Optional.of(new WindowsTerminalSession(terminalProcess, type, controls.getFirst()));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -239,9 +240,11 @@ public class TerminalView {
|
||||
public static class TerminalSession {
|
||||
|
||||
protected final ProcessHandle terminalProcess;
|
||||
protected final ExternalTerminalType terminalType;
|
||||
|
||||
protected TerminalSession(ProcessHandle terminalProcess) {
|
||||
protected TerminalSession(ProcessHandle terminalProcess, ExternalTerminalType terminalType) {
|
||||
this.terminalProcess = terminalProcess;
|
||||
this.terminalType = terminalType;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
|
||||
@@ -89,9 +89,9 @@ public interface WarpTerminalType extends ExternalTerminalType, TrackableTermina
|
||||
|
||||
var scriptArg = URLEncoder.encode(movedScriptFile.toString(), StandardCharsets.UTF_8);
|
||||
if (!configuration.isPreferTabs()) {
|
||||
DesktopHelper.openUrl("warp://action/new_window?path=" + scriptArg);
|
||||
DesktopHelper.openAssociatedApplication("warp://action/new_window?path=" + scriptArg);
|
||||
} else {
|
||||
DesktopHelper.openUrl("warp://action/new_tab?path=" + scriptArg);
|
||||
DesktopHelper.openAssociatedApplication("warp://action/new_tab?path=" + scriptArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,9 +125,9 @@ public interface WarpTerminalType extends ExternalTerminalType, TrackableTermina
|
||||
public void launch(TerminalLaunchConfiguration configuration) {
|
||||
var pane = configuration.single();
|
||||
if (!configuration.isPreferTabs()) {
|
||||
DesktopHelper.openUrl("warp://action/new_window?path=" + pane.getScriptFile());
|
||||
DesktopHelper.openAssociatedApplication("warp://action/new_window?path=" + pane.getScriptFile());
|
||||
} else {
|
||||
DesktopHelper.openUrl("warp://action/new_tab?path=" + pane.getScriptFile());
|
||||
DesktopHelper.openAssociatedApplication("warp://action/new_tab?path=" + pane.getScriptFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.app.util.WindowsRegistry;
|
||||
import io.xpipe.core.FilePath;
|
||||
|
||||
import io.xpipe.core.OsType;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -56,7 +57,11 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal
|
||||
}
|
||||
|
||||
default Path getSocketDir() {
|
||||
return AppSystemInfo.ofCurrent().getUserHome().resolve(".local", "share", "wezterm");
|
||||
if (OsType.ofLocal() == OsType.LINUX) {
|
||||
return Path.of(System.getenv("XDG_RUNTIME_DIR"), "wezterm");
|
||||
} else {
|
||||
return AppSystemInfo.ofCurrent().getUserHome().resolve(".local", "share", "wezterm");
|
||||
}
|
||||
}
|
||||
|
||||
default Optional<Path> waitForInstanceStart(int count) {
|
||||
@@ -114,6 +119,7 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal
|
||||
default void launch(TerminalLaunchConfiguration configuration) throws Exception {
|
||||
var base = getWeztermCommandBase();
|
||||
var activeSocket = waitForInstanceStart(1);
|
||||
var tabid = "0";
|
||||
// Always start a new window for split panes as we can't find the pane index to start with
|
||||
if (activeSocket.isEmpty() || configuration.getPanes().size() > 1 || !configuration.isPreferTabs()) {
|
||||
var gui = CommandBuilder.of().add(base.buildSimple().replace("wezterm.exe", "wezterm-gui.exe"));
|
||||
@@ -122,24 +128,36 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal
|
||||
.add("start", "--always-new-process")
|
||||
.add(configuration.getPanes().getFirst().getDialectLaunchCommand());
|
||||
ExternalApplicationHelper.startAsync(command);
|
||||
activeSocket = waitForInstanceStart(50);
|
||||
if (activeSocket.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
var command = CommandBuilder.of()
|
||||
.add(base)
|
||||
.add("cli", "spawn")
|
||||
.add(configuration.getPanes().getFirst().getDialectLaunchCommand());
|
||||
command.fixedEnvironment("WEZTERM_UNIX_SOCKET", activeSocket.get().toString());
|
||||
LocalShell.getShell()
|
||||
tabid = LocalShell.getShell()
|
||||
.command(command)
|
||||
.withWorkingDirectory(FilePath.of(getSocketDir()))
|
||||
.execute();
|
||||
.readStdoutOrThrow();
|
||||
}
|
||||
|
||||
if (configuration.getPanes().size() > 1) {
|
||||
activeSocket = waitForInstanceStart(50);
|
||||
if (activeSocket.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var titleCommand = CommandBuilder.of()
|
||||
.add(base)
|
||||
.add("cli", "set-tab-title")
|
||||
.add("--tab-id", tabid)
|
||||
.addQuoted(configuration.getColoredTitle());
|
||||
titleCommand.fixedEnvironment("WEZTERM_UNIX_SOCKET", activeSocket.get().toString());
|
||||
// Sometimes the tab ids don't exist even though it just returned them to us
|
||||
// So just ignore any errors
|
||||
LocalShell.getShell()
|
||||
.command(titleCommand)
|
||||
.withWorkingDirectory(FilePath.of(getSocketDir()))
|
||||
.executeAndCheck();
|
||||
|
||||
if (configuration.getPanes().size() > 1) {
|
||||
var direction = AppPrefs.get().terminalSplitStrategy().getValue();
|
||||
var directionIterator = direction.iterator();
|
||||
for (int i = 1; i < configuration.getPanes().size(); i++) {
|
||||
@@ -163,8 +181,7 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal
|
||||
activeSocket.get().toString()))
|
||||
.withWorkingDirectory(FilePath.of(getSocketDir()))
|
||||
.execute();
|
||||
directionIterator.next();
|
||||
}
|
||||
directionIterator.next(); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ public final class WindowsTerminalSession extends ControllableTerminalSession {
|
||||
|
||||
NativeWinWindowControl control;
|
||||
|
||||
public WindowsTerminalSession(ProcessHandle terminal, NativeWinWindowControl control) {
|
||||
super(terminal);
|
||||
public WindowsTerminalSession(ProcessHandle terminal, ExternalTerminalType terminalType, NativeWinWindowControl control) {
|
||||
super(terminal, terminalType);
|
||||
this.control = control;
|
||||
}
|
||||
|
||||
@@ -46,16 +46,17 @@ public final class WindowsTerminalSession extends ControllableTerminalSession {
|
||||
@Override
|
||||
public void own() {
|
||||
control.takeOwnership(NativeWinWindowControl.MAIN_WINDOW.getWindowHandle());
|
||||
if (terminalType != null && terminalType instanceof TrackableTerminalType t && t.getDockMode() == TerminalDockMode.BORDERLESS) {
|
||||
control.removeBorders();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disown() {
|
||||
control.releaseOwnership();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBorders() {
|
||||
control.removeBorders();
|
||||
if (terminalType != null && terminalType instanceof TrackableTerminalType t && t.getDockMode() == TerminalDockMode.BORDERLESS) {
|
||||
control.restoreBorders();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,7 +28,10 @@ public enum AppDistributionType implements Translatable {
|
||||
HOMEBREW("homebrew", true, () -> {
|
||||
var pkg = AppNames.ofCurrent().getKebapName();
|
||||
return new CommandUpdater(
|
||||
ShellScript.lines("brew upgrade --cask xpipe-io/tap/" + pkg, AppRestart.getTerminalRestartCommand()));
|
||||
ShellScript.lines(
|
||||
"brew upgrade --cask xpipe-io/tap/" + pkg,
|
||||
"if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi",
|
||||
AppRestart.getTerminalRestartCommand()));
|
||||
}),
|
||||
APT_REPO("apt", true, () -> {
|
||||
var pkg = AppNames.ofCurrent().getKebapName();
|
||||
@@ -36,6 +39,7 @@ public enum AppDistributionType implements Translatable {
|
||||
"echo \"+ sudo apt update && sudo apt install -y " + pkg + "\"",
|
||||
"sudo apt update",
|
||||
"sudo apt install -y " + pkg,
|
||||
"if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi",
|
||||
AppRestart.getTerminalRestartCommand()));
|
||||
}),
|
||||
RPM_REPO("rpm", true, () -> {
|
||||
@@ -43,13 +47,15 @@ public enum AppDistributionType implements Translatable {
|
||||
return new CommandUpdater(ShellScript.lines(
|
||||
"echo \"+ sudo yum upgrade " + pkg + " --refresh -y\"",
|
||||
"sudo yum upgrade " + pkg + " --refresh -y",
|
||||
"if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi",
|
||||
AppRestart.getTerminalRestartCommand()));
|
||||
}),
|
||||
AUR("aur", true, () -> {
|
||||
var pkg = AppNames.ofCurrent().getKebapName();
|
||||
return new CommandUpdater(ShellScript.lines(
|
||||
"echo \"+ git clone https://aur.archlinux.org/" + pkg + " . && makepkg -si\"",
|
||||
"cd $(mktemp -d) && git clone https://aur.archlinux.org/" + pkg + " . && makepkg -si --noconfirm",
|
||||
"echo \"+ git -c core.autocrlf=false clone https://aur.archlinux.org/" + pkg + " . && makepkg -si\"",
|
||||
"cd $(mktemp -d) && git -c core.autocrlf=false clone https://aur.archlinux.org/" + pkg + " . && makepkg -si --noconfirm",
|
||||
"if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi",
|
||||
AppRestart.getTerminalRestartCommand()));
|
||||
}),
|
||||
WEBTOP("webtop", true, () -> new WebtopUpdater()),
|
||||
|
||||
@@ -229,7 +229,7 @@ public class AppInstaller {
|
||||
runinstaller
|
||||
if [ "$?" != 0 ]; then
|
||||
echo "Update failed ..."
|
||||
read -rs -k 1 key
|
||||
read key
|
||||
fi
|
||||
""", file, file, AppRestart.getTerminalRestartCommand()));
|
||||
AppOperationMode.executeAfterShutdown(() -> {
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.xpipe.app.process.ShellDialects;
|
||||
import io.xpipe.app.process.ShellScript;
|
||||
import io.xpipe.app.pwman.KeePassXcAssociationKey;
|
||||
import io.xpipe.app.pwman.KeePassXcPasswordManager;
|
||||
import io.xpipe.app.pwman.KeeperPasswordManager;
|
||||
import io.xpipe.app.pwman.PasswordManager;
|
||||
import io.xpipe.app.rdp.ExternalRdpClient;
|
||||
import io.xpipe.app.secret.*;
|
||||
@@ -87,6 +88,7 @@ public class AppJacksonModule extends SimpleModule {
|
||||
context.registerSubtypes(ExternalSpiceClient.getClasses());
|
||||
context.registerSubtypes(SecretRetrievalStrategy.getClasses());
|
||||
context.registerSubtypes(DataStorageGroupStrategy.getClasses());
|
||||
context.registerSubtypes(KeeperPasswordManager.KeeperAuth.getClasses());
|
||||
|
||||
super.setupModule(context);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.List;
|
||||
|
||||
public class DesktopHelper {
|
||||
|
||||
public static void openUrl(String uri) {
|
||||
public static void openBrowser(String uri) {
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
@@ -56,6 +56,30 @@ public class DesktopHelper {
|
||||
});
|
||||
}
|
||||
|
||||
public static void openAssociatedApplication(String uri) {
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
URI parsed;
|
||||
try {
|
||||
parsed = URI.create(uri);
|
||||
} catch (IllegalArgumentException e) {
|
||||
ErrorEventFactory.fromThrowable("Invalid URI: " + uri, e.getCause() != null ? e.getCause() : e)
|
||||
.handle();
|
||||
return;
|
||||
}
|
||||
|
||||
// Windows URL open always uses browser
|
||||
if (OsType.ofLocal() == OsType.WINDOWS) {
|
||||
LocalExec.executeAsync("rundll32", "url.dll,FileProtocolHandler", parsed.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Other OS use associated app
|
||||
openBrowser(uri);
|
||||
}
|
||||
|
||||
public static void browseFile(Path file) {
|
||||
if (file == null || !Files.exists(file)) {
|
||||
return;
|
||||
|
||||
@@ -11,6 +11,6 @@ public class Hyperlinks {
|
||||
public static final String GITHUB_WEBTOP = "https://github.com/xpipe-io/xpipe-webtop";
|
||||
|
||||
public static void open(String uri) {
|
||||
DesktopHelper.openUrl(uri);
|
||||
DesktopHelper.openBrowser(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ open module io.xpipe.app {
|
||||
uses CloudSetupProvider;
|
||||
|
||||
provides ActionProvider with
|
||||
GradleRunMenuProvider,
|
||||
RefreshHubLeafProvider,
|
||||
SetupToolActionProvider,
|
||||
XPipeUrlProvider,
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
}
|
||||
|
||||
.browser .terminal-dock-comp {
|
||||
-fx-padding: 7 0 7 3;
|
||||
-fx-padding: 7 7 7 3;
|
||||
}
|
||||
|
||||
.browser .terminal-dock-comp:empty {
|
||||
|
||||
@@ -296,13 +296,13 @@ project.ext {
|
||||
}
|
||||
|
||||
// JavaFX config
|
||||
devJavafxVersion = '26-ea+19'
|
||||
devJavafxVersion = '27-ea+4'
|
||||
platformName = getPlatformName()
|
||||
useBundledJavaFx = fullVersion
|
||||
bundledJdkJavaFx = ModuleFinder.ofSystem().find("javafx.base").isPresent()
|
||||
// Define a custom JavaFX SDK location
|
||||
// customJavaFxLibsPath = file("C:\\Projects\\jfx\\build\\sdk\\lib")
|
||||
// customJavaFxJmodsPath = file("C:\\Projects\\jfx\\build\\jmods")
|
||||
customJavaFxLibsPath = null; // file("C:\\Projects\\jfx\\build\\sdk\\lib")
|
||||
customJavaFxJmodsPath = null; // file("C:\\Projects\\jfx\\build\\jmods")
|
||||
|
||||
// Other
|
||||
deeplApiKey = findProperty('DEEPL_API_KEY') != null ? findProperty('DEEPL_API_KEY') : ""
|
||||
|
||||
2
dist/build.gradle
vendored
2
dist/build.gradle
vendored
@@ -1,7 +1,7 @@
|
||||
|
||||
plugins {
|
||||
id 'org.beryx.jlink' version '3.2.1'
|
||||
id("com.netflix.nebula.ospackage") version "12.1.1"
|
||||
id("com.netflix.nebula.ospackage") version "12.2.0"
|
||||
id 'org.gradle.crypto.checksum' version '1.4.0'
|
||||
id 'signing'
|
||||
}
|
||||
|
||||
2
dist/changelog/21.2.md
vendored
2
dist/changelog/21.2.md
vendored
@@ -6,4 +6,4 @@
|
||||
- Fix terminal test button not showing for the terminal selection in the settings menu
|
||||
- Fix network switch ports being included in total connection count
|
||||
- Fix various NullPointers
|
||||
- Fix some broken documentation links
|
||||
- Fix some broken documentation links
|
||||
|
||||
45
dist/changelog/21.3.md
vendored
Normal file
45
dist/changelog/21.3.md
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
## Terminal docking
|
||||
|
||||
- Restore proper window borders once a terminal is undocked
|
||||
- Fix terminals randomly undocking
|
||||
- Fix docked terminals in background sometimes being larger than main window
|
||||
- The connection hub button will now always hide the terminal when clicked
|
||||
- Fix file browser docking tabbing being inconsistent
|
||||
- Fix dock being broken after application window has been hidden once
|
||||
|
||||
## Shell detection
|
||||
|
||||
Add new experimental heuristic for detecting custom shells of limited/embedded systems. Now, even if XPipe does not know the shell type and can't communicate with it, it will use heuristics to determine whether the shell is responding properly or the connection timeouted.
|
||||
|
||||
This should make the process of adding connections to custom systems that don't run a common operating system much easier.
|
||||
|
||||
## MCP
|
||||
|
||||
The MCP server integration has been improved. The connection query by name for agents has been improved to reduce ambiguous results. Furthermore, the MCP instructions haven been augmented for various popular clients:
|
||||
|
||||

|
||||
|
||||
There is now also the option to pass additional context to any agent to apply consistent rules and information:
|
||||
|
||||

|
||||
|
||||
The MCP integration can also now be used to manage cisco switches via the new integration:
|
||||
|
||||

|
||||
|
||||
## Other
|
||||
|
||||
- Add support for all other Keeper password manager 2FA methods
|
||||
- Fix vault sync failing in OneDrive directories
|
||||
- Improve rendering performance on Windows
|
||||
- Add support for neovim editor (Thanks to @leycm)
|
||||
- Add browser context menu action for gradlew files to run tasks (Thanks to @leycm)
|
||||
- Fix AUR update failing if git core.autocrlf was set to true (Thanks to @leycm)
|
||||
- Fix WezTerm not using tabs on Linux (Thanks to @leycm)
|
||||
- Fix WezTerm tab titles not being overridden (Thanks to @leycm)
|
||||
- Fix SSH config update for VsCode integration creating connection duplicates
|
||||
- Fix various terminal updaters instantly closing when update failed
|
||||
- Fix VsCode not launching in Webtop due to sandbox restrictions
|
||||
- Add custom theming to Webtop
|
||||
- Fix various NullPointers
|
||||
|
||||
2
dist/jpackage.gradle
vendored
2
dist/jpackage.gradle
vendored
@@ -47,7 +47,7 @@ jlink {
|
||||
options.addAll('--strip-native-debug-symbols', 'exclude-debuginfo-files')
|
||||
}
|
||||
|
||||
if (hasProperty("customJavaFxJmodsPath")) {
|
||||
if (customJavaFxJmodsPath != null) {
|
||||
addExtraModulePath(customJavaFxJmodsPath.toString())
|
||||
} else if (useBundledJavaFx && !bundledJdkJavaFx) {
|
||||
addExtraModulePath(layout.projectDirectory.dir("javafx/${platformName}/${arch}").toString())
|
||||
|
||||
@@ -143,6 +143,10 @@ public class RunFileScriptMenuProvider implements BrowserMenuBranchProvider {
|
||||
protected List<CommandBuilder> createCommand(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
|
||||
var sc = model.getFileSystem().getShell().orElseThrow();
|
||||
var content = ref.getStore().assembleScriptChain(sc, true);
|
||||
if (content == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
var script = ScriptHelper.createExecScript(sc, content.getValue());
|
||||
var builder = CommandBuilder.of().add(sc.getShellDialect().runScriptCommand(sc, script.toString()));
|
||||
for (BrowserEntry entry : entries) {
|
||||
|
||||
@@ -30,6 +30,10 @@ public class RunHubBatchScriptActionProvider implements ActionProvider {
|
||||
for (DataStoreEntryRef<ShellStore> ref : refs) {
|
||||
var sc = ref.getStore().getOrStartSession();
|
||||
var script = scriptStore.getStore().assembleScriptChain(sc, false);
|
||||
if (script == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var cmd = sc.command(script);
|
||||
list.add(new CommandDialog.CommandEntry(ref.get().getName(), cmd));
|
||||
}
|
||||
|
||||
@@ -26,8 +26,10 @@ public class RunHubScriptActionProvider implements ActionProvider {
|
||||
public void executeImpl() throws Exception {
|
||||
var sc = ref.getStore().getOrStartSession();
|
||||
var script = scriptStore.getStore().assembleScriptChain(sc, false);
|
||||
var cmd = sc.command(script);
|
||||
CommandDialog.runAndShow(cmd);
|
||||
if (script != null) {
|
||||
var cmd = sc.command(script);
|
||||
CommandDialog.runAndShow(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,7 +34,7 @@ public class RunTerminalScriptActionProvider implements ActionProvider {
|
||||
.entry(ref.get())
|
||||
.title(scriptStore.get().getName())
|
||||
.command(sc.command(script))
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.launch();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public class PodmanContainerLogsActionProvider implements HubLeafProvider<Podman
|
||||
var d = ref.getStore();
|
||||
var view = d.commandView(d.getCmd().getStore().getHost().getStore().getOrStartSession());
|
||||
TerminalLaunch.builder()
|
||||
.alwaysKeepOpen(true)
|
||||
.pauseOnExit(true)
|
||||
.entry(ref.get())
|
||||
.title("Logs")
|
||||
.command(view.logs(d.getContainerName()))
|
||||
|
||||
@@ -18,7 +18,7 @@ configurations {
|
||||
javafx
|
||||
}
|
||||
|
||||
if (hasProperty("customJavaFxLibsPath")) {
|
||||
if (customJavaFxLibsPath != null) {
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs customJavaFxLibsPath
|
||||
|
||||
3
lang/strings/fixed_en.properties
generated
3
lang/strings/fixed_en.properties
generated
@@ -12,6 +12,7 @@ elementaryTerminal=Elementary Terminal
|
||||
macosTerminal=Terminal.app
|
||||
iterm2=iTerm2
|
||||
warp=Warp
|
||||
claude=Claude
|
||||
wave=Wave
|
||||
tabby=Tabby
|
||||
alacritty=Alacritty
|
||||
@@ -159,3 +160,5 @@ passbolt=Passbolt
|
||||
yakuake=Yakuake
|
||||
remoteViewer=Virt viewer
|
||||
cosmicEdit=Cosmic Editor
|
||||
neovim=Neovim
|
||||
sms=SMS
|
||||
|
||||
12
lang/strings/translations_da.properties
generated
12
lang/strings/translations_da.properties
generated
@@ -760,6 +760,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=script
|
||||
genericScript=Generisk
|
||||
gradleTasks=Gradle-opgaver
|
||||
runTask=Kør opgave
|
||||
archiveName=Arkivets navn
|
||||
compress=Komprimere
|
||||
compressContents=Komprimere indhold
|
||||
@@ -1875,10 +1877,11 @@ testingConnection=Test af forbindelse ...
|
||||
openManagementConsole=Åben administrationskonsol
|
||||
openLxcTerminal=Åbn LXC-terminalen
|
||||
openContainerConsole=Åbn seriel konsol
|
||||
keeperUseMfa=Brug 2FA-godkendelsesapp
|
||||
keeperUseMfaDescription=Aktivér dette, hvis din Keeper-konto kræver en 2FA TOTP for at få adgang til adgangskoder.
|
||||
keeper2fa=2FA-metode
|
||||
keeper2faDescription=Den primære to-faktor-godkendelsesmetode, der er konfigureret til din konto. Aktivér denne, hvis din Keeper-konto kræver to-faktor-autentificering for at få adgang til adgangskoder.
|
||||
keeperTotpDuration=Varighed af brugerdefineret 2FA-kode
|
||||
keeperTotpDurationDescription=Tilsidesæt standardvarigheden for, hvor længe en 2FA-kode er gyldig. Gælder kun, hvis din organisations politik tillader ændring af varigheden.\n\nMulige værdier er: $VALUES$
|
||||
keeperOtherAuth=Andet (RSA SecurID, Duo Security, Keeper DNA osv.)
|
||||
extractReusableIdentities=Udtræk af genanvendelige identiteter
|
||||
identitiesAdded=Identiteter tilføjet
|
||||
syncMode=Synkroniseringstilstand
|
||||
@@ -1945,3 +1948,8 @@ showStatus=Vis status
|
||||
showAllPorts=Vis alle porte
|
||||
activeLicense=Licens
|
||||
activeLicenseDescription=Aktiver en XPipe-licensnøgle
|
||||
authenticatorApp=Autentificerings-app
|
||||
securityKey=Sikkerhedsnøgle
|
||||
mcpAdditionalContext=Yderligere MCP-kontekst
|
||||
mcpAdditionalContextDescription=Yderligere instruktioner, der skal sendes til MCP-klienten. Brug dette til at styre agentens adfærd og give yderligere kontekst til din individuelle opsætning.
|
||||
mcpAdditionalContextSample=- Genstart ikke tjenester og dæmoner automatisk uden at bekræfte det først\n- Når du konfigurerer en netværksgrænseflade, skal du altid bruge 192.168.1.1/24 som gateway
|
||||
|
||||
12
lang/strings/translations_de.properties
generated
12
lang/strings/translations_de.properties
generated
@@ -766,6 +766,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=skript
|
||||
genericScript=Allgemein
|
||||
gradleTasks=Gradle Aufgaben
|
||||
runTask=Aufgabe ausführen
|
||||
archiveName=Name des Archivs
|
||||
compress=Komprimieren
|
||||
compressContents=Inhalte komprimieren
|
||||
@@ -1870,10 +1872,11 @@ testingConnection=Verbindung testen ...
|
||||
openManagementConsole=Verwaltungskonsole öffnen
|
||||
openLxcTerminal=LXC-Terminal öffnen
|
||||
openContainerConsole=Serielle Konsole öffnen
|
||||
keeperUseMfa=2FA-Authentifikator-App verwenden
|
||||
keeperUseMfaDescription=Aktiviere dies, wenn dein Keeper-Konto ein 2FA TOTP erfordert, um auf Passwörter zuzugreifen.
|
||||
keeper2fa=2FA-Methode
|
||||
keeper2faDescription=Die primäre Zwei-Faktor-Authentifizierungsmethode, die für dein Konto konfiguriert ist. Aktiviere dies, wenn dein Keeper-Konto eine Zwei-Faktor-Authentifizierung für den Zugriff auf Passwörter erfordert.
|
||||
keeperTotpDuration=Dauer des benutzerdefinierten 2FA-Codes
|
||||
keeperTotpDurationDescription=Überschreibe die Standarddauer, wie lange ein 2FA-Code gültig ist. Gilt nur, wenn deine Unternehmensrichtlinie das Ändern der Dauer erlaubt.\n\nMögliche Werte sind: $VALUES$
|
||||
keeperOtherAuth=Andere (RSA SecurID, Duo Security, Keeper DNA, etc.)
|
||||
extractReusableIdentities=Wiederverwendbare Identitäten extrahieren
|
||||
identitiesAdded=Hinzugefügte Identitäten
|
||||
syncMode=Sync-Modus
|
||||
@@ -1940,3 +1943,8 @@ showStatus=Status anzeigen
|
||||
showAllPorts=Alle Ports anzeigen
|
||||
activeLicense=Lizenz
|
||||
activeLicenseDescription=Aktivieren eines XPipe-Lizenzschlüssels
|
||||
authenticatorApp=Authenticator-App
|
||||
securityKey=Sicherheitsschlüssel
|
||||
mcpAdditionalContext=Zusätzlicher MCP-Kontext
|
||||
mcpAdditionalContextDescription=Zusätzliche Anweisungen, die an den MCP-Client weitergegeben werden. Damit kannst du das Verhalten des Agenten steuern und zusätzlichen Kontext für dein individuelles Setup liefern.
|
||||
mcpAdditionalContextSample=- Starten Sie keine Dienste und Daemons automatisch neu, ohne dies vorher zu bestätigen\n- Wenn du eine Netzwerkschnittstelle konfigurierst, verwende immer 192.168.1.1/24 als Gateway
|
||||
|
||||
13
lang/strings/translations_en.properties
generated
13
lang/strings/translations_en.properties
generated
@@ -778,6 +778,8 @@ hub=Hub
|
||||
#context: Computer script
|
||||
script=script
|
||||
genericScript=Generic
|
||||
gradleTasks=Gradle tasks
|
||||
runTask=Run task
|
||||
archiveName=Archive name
|
||||
compress=Compress
|
||||
compressContents=Compress contents
|
||||
@@ -1905,10 +1907,12 @@ testingConnection=Testing connection ...
|
||||
openManagementConsole=Open management console
|
||||
openLxcTerminal=Open LXC terminal
|
||||
openContainerConsole=Open serial console
|
||||
keeperUseMfa=Use 2FA authenticator app
|
||||
keeperUseMfaDescription=Enable this if your Keeper account requires an 2FA TOTP to access passwords.
|
||||
keeper2fa=2FA method
|
||||
keeper2faDescription=The primary two-factor authentication method that is configured for your account. Enable this if your Keeper account requires two-factor auth to access passwords.
|
||||
keeperTotpDuration=Custom 2FA code duration
|
||||
keeperTotpDurationDescription=Override the default duration on how long a 2FA code is valid. Only applies if your organization policy allows changing the duration.\n\nPossible values are: $VALUES$
|
||||
#context: Brand and product names
|
||||
keeperOtherAuth=Other (RSA SecurID, Duo Security, Keeper DNA, etc.)
|
||||
extractReusableIdentities=Extract reusable identities
|
||||
identitiesAdded=Identities added
|
||||
syncMode=Sync mode
|
||||
@@ -1975,3 +1979,8 @@ showStatus=Show status
|
||||
showAllPorts=Show all ports
|
||||
activeLicense=License
|
||||
activeLicenseDescription=Activate an XPipe license key
|
||||
authenticatorApp=Authenticator app
|
||||
securityKey=Security key
|
||||
mcpAdditionalContext=Additional MCP context
|
||||
mcpAdditionalContextDescription=Additional instructions to pass to the MCP client. Use this to control the agent behaviour and supply additional context for your individual setup.
|
||||
mcpAdditionalContextSample=- Do not restart any services and daemons automatically without confirming first\n- When configuring a network interface, always use 192.168.1.1/24 as the gateway
|
||||
|
||||
12
lang/strings/translations_es.properties
generated
12
lang/strings/translations_es.properties
generated
@@ -742,6 +742,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=script
|
||||
genericScript=Genérico
|
||||
gradleTasks=Tareas Gradle
|
||||
runTask=Ejecutar tarea
|
||||
archiveName=Nombre de archivo
|
||||
compress=Comprime
|
||||
compressContents=Comprimir contenidos
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Probar la conexión ...
|
||||
openManagementConsole=Consola de gestión abierta
|
||||
openLxcTerminal=Abrir terminal LXC
|
||||
openContainerConsole=Abrir consola serie
|
||||
keeperUseMfa=Utilizar la aplicación de autenticación 2FA
|
||||
keeperUseMfaDescription=Actívala si tu cuenta de Keeper requiere un TOTP 2FA para acceder a las contraseñas.
|
||||
keeper2fa=método 2FA
|
||||
keeper2faDescription=El método principal de autenticación de dos factores que está configurado para tu cuenta. Actívalo si tu cuenta de Keeper requiere autenticación de dos factores para acceder a las contraseñas.
|
||||
keeperTotpDuration=Duración del código 2FA personalizado
|
||||
keeperTotpDurationDescription=Anula la duración por defecto de la validez de un código 2FA. Sólo se aplica si la política de tu organización permite cambiar la duración.\n\nLos valores posibles son: $VALUES$
|
||||
keeperOtherAuth=Otros (RSA SecurID, Duo Security, Keeper DNA, etc.)
|
||||
extractReusableIdentities=Extraer identidades reutilizables
|
||||
identitiesAdded=Identidades añadidas
|
||||
syncMode=Modo de sincronización
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Mostrar estado
|
||||
showAllPorts=Mostrar todos los puertos
|
||||
activeLicense=Licencia
|
||||
activeLicenseDescription=Activar una clave de licencia XPipe
|
||||
authenticatorApp=Aplicación Autenticador
|
||||
securityKey=Clave de seguridad
|
||||
mcpAdditionalContext=Contexto MCP adicional
|
||||
mcpAdditionalContextDescription=Instrucciones adicionales para pasar al cliente MCP. Utilízalo para controlar el comportamiento del agente y proporcionar contexto adicional para tu configuración individual.
|
||||
mcpAdditionalContextSample=- No reinicies ningún servicio o demonio automáticamente sin confirmarlo primero\n- Al configurar una interfaz de red, utiliza siempre 192.168.1.1/24 como puerta de enlace
|
||||
|
||||
12
lang/strings/translations_fr.properties
generated
12
lang/strings/translations_fr.properties
generated
@@ -761,6 +761,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=script
|
||||
genericScript=Générique
|
||||
gradleTasks=Tâches Gradle
|
||||
runTask=Exécuter une tâche
|
||||
archiveName=Nom de l'archive
|
||||
compress=Compresser
|
||||
compressContents=Compresser le contenu
|
||||
@@ -1874,10 +1876,11 @@ testingConnection=Test de connexion ...
|
||||
openManagementConsole=Console de gestion ouverte
|
||||
openLxcTerminal=Ouvrir le terminal LXC
|
||||
openContainerConsole=Ouvrir une console série
|
||||
keeperUseMfa=Utilise l'application 2FA authenticator
|
||||
keeperUseMfaDescription=Active cette option si ton compte Keeper nécessite un TOTP 2FA pour accéder aux mots de passe.
|
||||
keeper2fa=méthode 2FA
|
||||
keeper2faDescription=La principale méthode d'authentification à deux facteurs qui est configurée pour ton compte. Active cette option si ton compte Keeper nécessite une authentification à deux facteurs pour accéder aux mots de passe.
|
||||
keeperTotpDuration=Durée du code 2FA personnalisé
|
||||
keeperTotpDurationDescription=Remplacer la durée par défaut de la validité d'un code 2FA. Ne s'applique que si la politique de ton organisation permet de modifier la durée.\n\nLes valeurs possibles sont : $VALUES$
|
||||
keeperOtherAuth=Autre (RSA SecurID, Duo Security, Keeper DNA, etc.)
|
||||
extractReusableIdentities=Extraire des identités réutilisables
|
||||
identitiesAdded=Identités ajoutées
|
||||
syncMode=Mode de synchronisation
|
||||
@@ -1944,3 +1947,8 @@ showStatus=Afficher l'état
|
||||
showAllPorts=Afficher tous les ports
|
||||
activeLicense=Licence
|
||||
activeLicenseDescription=Activer une clé de licence XPipe
|
||||
authenticatorApp=Application Authenticator
|
||||
securityKey=Clé de sécurité
|
||||
mcpAdditionalContext=Contexte MCP supplémentaire
|
||||
mcpAdditionalContextDescription=Instructions supplémentaires à transmettre au client MCP. Sert à contrôler le comportement de l'agent et à fournir un contexte supplémentaire pour ta configuration individuelle.
|
||||
mcpAdditionalContextSample=- Ne redémarre pas automatiquement les services et les démons sans confirmer au préalable\n- Lors de la configuration d'une interface réseau, utilise toujours 192.168.1.1/24 comme passerelle
|
||||
|
||||
12
lang/strings/translations_id.properties
generated
12
lang/strings/translations_id.properties
generated
@@ -742,6 +742,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=skrip
|
||||
genericScript=Umum
|
||||
gradleTasks=Tugas-tugas gradle
|
||||
runTask=Menjalankan tugas
|
||||
archiveName=Nama arsip
|
||||
compress=Kompres
|
||||
compressContents=Mengompres konten
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Menguji koneksi ...
|
||||
openManagementConsole=Konsol manajemen terbuka
|
||||
openLxcTerminal=Membuka terminal LXC
|
||||
openContainerConsole=Membuka konsol serial
|
||||
keeperUseMfa=Menggunakan aplikasi pengautentikasi 2FA
|
||||
keeperUseMfaDescription=Aktifkan ini jika akun Keeper Anda memerlukan TOTP 2FA untuk mengakses kata sandi.
|
||||
keeper2fa=metode 2FA
|
||||
keeper2faDescription=Metode autentikasi dua faktor utama yang dikonfigurasikan untuk akun Anda. Aktifkan ini jika akun Keeper Anda memerlukan autentikasi dua faktor untuk mengakses kata sandi.
|
||||
keeperTotpDuration=Durasi kode 2FA khusus
|
||||
keeperTotpDurationDescription=Mengganti durasi default tentang berapa lama kode 2FA berlaku. Hanya berlaku jika kebijakan organisasi Anda mengizinkan pengubahan durasi.\n\nNilai yang mungkin adalah: $VALUES$
|
||||
keeperOtherAuth=Lainnya (RSA SecurID, Duo Security, Keeper DNA, dll.)
|
||||
extractReusableIdentities=Mengekstrak identitas yang dapat digunakan kembali
|
||||
identitiesAdded=Identitas ditambahkan
|
||||
syncMode=Mode sinkronisasi
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Menampilkan status
|
||||
showAllPorts=Menampilkan semua port
|
||||
activeLicense=Lisensi
|
||||
activeLicenseDescription=Mengaktifkan kunci lisensi XPipe
|
||||
authenticatorApp=Aplikasi autentikator
|
||||
securityKey=Kunci keamanan
|
||||
mcpAdditionalContext=Konteks MCP tambahan
|
||||
mcpAdditionalContextDescription=Instruksi tambahan untuk diteruskan ke klien MCP. Gunakan ini untuk mengontrol perilaku agen dan memberikan konteks tambahan untuk pengaturan individual Anda.
|
||||
mcpAdditionalContextSample=- Jangan memulai ulang layanan dan daemon apa pun secara otomatis tanpa mengonfirmasi terlebih dahulu\n- Saat mengonfigurasi antarmuka jaringan, selalu gunakan 192.168.1.1/24 sebagai gateway
|
||||
|
||||
12
lang/strings/translations_it.properties
generated
12
lang/strings/translations_it.properties
generated
@@ -742,6 +742,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=script
|
||||
genericScript=Generico
|
||||
gradleTasks=Attività di Gradle
|
||||
runTask=Esegui attività
|
||||
archiveName=Nome dell'archivio
|
||||
compress=Comprimere
|
||||
compressContents=Comprimere i contenuti
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Verifica della connessione ...
|
||||
openManagementConsole=Console di gestione aperta
|
||||
openLxcTerminal=Aprire il terminale LXC
|
||||
openContainerConsole=Aprire la console seriale
|
||||
keeperUseMfa=Usa l'applicazione autenticatore 2FA
|
||||
keeperUseMfaDescription=Abilita questa opzione se il tuo account Keeper richiede un 2FA TOTP per accedere alle password.
|
||||
keeper2fa=metodo 2FA
|
||||
keeper2faDescription=Il metodo principale di autenticazione a due fattori configurato per il tuo account. Abilita questa opzione se il tuo account Keeper richiede l'autenticazione a due fattori per accedere alle password.
|
||||
keeperTotpDuration=Durata del codice 2FA personalizzato
|
||||
keeperTotpDurationDescription=Annulla la durata predefinita della validità di un codice 2FA. Si applica solo se la politica dell'organizzazione consente di modificare la durata.\n\nI valori possibili sono: $VALUES$
|
||||
keeperOtherAuth=Altro (RSA SecurID, Duo Security, Keeper DNA, ecc.)
|
||||
extractReusableIdentities=Estrarre identità riutilizzabili
|
||||
identitiesAdded=Identità aggiunte
|
||||
syncMode=Modalità di sincronizzazione
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Mostra stato
|
||||
showAllPorts=Mostra tutte le porte
|
||||
activeLicense=Licenza
|
||||
activeLicenseDescription=Attivare una chiave di licenza XPipe
|
||||
authenticatorApp=App Autenticatore
|
||||
securityKey=Chiave di sicurezza
|
||||
mcpAdditionalContext=Contesto MCP aggiuntivo
|
||||
mcpAdditionalContextDescription=Istruzioni aggiuntive da passare al client MCP. Utilizzale per controllare il comportamento dell'agente e fornire un contesto aggiuntivo per la tua configurazione individuale.
|
||||
mcpAdditionalContextSample=- Non riavviare automaticamente i servizi e i demoni senza averne prima avuto conferma\n- Quando configuri un'interfaccia di rete, usa sempre 192.168.1.1/24 come gateway
|
||||
|
||||
12
lang/strings/translations_ja.properties
generated
12
lang/strings/translations_ja.properties
generated
@@ -742,6 +742,8 @@ shell=シェル
|
||||
hub=ハブ
|
||||
script=スクリプト
|
||||
genericScript=一般的な
|
||||
gradleTasks=Gradleタスク
|
||||
runTask=タスクを実行する
|
||||
archiveName=アーカイブ名
|
||||
compress=圧縮する
|
||||
compressContents=コンテンツを圧縮する
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=接続をテストする
|
||||
openManagementConsole=オープン管理コンソール
|
||||
openLxcTerminal=LXCターミナルを開く
|
||||
openContainerConsole=シリアルコンソールを開く
|
||||
keeperUseMfa=2FA認証アプリを使用する
|
||||
keeperUseMfaDescription=Keeperアカウントがパスワードにアクセスするために2FA TOTPを必要とする場合、これを有効にする。
|
||||
keeper2fa=2FA方式
|
||||
keeper2faDescription=アカウントに設定されているプライマリ2要素認証方法。Keeperアカウントがパスワードにアクセスするために2要素認証が必要な場合、これを有効にする。
|
||||
keeperTotpDuration=カスタム2FAコード期間
|
||||
keeperTotpDurationDescription=2FAコードの有効期間に関するデフォルトの期間をオーバーライドする。組織のポリシーで期間の変更が許可されている場合にのみ適用される。\n\n可能な値は以下の通り:$VALUES$
|
||||
keeperOtherAuth=その他(RSA SecurID、Duo Security、Keeper DNAなど)
|
||||
extractReusableIdentities=再利用可能なIDを抽出する
|
||||
identitiesAdded=IDが追加された
|
||||
syncMode=同期モード
|
||||
@@ -1904,3 +1907,8 @@ showStatus=ステータスを表示する
|
||||
showAllPorts=すべてのポートを表示する
|
||||
activeLicense=ライセンス
|
||||
activeLicenseDescription=XPipeライセンスキーをアクティベートする
|
||||
authenticatorApp=認証アプリ
|
||||
securityKey=セキュリティキー
|
||||
mcpAdditionalContext=追加のMCPコンテキスト
|
||||
mcpAdditionalContextDescription=MCP クライアントに渡す追加の指示。エージェントの動作を制御し、個々の設定に追加のコンテキストを提供するために使用する。
|
||||
mcpAdditionalContextSample=- 最初に確認することなく、自動的にサービスやデーモンを再起動しないこと\n- ネットワークインターフェイスを設定するときは、常に192.168.1.1/24をゲートウェイとして使用すること
|
||||
|
||||
12
lang/strings/translations_ko.properties
generated
12
lang/strings/translations_ko.properties
generated
@@ -770,6 +770,8 @@ shell=셸
|
||||
hub=Hub
|
||||
script=스크립트
|
||||
genericScript=Generic
|
||||
gradleTasks=그레이들 작업
|
||||
runTask=작업 실행
|
||||
archiveName=아카이브 이름
|
||||
compress=압축
|
||||
compressContents=콘텐츠 압축
|
||||
@@ -1887,10 +1889,11 @@ testingConnection=연결 테스트 중 ...
|
||||
openManagementConsole=관리 콘솔 열기
|
||||
openLxcTerminal=LXC 터미널 열기
|
||||
openContainerConsole=직렬 콘솔 열기
|
||||
keeperUseMfa=2FA 인증 앱 사용
|
||||
keeperUseMfaDescription=Keeper 계정에서 비밀번호에 액세스하기 위해 2FA TOTP가 필요한 경우 이 옵션을 사용 설정합니다.
|
||||
keeper2fa=2FA 방법
|
||||
keeper2faDescription=계정에 대해 구성된 기본 2단계 인증 방법입니다. Keeper 계정에서 비밀번호에 액세스하기 위해 2단계 인증이 필요한 경우 이 옵션을 사용 설정합니다.
|
||||
keeperTotpDuration=사용자 지정 2FA 코드 기간
|
||||
keeperTotpDurationDescription=2FA 코드의 유효 기간에 대한 기본 기간을 재정의합니다. 조직 정책에서 기간 변경을 허용하는 경우에만 적용됩니다.\n\n가능한 값은 다음과 같습니다: $VALUES$
|
||||
keeperOtherAuth=기타(RSA SecurID, Duo Security, Keeper DNA 등)
|
||||
extractReusableIdentities=재사용 가능한 ID 추출
|
||||
identitiesAdded=추가된 ID
|
||||
syncMode=동기화 모드
|
||||
@@ -1957,3 +1960,8 @@ showStatus=상태 표시
|
||||
showAllPorts=모든 포트 표시
|
||||
activeLicense=라이선스
|
||||
activeLicenseDescription=XPipe 라이선스 키 활성화
|
||||
authenticatorApp=인증 앱
|
||||
securityKey=보안 키
|
||||
mcpAdditionalContext=추가 MCP 컨텍스트
|
||||
mcpAdditionalContextDescription=MCP 클라이언트에 전달할 추가 지침입니다. 이를 사용하여 상담원 동작을 제어하고 개별 설정에 대한 추가 컨텍스트를 제공할 수 있습니다.
|
||||
mcpAdditionalContextSample=- 먼저 확인하지 않고 서비스 및 데몬을 자동으로 다시 시작하지 마세요\n- 네트워크 인터페이스를 구성할 때는 항상 192.168.1.1/24를 게이트웨이로 사용하세요
|
||||
|
||||
12
lang/strings/translations_nl.properties
generated
12
lang/strings/translations_nl.properties
generated
@@ -742,6 +742,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=script
|
||||
genericScript=Algemeen
|
||||
gradleTasks=Gradle taken
|
||||
runTask=Taak uitvoeren
|
||||
archiveName=Naam archief
|
||||
compress=Comprimeren
|
||||
compressContents=Inhoud comprimeren
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Verbinding testen ...
|
||||
openManagementConsole=Open beheerconsole
|
||||
openLxcTerminal=LXC-terminal openen
|
||||
openContainerConsole=Seriële console openen
|
||||
keeperUseMfa=Gebruik 2FA authenticator app
|
||||
keeperUseMfaDescription=Schakel dit in als je Keeper-account een 2FA TOTP vereist om toegang te krijgen tot wachtwoorden.
|
||||
keeper2fa=2FA methode
|
||||
keeper2faDescription=De primaire twee-factor authenticatiemethode die is geconfigureerd voor je account. Schakel dit in als je Keeper-account tweefactorauthenticatie vereist om toegang te krijgen tot wachtwoorden.
|
||||
keeperTotpDuration=Aangepaste 2FA code duur
|
||||
keeperTotpDurationDescription=Overschrijf de standaardduur van de geldigheid van een 2FA code. Alleen van toepassing als het beleid van je organisatie het wijzigen van de duur toestaat.\n\nMogelijke waarden zijn: $VALUES$
|
||||
keeperOtherAuth=Andere (RSA SecurID, Duo Security, Keeper DNA, etc.)
|
||||
extractReusableIdentities=Herbruikbare identiteiten extraheren
|
||||
identitiesAdded=Identiteiten toegevoegd
|
||||
syncMode=Synchronisatiemodus
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Status weergeven
|
||||
showAllPorts=Toon alle poorten
|
||||
activeLicense=Licentie
|
||||
activeLicenseDescription=Een XPipe-licentiesleutel activeren
|
||||
authenticatorApp=Authenticator app
|
||||
securityKey=Beveiligingssleutel
|
||||
mcpAdditionalContext=Extra MCP-context
|
||||
mcpAdditionalContextDescription=Extra instructies om door te geven aan de MCP-client. Gebruik dit om het gedrag van de agent te regelen en aanvullende context te leveren voor je individuele opstelling.
|
||||
mcpAdditionalContextSample=- Start geen services en daemons automatisch opnieuw op zonder dit eerst te bevestigen\n- Gebruik bij het configureren van een netwerkinterface altijd 192.168.1.1/24 als gateway
|
||||
|
||||
12
lang/strings/translations_pl.properties
generated
12
lang/strings/translations_pl.properties
generated
@@ -742,6 +742,8 @@ shell=Powłoka
|
||||
hub=Hub
|
||||
script=skrypt
|
||||
genericScript=Ogólny
|
||||
gradleTasks=Zadania Gradle
|
||||
runTask=Uruchom zadanie
|
||||
archiveName=Nazwa archiwum
|
||||
compress=Kompresja
|
||||
compressContents=Kompresuj zawartość
|
||||
@@ -1835,10 +1837,11 @@ testingConnection=Testowanie połączenia ...
|
||||
openManagementConsole=Otwarta konsola zarządzania
|
||||
openLxcTerminal=Otwórz terminal LXC
|
||||
openContainerConsole=Otwórz konsolę szeregową
|
||||
keeperUseMfa=Użyj aplikacji uwierzytelniającej 2FA
|
||||
keeperUseMfaDescription=Włącz tę opcję, jeśli Twoje konto Keeper wymaga TOTP 2FA, aby uzyskać dostęp do haseł.
|
||||
keeper2fa=metoda 2FA
|
||||
keeper2faDescription=Podstawowa metoda uwierzytelniania dwuskładnikowego skonfigurowana dla Twojego konta. Włącz tę opcję, jeśli Twoje konto Keeper wymaga uwierzytelniania dwuskładnikowego w celu uzyskania dostępu do haseł.
|
||||
keeperTotpDuration=Czas trwania niestandardowego kodu 2FA
|
||||
keeperTotpDurationDescription=Zastąp domyślny czas ważności kodu 2FA. Ma zastosowanie tylko wtedy, gdy zasady Twojej organizacji zezwalają na zmianę czasu trwania.\n\nMożliwe wartości to: $VALUES$
|
||||
keeperOtherAuth=Inne (RSA SecurID, Duo Security, Keeper DNA itp.)
|
||||
extractReusableIdentities=Wyodrębnij tożsamości wielokrotnego użytku
|
||||
identitiesAdded=Dodane tożsamości
|
||||
syncMode=Tryb synchronizacji
|
||||
@@ -1905,3 +1908,8 @@ showStatus=Pokaż status
|
||||
showAllPorts=Pokaż wszystkie porty
|
||||
activeLicense=Licencja
|
||||
activeLicenseDescription=Aktywuj klucz licencyjny XPipe
|
||||
authenticatorApp=Aplikacja uwierzytelniająca
|
||||
securityKey=Klucz zabezpieczeń
|
||||
mcpAdditionalContext=Dodatkowy kontekst MCP
|
||||
mcpAdditionalContextDescription=Dodatkowe instrukcje do przekazania klientowi MCP. Użyj tego, aby kontrolować zachowanie agenta i zapewnić dodatkowy kontekst dla indywidualnej konfiguracji.
|
||||
mcpAdditionalContextSample=- Nie uruchamiaj ponownie żadnych usług i demonów automatycznie bez uprzedniego potwierdzenia\n- Podczas konfigurowania interfejsu sieciowego zawsze używaj 192.168.1.1/24 jako bramy
|
||||
|
||||
12
lang/strings/translations_pt.properties
generated
12
lang/strings/translations_pt.properties
generated
@@ -742,6 +742,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=guião
|
||||
genericScript=Genérico
|
||||
gradleTasks=Tarefas Gradle
|
||||
runTask=Executa uma tarefa
|
||||
archiveName=Nome do arquivo
|
||||
compress=Comprimir
|
||||
compressContents=Comprimir conteúdos
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Testar a ligação ...
|
||||
openManagementConsole=Abre a consola de gestão
|
||||
openLxcTerminal=Abre o terminal LXC
|
||||
openContainerConsole=Abre a consola de série
|
||||
keeperUseMfa=Utiliza a aplicação de autenticação 2FA
|
||||
keeperUseMfaDescription=Ative essa opção se a sua conta do Keeper exigir um TOTP 2FA para acessar senhas.
|
||||
keeper2fa=método 2FA
|
||||
keeper2faDescription=O método primário de autenticação de dois fatores que está configurado para a tua conta. Ative isso se sua conta do Keeper exigir autenticação de dois fatores para acessar senhas.
|
||||
keeperTotpDuration=Duração do código 2FA personalizado
|
||||
keeperTotpDurationDescription=Substitui a duração predefinida de quanto tempo um código 2FA é válido. Só se aplica se a política da tua organização permitir alterar a duração.\n\nOs valores possíveis são: $VALUES$
|
||||
keeperOtherAuth=Outros (RSA SecurID, Duo Security, Keeper DNA, etc.)
|
||||
extractReusableIdentities=Extrai identidades reutilizáveis
|
||||
identitiesAdded=Identificações adicionadas
|
||||
syncMode=Modo de sincronização
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Mostra o estado
|
||||
showAllPorts=Mostra todas as portas
|
||||
activeLicense=Licença
|
||||
activeLicenseDescription=Ativar uma chave de licença XPipe
|
||||
authenticatorApp=Aplicação de autenticação
|
||||
securityKey=Chave de segurança
|
||||
mcpAdditionalContext=Contexto adicional do MCP
|
||||
mcpAdditionalContextDescription=Instruções adicionais para passar para o cliente MCP. Utiliza isto para controlar o comportamento do agente e fornecer contexto adicional para a sua configuração individual.
|
||||
mcpAdditionalContextSample=- Não reinicies automaticamente quaisquer serviços e daemons sem confirmar primeiro\n- Ao configurar uma interface de rede, utiliza sempre 192.168.1.1/24 como gateway
|
||||
|
||||
12
lang/strings/translations_ru.properties
generated
12
lang/strings/translations_ru.properties
generated
@@ -807,6 +807,8 @@ hub=Хаб
|
||||
#custom
|
||||
script=Скрипт
|
||||
genericScript=Generic
|
||||
gradleTasks=Задачи Gradle
|
||||
runTask=Выполнить задание
|
||||
archiveName=Название архива
|
||||
compress=Сжать
|
||||
compressContents=Сжать содержимое
|
||||
@@ -1946,10 +1948,11 @@ testingConnection=Тестирование соединения ...
|
||||
openManagementConsole=Открытая консоль управления
|
||||
openLxcTerminal=Откройте терминал LXC
|
||||
openContainerConsole=Открыть последовательную консоль
|
||||
keeperUseMfa=Используйте приложение аутентификатора 2FA
|
||||
keeperUseMfaDescription=Включи эту опцию, если твой аккаунт Keeper требует 2FA TOTP для доступа к паролям.
|
||||
keeper2fa=метод 2FA
|
||||
keeper2faDescription=Основной метод двухфакторной аутентификации, который настроен для твоего аккаунта. Включи этот параметр, если твой аккаунт Keeper требует двухфакторной аутентификации для доступа к паролям.
|
||||
keeperTotpDuration=Продолжительность действия пользовательского кода 2FA
|
||||
keeperTotpDurationDescription=Переопредели стандартную продолжительность действия кода 2FA. Применяется только в том случае, если политика твоей организации позволяет изменять продолжительность.\n\nВозможные значения: $VALUES$
|
||||
keeperOtherAuth=Прочее (RSA SecurID, Duo Security, Keeper DNA и так далее)
|
||||
extractReusableIdentities=Извлечение многократно используемых идентификационных данных
|
||||
identitiesAdded=Добавлены идентификаторы
|
||||
syncMode=Режим синхронизации
|
||||
@@ -2016,3 +2019,8 @@ showStatus=Показать состояние
|
||||
showAllPorts=Показать все порты
|
||||
activeLicense=Лицензия
|
||||
activeLicenseDescription=Активировать лицензионный ключ XPipe
|
||||
authenticatorApp=Приложение-аутентификатор
|
||||
securityKey=Ключ безопасности
|
||||
mcpAdditionalContext=Дополнительный контекст MCP
|
||||
mcpAdditionalContextDescription=Дополнительные инструкции, которые нужно передать MCP-клиенту. Используй это, чтобы управлять поведением агента и предоставлять дополнительный контекст для твоей индивидуальной настройки.
|
||||
mcpAdditionalContextSample=- Не перезапускай службы и демоны автоматически без предварительного подтверждения\n- При настройке сетевого интерфейса всегда используй 192.168.1.1/24 в качестве шлюза
|
||||
|
||||
12
lang/strings/translations_sv.properties
generated
12
lang/strings/translations_sv.properties
generated
@@ -742,6 +742,8 @@ shell=Skal
|
||||
hub=Navet
|
||||
script=skript
|
||||
genericScript=Generisk text
|
||||
gradleTasks=Uppgifter för Gradle
|
||||
runTask=Kör uppgift
|
||||
archiveName=Arkivets namn
|
||||
compress=Komprimera
|
||||
compressContents=Komprimera innehåll
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Testning av anslutning ...
|
||||
openManagementConsole=Öppen hanteringskonsol
|
||||
openLxcTerminal=Öppna LXC-terminalen
|
||||
openContainerConsole=Öppna seriell konsol
|
||||
keeperUseMfa=Använd 2FA-autentiseringsapp
|
||||
keeperUseMfaDescription=Aktivera detta om ditt Keeper-konto kräver en 2FA TOTP för att komma åt lösenord.
|
||||
keeper2fa=2FA-metod
|
||||
keeper2faDescription=Den primära tvåfaktorsautentiseringsmetoden som är konfigurerad för ditt konto. Aktivera detta om ditt Keeper-konto kräver tvåfaktorsautentisering för att komma åt lösenord.
|
||||
keeperTotpDuration=Anpassad 2FA-kods varaktighet
|
||||
keeperTotpDurationDescription=Åsidosätt standardtiden för hur länge en 2FA-kod är giltig. Gäller endast om organisationens policy tillåter ändring av giltighetstiden.\n\nMöjliga värden är: $VALUES$
|
||||
keeperOtherAuth=Annat (RSA SecurID, Duo Security, Keeper DNA, etc.)
|
||||
extractReusableIdentities=Extrahera återanvändbara identiteter
|
||||
identitiesAdded=Identiteter tillagda
|
||||
syncMode=Synkroniseringsläge
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Visa status
|
||||
showAllPorts=Visa alla portar
|
||||
activeLicense=Licens
|
||||
activeLicenseDescription=Aktivera en XPipe-licensnyckel
|
||||
authenticatorApp=Autentiseringsapplikation
|
||||
securityKey=Säkerhetsnyckel
|
||||
mcpAdditionalContext=Ytterligare MCP-kontext
|
||||
mcpAdditionalContextDescription=Ytterligare instruktioner att vidarebefordra till MCP-klienten. Använd detta för att styra agentens beteende och ge ytterligare sammanhang för din individuella installation.
|
||||
mcpAdditionalContextSample=- Starta inte om några tjänster och daemons automatiskt utan att först bekräfta\n- När du konfigurerar ett nätverksgränssnitt ska du alltid använda 192.168.1.1/24 som gateway
|
||||
|
||||
12
lang/strings/translations_tr.properties
generated
12
lang/strings/translations_tr.properties
generated
@@ -742,6 +742,8 @@ shell=Kabuk
|
||||
hub=Hub
|
||||
script=senaryo
|
||||
genericScript=Jenerik
|
||||
gradleTasks=Gradle görevleri
|
||||
runTask=Görevi çalıştır
|
||||
archiveName=Arşiv adı
|
||||
compress=Sıkıştır
|
||||
compressContents=İçeriği sıkıştır
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Test bağlantısı ...
|
||||
openManagementConsole=Açık yönetim konsolu
|
||||
openLxcTerminal=LXC terminalini açın
|
||||
openContainerConsole=Seri konsolu açın
|
||||
keeperUseMfa=2FA kimlik doğrulayıcı uygulamasını kullanın
|
||||
keeperUseMfaDescription=Keeper hesabınız parolalara erişmek için bir 2FA TOTP gerektiriyorsa bunu etkinleştirin.
|
||||
keeper2fa=2FA yöntemi
|
||||
keeper2faDescription=Hesabınız için yapılandırılmış olan birincil iki faktörlü kimlik doğrulama yöntemi. Keeper hesabınız parolalara erişmek için iki faktörlü kimlik doğrulama gerektiriyorsa bunu etkinleştirin.
|
||||
keeperTotpDuration=Özel 2FA kodu süresi
|
||||
keeperTotpDurationDescription=Bir 2FA kodunun ne kadar süreyle geçerli olacağına ilişkin varsayılan süreyi geçersiz kılın. Yalnızca kuruluş politikanız sürenin değiştirilmesine izin veriyorsa geçerlidir.\n\nOlası değerler şunlardır: $VALUES$
|
||||
keeperOtherAuth=Diğer (RSA SecurID, Duo Security, Keeper DNA, vb.)
|
||||
extractReusableIdentities=Yeniden kullanılabilir kimlikleri ayıklayın
|
||||
identitiesAdded=Kimlikler eklendi
|
||||
syncMode=Senkronizasyon modu
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Durumu göster
|
||||
showAllPorts=Tüm bağlantı noktalarını göster
|
||||
activeLicense=Lisans
|
||||
activeLicenseDescription=XPipe lisans anahtarını etkinleştirme
|
||||
authenticatorApp=Authenticator uygulaması
|
||||
securityKey=Güvenlik anahtarı
|
||||
mcpAdditionalContext=Ek MCP bağlamı
|
||||
mcpAdditionalContextDescription=MCP istemcisine iletilecek ek talimatlar. Aracı davranışını kontrol etmek ve bireysel kurulumunuz için ek bağlam sağlamak için bunu kullanın.
|
||||
mcpAdditionalContextSample=- Önce onaylamadan hiçbir hizmeti ve daemon'u otomatik olarak yeniden başlatmayın\n- Bir ağ arayüzünü yapılandırırken, ağ geçidi olarak her zaman 192.168.1.1/24 adresini kullanın
|
||||
|
||||
12
lang/strings/translations_vi.properties
generated
12
lang/strings/translations_vi.properties
generated
@@ -742,6 +742,8 @@ shell=Shell
|
||||
hub=Hub
|
||||
script=script
|
||||
genericScript=Chung chung
|
||||
gradleTasks=Các tác vụ Gradle
|
||||
runTask=Chạy tác vụ
|
||||
archiveName=Tên tệp lưu trữ
|
||||
compress=Nén
|
||||
compressContents=Nén nội dung
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=Kiểm tra kết nối ...
|
||||
openManagementConsole=Mở bảng điều khiển quản lý
|
||||
openLxcTerminal=Mở cửa sổ terminal LXC
|
||||
openContainerConsole=Mở giao diện điều khiển serial
|
||||
keeperUseMfa=Sử dụng ứng dụng xác thực hai yếu tố (2FA)
|
||||
keeperUseMfaDescription=Bật tùy chọn này nếu tài khoản Keeper của cậu yêu cầu xác thực hai yếu tố (2FA) bằng mã TOTP để truy cập mật khẩu.
|
||||
keeper2fa=phương pháp xác thực hai yếu tố (2FA)
|
||||
keeper2faDescription=Phương thức xác thực hai yếu tố chính được cấu hình cho tài khoản của cậu. Bật tính năng này nếu tài khoản Keeper của cậu yêu cầu xác thực hai yếu tố để truy cập mật khẩu.
|
||||
keeperTotpDuration=Thời gian hiệu lực của mã 2FA tùy chỉnh
|
||||
keeperTotpDurationDescription=Thay đổi thời gian mặc định mà mã xác thực hai yếu tố (2FA) có hiệu lực. Tính năng này chỉ áp dụng nếu chính sách của tổ chức cho phép thay đổi thời gian.\n\nCác giá trị có thể là: $VALUES$
|
||||
keeperOtherAuth=Khác (RSA SecurID, Duo Security, Keeper DNA, v.v.)
|
||||
extractReusableIdentities=Trích xuất các danh tính có thể tái sử dụng
|
||||
identitiesAdded=Thêm danh tính
|
||||
syncMode=Chế độ đồng bộ hóa
|
||||
@@ -1904,3 +1907,8 @@ showStatus=Hiển thị trạng thái
|
||||
showAllPorts=Hiển thị tất cả các cổng
|
||||
activeLicense=Giấy phép
|
||||
activeLicenseDescription=Kích hoạt khóa cấp phép XPipe
|
||||
authenticatorApp=Ứng dụng xác thực
|
||||
securityKey=Khóa bảo mật
|
||||
mcpAdditionalContext=Bối cảnh bổ sung của MCP
|
||||
mcpAdditionalContextDescription=Hướng dẫn bổ sung để truyền cho khách hàng MCP. Sử dụng điều này để kiểm soát hành vi của đại lý và cung cấp bối cảnh bổ sung cho cấu hình cá nhân của cậu.
|
||||
mcpAdditionalContextSample=- Không tự động khởi động lại bất kỳ dịch vụ hoặc daemon nào mà không xác nhận trước\n- Khi cấu hình giao diện mạng, luôn sử dụng 192.168.1.1/24 làm cổng mặc định
|
||||
|
||||
12
lang/strings/translations_zh-Hans.properties
generated
12
lang/strings/translations_zh-Hans.properties
generated
@@ -1049,6 +1049,8 @@ shell=Shell
|
||||
hub=连接中心
|
||||
script=脚本
|
||||
genericScript=通用
|
||||
gradleTasks=Gradle 任务
|
||||
runTask=运行任务
|
||||
#custom
|
||||
archiveName=压缩包名称
|
||||
compress=压缩
|
||||
@@ -2459,10 +2461,11 @@ testingConnection=测试连接...
|
||||
openManagementConsole=开放式管理控制台
|
||||
openLxcTerminal=打开 LXC 终端
|
||||
openContainerConsole=打开串行控制台
|
||||
keeperUseMfa=使用 2FA 验证器应用程序
|
||||
keeperUseMfaDescription=如果 Keeper 帐户要求使用 2FA TOTP 访问密码,请启用此选项。
|
||||
keeper2fa=2FA 方法
|
||||
keeper2faDescription=为您的账户配置的主要双因素身份验证方法。如果您的 Keeper 帐户需要双因素身份验证才能访问密码,请启用此选项。
|
||||
keeperTotpDuration=自定义 2FA 代码持续时间
|
||||
keeperTotpDurationDescription=覆盖 2FA 验证码有效期的默认持续时间。仅适用于组织政策允许更改持续时间的情况。\n\n可能的值有$VALUES$
|
||||
keeperOtherAuth=其他(RSA SecurID、Duo Security、Keeper DNA 等)
|
||||
extractReusableIdentities=提取可重复使用的身份
|
||||
identitiesAdded=添加的身份信息
|
||||
syncMode=同步模式
|
||||
@@ -2529,3 +2532,8 @@ showStatus=显示状态
|
||||
showAllPorts=显示所有端口
|
||||
activeLicense=许可证
|
||||
activeLicenseDescription=激活 XPipe 许可证密钥
|
||||
authenticatorApp=验证器应用程序
|
||||
securityKey=安全密钥
|
||||
mcpAdditionalContext=其他 MCP 上下文
|
||||
mcpAdditionalContextDescription=传递给 MCP 客户端的附加指令。用它来控制代理行为,并为您的个性化设置提供额外的上下文。
|
||||
mcpAdditionalContextSample=- 未经确认,请勿自动重启任何服务和守护进程\n- 配置网络接口时,始终使用 192.168.1.1/24 作为网关
|
||||
|
||||
12
lang/strings/translations_zh-Hant.properties
generated
12
lang/strings/translations_zh-Hant.properties
generated
@@ -742,6 +742,8 @@ shell=殼
|
||||
hub=樞紐
|
||||
script=腳本
|
||||
genericScript=通用
|
||||
gradleTasks=Gradle 任務
|
||||
runTask=執行任務
|
||||
archiveName=存檔名稱
|
||||
compress=壓縮
|
||||
compressContents=壓縮內容
|
||||
@@ -1834,10 +1836,11 @@ testingConnection=測試連接 ...
|
||||
openManagementConsole=開放式管理主控台
|
||||
openLxcTerminal=開啟 LXC 終端機
|
||||
openContainerConsole=開啟序列控制台
|
||||
keeperUseMfa=使用 2FA 認證器應用程式
|
||||
keeperUseMfaDescription=如果您的 Keeper 帳戶需要 2FA TOTP 才能存取密碼,請啟用此項。
|
||||
keeper2fa=2FA 方法
|
||||
keeper2faDescription=為您帳戶設定的主要雙因素驗證方法。如果您的 Keeper 帳戶需要雙因素認證才能存取密碼,請啟用此項。
|
||||
keeperTotpDuration=自訂 2FA 碼持續時間
|
||||
keeperTotpDurationDescription=覆寫 2FA 密碼有效期的預設持續時間。僅適用於組織政策允許變更持續時間的情況。\n\n可能的值有$VALUES$
|
||||
keeperOtherAuth=其他 (RSA SecurID、Duo Security、Keeper DNA 等)
|
||||
extractReusableIdentities=擷取可重複使用的身分
|
||||
identitiesAdded=新增的身分
|
||||
syncMode=同步模式
|
||||
@@ -1904,3 +1907,8 @@ showStatus=顯示狀態
|
||||
showAllPorts=顯示所有連接埠
|
||||
activeLicense=許可證
|
||||
activeLicenseDescription=啟動 XPipe 授權金鑰
|
||||
authenticatorApp=驗證器應用程式
|
||||
securityKey=安全金鑰
|
||||
mcpAdditionalContext=其他 MCP 上下文
|
||||
mcpAdditionalContextDescription=傳送給 MCP 用戶端的附加指示。使用此項可控制代理程式的行為,並為您的個別設定提供額外的上下文。
|
||||
mcpAdditionalContextSample=- 在未確認之前,請勿自動重新啟動任何服務和 daemons\n- 設定網路介面時,請務必使用 192.168.1.1/24 作為閘道
|
||||
|
||||
Reference in New Issue
Block a user