Merge remote-tracking branch 'origin/21.x-release'

This commit is contained in:
Christopher Schnick
2026-02-18 19:07:17 +01:00
74 changed files with 1159 additions and 244 deletions

View File

@@ -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));
}

View File

@@ -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>();

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -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()));

View File

@@ -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"),

View File

@@ -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());

View File

@@ -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());

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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()) {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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();
}
});

View File

@@ -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();

View File

@@ -92,7 +92,7 @@ public class BitwardenPasswordManager implements PasswordManager {
.localScript(script)
.logIfEnabled(false)
.preferTabs(false)
.alwaysKeepOpen(true)
.pauseOnExit(true)
.launch();
return null;
}

View File

@@ -48,7 +48,7 @@ public class DashlanePasswordManager implements PasswordManager {
.title("Dashlane login")
.localScript(script)
.logIfEnabled(false)
.alwaysKeepOpen(true)
.pauseOnExit(true)
.launch();
return null;
}

View File

@@ -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();
}
}

View File

@@ -54,7 +54,7 @@ public class LastpassPasswordManager implements PasswordManager {
.title("LastPass login")
.localScript(script)
.logIfEnabled(false)
.alwaysKeepOpen(true)
.pauseOnExit(true)
.launch();
}
return null;

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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),

View File

@@ -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),

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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());
}
}
}

View File

@@ -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(); }
}
}

View File

@@ -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

View File

@@ -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()),

View File

@@ -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(() -> {

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -128,6 +128,7 @@ open module io.xpipe.app {
uses CloudSetupProvider;
provides ActionProvider with
GradleRunMenuProvider,
RefreshHubLeafProvider,
SetupToolActionProvider,
XPipeUrlProvider,

View File

@@ -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 {

View File

@@ -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
View File

@@ -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'
}

View File

@@ -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
View 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:
![](https://xpipe.io/assets/images/BlogPage/mcp-instructions.png)
There is now also the option to pass additional context to any agent to apply consistent rules and information:
![](https://xpipe.io/assets/images/BlogPage/mcp-context.png)
The MCP integration can also now be used to manage cisco switches via the new integration:
![](https://xpipe.io/assets/images/BlogPage/mcp-switch.png)
## 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

View File

@@ -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())

View File

@@ -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) {

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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()))

View File

@@ -18,7 +18,7 @@ configurations {
javafx
}
if (hasProperty("customJavaFxLibsPath")) {
if (customJavaFxLibsPath != null) {
repositories {
flatDir {
dirs customJavaFxLibsPath

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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をゲートウェイとして使用すること

View File

@@ -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를 게이트웨이로 사용하세요

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 в качестве шлюза

View File

@@ -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

View File

@@ -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=ı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

View File

@@ -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

View File

@@ -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 作为网关

View File

@@ -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 作為閘道

View File

@@ -1 +1 @@
21.2.1
21.3-10