Compare commits

...

14 Commits

Author SHA1 Message Date
陈大猫
ff6fa55829 Merge pull request #13 from Weihong-Liu/feature/login-shell-path
Some checks failed
build-packages / build-macos-latest (push) Has been cancelled
build-packages / build-windows-latest (push) Has been cancelled
build-packages / release (push) Has been cancelled
2026-01-04 22:54:27 +08:00
Puppet
a9fabf6677 Use login shell for local terminals 2026-01-04 22:34:35 +08:00
LAPTOP-O016UC3M\Qi Chen
aa42468ccd Improve code formatting and consistency
Updates whitespace around type definitions for better readability
and maintains consistent formatting throughout the component.
No functional changes introduced.
2026-01-04 19:16:54 +08:00
LAPTOP-O016UC3M\Qi Chen
242c420961 Adds terminal scroll behaviors and improves SFTP modal clarity
Introduces additional scroll options to terminal settings, enabling more granular control over scroll triggers such as output, key press, and paste. Clarifies SFTP upload task error handling and enhances host picker type safety for better maintainability and reliability.
2026-01-04 19:16:41 +08:00
LAPTOP-O016UC3M\Qi Chen
abdac05db6 Fixes indentation for cloud provider props
Improves readability and consistency by correcting indentation
of cloud sync provider prop assignments in the dashboard
component. No functional changes are introduced.
2026-01-04 19:13:32 +08:00
LAPTOP-O016UC3M\Qi Chen
84809b37a7 Removes SMB cloud sync provider support
Drops all SMB provider types, UI, and logic to simplify codebase and focus on maintained sync backends. Reduces complexity and maintenance burden by eliminating unused integration.
2026-01-04 19:13:00 +08:00
陈大猫
2cdd83d6f1 Merge pull request #11 from binaricat/copilot/add-smb-protocol-support
Add SMB protocol support for cloud sync
2026-01-04 17:43:26 +08:00
copilot-swe-agent[bot]
0ecb51ea17 Add port validation for SMB configuration
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>
2026-01-04 09:39:41 +00:00
copilot-swe-agent[bot]
326e613e82 Fix code review issues: implement CloudAdapter interface and add SMB bridge methods
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>
2026-01-04 09:38:01 +00:00
copilot-swe-agent[bot]
71aaeba17b Add SMB protocol support for cloud sync
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>
2026-01-04 09:35:57 +00:00
copilot-swe-agent[bot]
9d2e19a034 Initial plan 2026-01-04 09:22:13 +00:00
陈大猫
fcdf5bce32 Merge pull request #10 from binaricat/copilot/optimize-port-forward-deletion
Show confirmation dialog and stop tunnel before deleting active port forwarding rules
2026-01-04 17:09:16 +08:00
copilot-swe-agent[bot]
ea655d95a3 feat: show confirmation dialog and stop tunnel before deleting active port forwarding rules
When deleting a port forwarding rule that is currently active (status === "active" or "connecting"), a confirmation dialog is shown asking if the user wants to stop and delete the rule. If confirmed, the tunnel is stopped first before deleting the rule, ensuring consistency between configuration and actual port forwarding state.

Added i18n translations for both English and Chinese languages.

Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>
2026-01-04 09:04:45 +00:00
copilot-swe-agent[bot]
be5110f306 Initial plan 2026-01-04 08:50:41 +00:00
12 changed files with 222 additions and 77 deletions

View File

@@ -356,6 +356,9 @@ const en: Messages = {
'pf.view.list': 'List',
'pf.rule.summary.dynamic': 'SOCKS on {bindAddress}:{localPort}',
'pf.rule.summary.default': '{bindAddress}:{localPort} -> {remoteHost}:{remotePort}',
'pf.deleteActive.title': 'Delete Active Port Forwarding?',
'pf.deleteActive.desc': 'This port forwarding rule "{label}" is currently active. Deleting it will stop the tunnel first.',
'pf.deleteActive.confirm': 'Stop and Delete',
// SFTP
'sftp.newFolder': 'New Folder',
@@ -690,6 +693,20 @@ const en: Messages = {
'cloudSync.s3.forcePathStyle': 'Force path-style URLs (for MinIO/R2, etc.)',
'cloudSync.s3.showSecret': 'Show secrets',
'cloudSync.s3.validation.required': 'Endpoint, region, bucket, access key, and secret are required.',
'cloudSync.smb.title': 'SMB Settings',
'cloudSync.smb.desc': 'Connect to an SMB/CIFS file share for encrypted sync.',
'cloudSync.smb.share': 'Share Path',
'cloudSync.smb.username': 'Username',
'cloudSync.smb.password': 'Password',
'cloudSync.smb.domain': 'Domain (optional)',
'cloudSync.smb.domainPlaceholder': 'e.g., WORKGROUP',
'cloudSync.smb.port': 'Port (optional)',
'cloudSync.smb.showSecret': 'Show password',
'cloudSync.smb.validation.share': 'Share path is required.',
'cloudSync.smb.validation.port': 'Port must be a number between 1 and 65535.',
'cloudSync.connect.smb.success': 'SMB connected successfully',
'cloudSync.connect.smb.failedTitle': 'SMB connection failed',
'cloudSync.provider.smb': 'SMB Share',
'cloudSync.connect.webdav.success': 'WebDAV connected successfully',
'cloudSync.connect.webdav.failedTitle': 'WebDAV connection failed',
'cloudSync.connect.s3.success': 'S3 connected successfully',

View File

@@ -530,6 +530,20 @@ const zhCN: Messages = {
'cloudSync.s3.forcePathStyle': '强制使用 path-style URL适用于 MinIO/R2 等)',
'cloudSync.s3.showSecret': '显示密钥',
'cloudSync.s3.validation.required': '端点、Region、Bucket、Access Key 与 Secret 必填。',
'cloudSync.smb.title': 'SMB 设置',
'cloudSync.smb.desc': '连接到 SMB/CIFS 文件共享以进行加密同步。',
'cloudSync.smb.share': '共享路径',
'cloudSync.smb.username': '用户名',
'cloudSync.smb.password': '密码',
'cloudSync.smb.domain': '域(可选)',
'cloudSync.smb.domainPlaceholder': '例如WORKGROUP',
'cloudSync.smb.port': '端口(可选)',
'cloudSync.smb.showSecret': '显示密码',
'cloudSync.smb.validation.share': '共享路径必填。',
'cloudSync.smb.validation.port': '端口必须是 1 到 65535 之间的数字。',
'cloudSync.connect.smb.success': 'SMB 已连接',
'cloudSync.connect.smb.failedTitle': 'SMB 连接失败',
'cloudSync.provider.smb': 'SMB 共享',
'cloudSync.connect.webdav.success': 'WebDAV 已连接',
'cloudSync.connect.webdav.failedTitle': 'WebDAV 连接失败',
'cloudSync.connect.s3.success': 'S3 已连接',
@@ -649,6 +663,9 @@ const zhCN: Messages = {
'pf.view.list': '列表',
'pf.rule.summary.dynamic': 'SOCKS 监听于 {bindAddress}:{localPort}',
'pf.rule.summary.default': '{bindAddress}:{localPort} -> {remoteHost}:{remotePort}',
'pf.deleteActive.title': '删除正在运行的端口转发?',
'pf.deleteActive.desc': '端口转发规则 "{label}" 当前正在运行。删除前将先关闭转发连接。',
'pf.deleteActive.confirm': '关闭并删除',
// SFTP (pane + conflict)
'sftp.pane.local': '本地',

View File

@@ -148,7 +148,7 @@ export const useCloudSync = (): CloudSyncHook => {
useEffect(() => {
// Compute a simple hash of the master key config to detect changes
const currentHash = state.masterKeyConfig
? JSON.stringify({ salt: state.masterKeyConfig.salt, iv: state.masterKeyConfig.iv })
? JSON.stringify({ salt: state.masterKeyConfig.salt, kdf: state.masterKeyConfig.kdf })
: null;
// If master key config changed (e.g., set up in settings window), reset the attempt flag

View File

@@ -1045,13 +1045,13 @@ export const SyncDashboard: React.FC<SyncDashboardProps> = ({
provider="github"
name="GitHub Gist"
icon={<Github size={24} />}
isConnected={sync.providers.github.status === 'connected' || sync.providers.github.status === 'syncing'}
isSyncing={sync.providers.github.status === 'syncing'}
isConnecting={sync.providers.github.status === 'connecting'}
account={sync.providers.github.account}
lastSync={sync.providers.github.lastSync}
error={sync.providers.github.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.github.status !== 'connected' && sync.providers.github.status !== 'syncing'}
isConnected={sync.providers.github.status === 'connected' || sync.providers.github.status === 'syncing'}
isSyncing={sync.providers.github.status === 'syncing'}
isConnecting={sync.providers.github.status === 'connecting'}
account={sync.providers.github.account}
lastSync={sync.providers.github.lastSync}
error={sync.providers.github.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.github.status !== 'connected' && sync.providers.github.status !== 'syncing'}
onConnect={handleConnectGitHub}
onDisconnect={() => sync.disconnectProvider('github')}
onSync={() => handleSync('github')}
@@ -1061,13 +1061,13 @@ export const SyncDashboard: React.FC<SyncDashboardProps> = ({
provider="google"
name="Google Drive"
icon={<GoogleDriveIcon className="w-6 h-6" />}
isConnected={sync.providers.google.status === 'connected' || sync.providers.google.status === 'syncing'}
isSyncing={sync.providers.google.status === 'syncing'}
isConnecting={sync.providers.google.status === 'connecting'}
account={sync.providers.google.account}
lastSync={sync.providers.google.lastSync}
error={sync.providers.google.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.google.status !== 'connected' && sync.providers.google.status !== 'syncing'}
isConnected={sync.providers.google.status === 'connected' || sync.providers.google.status === 'syncing'}
isSyncing={sync.providers.google.status === 'syncing'}
isConnecting={sync.providers.google.status === 'connecting'}
account={sync.providers.google.account}
lastSync={sync.providers.google.lastSync}
error={sync.providers.google.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.google.status !== 'connected' && sync.providers.google.status !== 'syncing'}
onConnect={handleConnectGoogle}
onDisconnect={() => sync.disconnectProvider('google')}
onSync={() => handleSync('google')}
@@ -1077,13 +1077,13 @@ export const SyncDashboard: React.FC<SyncDashboardProps> = ({
provider="onedrive"
name="Microsoft OneDrive"
icon={<OneDriveIcon className="w-6 h-6" />}
isConnected={sync.providers.onedrive.status === 'connected' || sync.providers.onedrive.status === 'syncing'}
isSyncing={sync.providers.onedrive.status === 'syncing'}
isConnecting={sync.providers.onedrive.status === 'connecting'}
account={sync.providers.onedrive.account}
lastSync={sync.providers.onedrive.lastSync}
error={sync.providers.onedrive.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.onedrive.status !== 'connected' && sync.providers.onedrive.status !== 'syncing'}
isConnected={sync.providers.onedrive.status === 'connected' || sync.providers.onedrive.status === 'syncing'}
isSyncing={sync.providers.onedrive.status === 'syncing'}
isConnecting={sync.providers.onedrive.status === 'connecting'}
account={sync.providers.onedrive.account}
lastSync={sync.providers.onedrive.lastSync}
error={sync.providers.onedrive.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.onedrive.status !== 'connected' && sync.providers.onedrive.status !== 'syncing'}
onConnect={handleConnectOneDrive}
onDisconnect={() => sync.disconnectProvider('onedrive')}
onSync={() => handleSync('onedrive')}
@@ -1093,13 +1093,13 @@ export const SyncDashboard: React.FC<SyncDashboardProps> = ({
provider="webdav"
name={t('cloudSync.provider.webdav')}
icon={<Server size={24} />}
isConnected={sync.providers.webdav.status === 'connected' || sync.providers.webdav.status === 'syncing'}
isSyncing={sync.providers.webdav.status === 'syncing'}
isConnecting={sync.providers.webdav.status === 'connecting'}
account={sync.providers.webdav.account}
lastSync={sync.providers.webdav.lastSync}
error={sync.providers.webdav.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.webdav.status !== 'connected' && sync.providers.webdav.status !== 'syncing'}
isConnected={sync.providers.webdav.status === 'connected' || sync.providers.webdav.status === 'syncing'}
isSyncing={sync.providers.webdav.status === 'syncing'}
isConnecting={sync.providers.webdav.status === 'connecting'}
account={sync.providers.webdav.account}
lastSync={sync.providers.webdav.lastSync}
error={sync.providers.webdav.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.webdav.status !== 'connected' && sync.providers.webdav.status !== 'syncing'}
onEdit={openWebdavDialog}
onConnect={openWebdavDialog}
onDisconnect={() => sync.disconnectProvider('webdav')}
@@ -1110,13 +1110,13 @@ export const SyncDashboard: React.FC<SyncDashboardProps> = ({
provider="s3"
name={t('cloudSync.provider.s3')}
icon={<Database size={24} />}
isConnected={sync.providers.s3.status === 'connected' || sync.providers.s3.status === 'syncing'}
isSyncing={sync.providers.s3.status === 'syncing'}
isConnecting={sync.providers.s3.status === 'connecting'}
account={sync.providers.s3.account}
lastSync={sync.providers.s3.lastSync}
error={sync.providers.s3.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.s3.status !== 'connected' && sync.providers.s3.status !== 'syncing'}
isConnected={sync.providers.s3.status === 'connected' || sync.providers.s3.status === 'syncing'}
isSyncing={sync.providers.s3.status === 'syncing'}
isConnecting={sync.providers.s3.status === 'connecting'}
account={sync.providers.s3.account}
lastSync={sync.providers.s3.lastSync}
error={sync.providers.s3.error}
disabled={sync.hasAnyConnectedProvider && sync.providers.s3.status !== 'connected' && sync.providers.s3.status !== 'syncing'}
onEdit={openS3Dialog}
onConnect={openS3Dialog}
onDisconnect={() => sync.disconnectProvider('s3')}
@@ -1714,7 +1714,7 @@ interface CloudSyncSettingsProps {
export const CloudSyncSettings: React.FC<CloudSyncSettingsProps> = (props) => {
const { securityState } = useCloudSync();
// Simplified UX: once a master key is configured, we auto-unlock via safeStorage
// so users don't have to manage a separate LOCKED screen.
if (securityState === 'NO_KEY') {

View File

@@ -1,4 +1,5 @@
import {
AlertTriangle,
Check,
ChevronDown,
Globe,
@@ -26,6 +27,14 @@ import {
AsidePanelFooter,
} from "./ui/aside-panel";
import { Button } from "./ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "./ui/dialog";
import { Dropdown, DropdownContent, DropdownTrigger } from "./ui/dropdown";
import { Input } from "./ui/input";
import { SortDropdown } from "./ui/sort-dropdown";
@@ -207,6 +216,11 @@ const PortForwarding: React.FC<PortForwardingProps> = ({
// New forwarding menu
const [showNewMenu, setShowNewMenu] = useState(false);
// Delete confirmation dialog state for active tunnels
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [ruleToDelete, setRuleToDelete] = useState<PortForwardingRule | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
// Reset wizard
const resetWizard = () => {
setWizardStep("type");
@@ -358,12 +372,50 @@ const PortForwarding: React.FC<PortForwardingProps> = ({
};
// Close edit panel
const closeEditPanel = () => {
const closeEditPanel = useCallback(() => {
setShowEditPanel(false);
setEditingRule(null);
setEditDraft({});
setSelectedRuleId(null);
};
}, [setSelectedRuleId]);
// Handle delete with confirmation for active tunnels
const handleDeleteRule = useCallback(
(rule: PortForwardingRule) => {
// If tunnel is active or connecting, show confirmation dialog
if (rule.status === "active" || rule.status === "connecting") {
setRuleToDelete(rule);
setShowDeleteConfirm(true);
} else {
// If inactive, delete directly
if (editingRule?.id === rule.id) {
closeEditPanel();
}
deleteRule(rule.id);
}
},
[editingRule, deleteRule, closeEditPanel],
);
// Confirm delete of active tunnel: stop first, then delete
const confirmDeleteActiveRule = useCallback(async () => {
if (!ruleToDelete) return;
setIsDeleting(true);
try {
// Stop the tunnel first
await stopTunnel(ruleToDelete.id);
// Then delete the rule
if (editingRule?.id === ruleToDelete.id) {
closeEditPanel();
}
deleteRule(ruleToDelete.id);
} finally {
setIsDeleting(false);
setShowDeleteConfirm(false);
setRuleToDelete(null);
}
}, [ruleToDelete, stopTunnel, deleteRule, editingRule, closeEditPanel]);
// Handle wizard navigation
// Flow for local: type -> local-config -> destination -> host-selection
@@ -654,12 +706,7 @@ const PortForwarding: React.FC<PortForwardingProps> = ({
}}
onEdit={() => startEditRule(rule)}
onDuplicate={() => duplicateRule(rule.id)}
onDelete={() => {
if (editingRule?.id === rule.id) {
closeEditPanel();
}
deleteRule(rule.id);
}}
onDelete={() => handleDeleteRule(rule)}
onStart={() => handleStartTunnel(rule)}
onStop={() => handleStopTunnel(rule)}
/>
@@ -685,10 +732,7 @@ const PortForwarding: React.FC<PortForwardingProps> = ({
duplicateRule(editingRule.id);
closeEditPanel();
}}
onDelete={() => {
deleteRule(editingRule.id);
closeEditPanel();
}}
onDelete={() => handleDeleteRule(editingRule)}
onOpenHostSelector={() => setShowHostSelector(true)}
/>
)}
@@ -819,6 +863,45 @@ const PortForwarding: React.FC<PortForwardingProps> = ({
isValid={isNewFormValid()}
/>
)}
{/* Delete Active Tunnel Confirmation Dialog */}
<Dialog open={showDeleteConfirm} onOpenChange={(open) => {
if (!isDeleting) {
setShowDeleteConfirm(open);
if (!open) setRuleToDelete(null);
}
}}>
<DialogContent className="sm:max-w-[400px]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle size={20} />
{t("pf.deleteActive.title")}
</DialogTitle>
<DialogDescription>
{t("pf.deleteActive.desc", { label: ruleToDelete?.label ?? "" })}
</DialogDescription>
</DialogHeader>
<DialogFooter className="gap-2 sm:gap-0">
<Button
variant="outline"
onClick={() => {
setShowDeleteConfirm(false);
setRuleToDelete(null);
}}
disabled={isDeleting}
>
{t("common.cancel")}
</Button>
<Button
variant="destructive"
onClick={confirmDeleteActiveRule}
disabled={isDeleting}
>
{t("pf.deleteActive.confirm")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
};

View File

@@ -743,14 +743,14 @@ const SFTPModal: React.FC<SFTPModalProps> = ({
return true;
} catch (e) {
setUploadTasks((prev) =>
prev.map((t) =>
t.id === taskId
prev.map((task) =>
task.id === taskId
? {
...t,
...task,
status: "failed" as const,
error: e instanceof Error ? e.message : t("sftp.error.uploadFailed"),
}
: t,
: task,
),
);
return false;

View File

@@ -44,10 +44,13 @@ export const SftpHostPicker: React.FC<SftpHostPickerProps> = ({
).sort((a, b) => a.label.localeCompare(b.label));
}, [hosts, hostSearch]);
const sideLabel = side === 'left' ? t('common.left') : t('common.right');
const items = useMemo(() => {
return [{ type: 'local' as const, id: 'local' }].concat(
filteredHosts.map((host) => ({ type: 'host' as const, id: host.id, host }))
);
type PickerItem = { type: 'local'; id: string } | { type: 'host'; id: string; host: Host };
const items = useMemo<PickerItem[]>(() => {
const localItem: PickerItem = { type: 'local', id: 'local' };
const hostItems: PickerItem[] = filteredHosts.map((host) => ({ type: 'host', id: host.id, host }));
return [localItem, ...hostItems];
}, [filteredHosts]);
useEffect(() => {
@@ -62,7 +65,7 @@ export const SftpHostPicker: React.FC<SftpHostPickerProps> = ({
setSelectedIndex(0);
}, [hostSearch, open]);
const handleSelect = (item: typeof items[number]) => {
const handleSelect = (item: PickerItem) => {
if (item.type === 'local') {
onSelectLocal();
} else {

View File

@@ -345,6 +345,9 @@ export interface TerminalSettings {
// Keyboard
altAsMeta: boolean; // Use ⌥ as the Meta key
scrollOnInput: boolean; // Scroll terminal to bottom on input
scrollOnOutput: boolean; // Scroll terminal to bottom on output
scrollOnKeyPress: boolean; // Scroll terminal to bottom on key press
scrollOnPaste: boolean; // Scroll terminal to bottom on paste
// Mouse
rightClickBehavior: RightClickBehavior;
@@ -381,6 +384,9 @@ export const DEFAULT_TERMINAL_SETTINGS: TerminalSettings = {
minimumContrastRatio: 1,
altAsMeta: false,
scrollOnInput: true,
scrollOnOutput: false,
scrollOnKeyPress: false,
scrollOnPaste: true,
rightClickBehavior: 'context-menu',
copyOnSelect: false,
middleClickPaste: true,

View File

@@ -373,6 +373,7 @@ export const SYNC_STORAGE_KEYS = {
PROVIDER_ONEDRIVE: 'netcatty_provider_onedrive_v1',
PROVIDER_WEBDAV: 'netcatty_provider_webdav_v1',
PROVIDER_S3: 'netcatty_provider_s3_v1',
PROVIDER_SMB: 'netcatty_provider_smb_v1',
LOCAL_SYNC_META: 'netcatty_local_sync_meta_v1',
} as const;

View File

@@ -6,6 +6,7 @@
const os = require("node:os");
const fs = require("node:fs");
const net = require("node:net");
const path = require("node:path");
const pty = require("node-pty");
// Shared references
@@ -13,6 +14,13 @@ let sessions = null;
let electronModule = null;
const DEFAULT_UTF8_LOCALE = "en_US.UTF-8";
const LOGIN_SHELLS = new Set(["bash", "zsh", "fish", "ksh"]);
const getLoginShellArgs = (shellPath) => {
if (!shellPath || process.platform === "win32") return [];
const shellName = path.basename(shellPath);
return LOGIN_SHELLS.has(shellName) ? ["-l"] : [];
};
/**
* Initialize the terminal bridge with dependencies
@@ -91,6 +99,7 @@ function startLocalSession(event, payload) {
? findExecutable("powershell") || "powershell.exe"
: process.env.SHELL || "/bin/bash";
const shell = payload?.shell || defaultShell;
const shellArgs = getLoginShellArgs(shell);
const env = applyLocaleDefaults({
...process.env,
...(payload?.env || {}),
@@ -98,7 +107,7 @@ function startLocalSession(event, payload) {
COLORTERM: "truecolor",
});
const proc = pty.spawn(shell, [], {
const proc = pty.spawn(shell, shellArgs, {
cols: payload?.cols || 80,
rows: payload?.rows || 24,
env,

10
global.d.ts vendored
View File

@@ -1,5 +1,5 @@
import type { RemoteFile } from "./types";
import type { S3Config, SyncedFile, WebDAVConfig } from "./domain/sync";
import type { S3Config, SMBConfig, SyncedFile, WebDAVConfig } from "./domain/sync";
declare global {
// Proxy configuration for SSH connections
@@ -261,6 +261,14 @@ interface NetcattyBridge {
): Promise<{ resourceId: string }>;
cloudSyncS3Download?(config: S3Config): Promise<{ syncedFile: SyncedFile | null }>;
cloudSyncS3Delete?(config: S3Config): Promise<{ ok: true }>;
cloudSyncSmbInitialize?(config: SMBConfig): Promise<{ resourceId: string | null }>;
cloudSyncSmbUpload?(
config: SMBConfig,
syncedFile: SyncedFile
): Promise<{ resourceId: string }>;
cloudSyncSmbDownload?(config: SMBConfig): Promise<{ syncedFile: SyncedFile | null }>;
cloudSyncSmbDelete?(config: SMBConfig): Promise<{ ok: true }>;
// Port Forwarding
startPortForward?(options: PortForwardOptions): Promise<PortForwardResult>;

35
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "netcatty",
"version": "0.0.0",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/client-s3": "^3.956.0",
"@fontsource/jetbrains-mono": "^5.2.8",
@@ -986,7 +987,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -2063,6 +2063,7 @@
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"dependencies": {
"cross-dirname": "^0.1.0",
"debug": "^4.3.4",
@@ -2084,6 +2085,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -2100,6 +2102,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"universalify": "^2.0.0"
},
@@ -2114,6 +2117,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 10.0.0"
}
@@ -5702,7 +5706,6 @@
"integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.49.0",
@@ -5732,7 +5735,6 @@
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.49.0",
"@typescript-eslint/types": "8.49.0",
@@ -6011,8 +6013,7 @@
"version": "5.5.0",
"resolved": "https://registry.npmmirror.com/@xterm/xterm/-/xterm-5.5.0.tgz",
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/7zip-bin": {
"version": "5.2.0",
@@ -6027,7 +6028,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -6087,7 +6087,6 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -6487,7 +6486,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -7156,7 +7154,8 @@
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
"dev": true,
"license": "MIT",
"optional": true
"optional": true,
"peer": true
},
"node_modules/cross-env": {
"version": "10.1.0",
@@ -7397,7 +7396,6 @@
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"app-builder-lib": "26.0.12",
"builder-util": "26.0.11",
@@ -7715,6 +7713,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/asar": "^3.2.1",
"debug": "^4.1.1",
@@ -7735,6 +7734,7 @@
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
@@ -7959,7 +7959,6 @@
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -10425,7 +10424,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -10484,6 +10482,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"commander": "^9.4.0"
},
@@ -10501,6 +10500,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": "^12.20.0 || >=14"
}
@@ -10598,7 +10598,6 @@
"resolved": "https://registry.npmmirror.com/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -10608,7 +10607,6 @@
"resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -11458,6 +11456,7 @@
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
@@ -11521,6 +11520,7 @@
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -11533,6 +11533,7 @@
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -11554,6 +11555,7 @@
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -11567,6 +11569,7 @@
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"minimist": "^1.2.6"
},
@@ -11581,6 +11584,7 @@
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -11729,7 +11733,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -11906,7 +11909,6 @@
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -12342,7 +12344,6 @@
"integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}