Compare commits

..
4 changed files with 50 additions and 32 deletions

No files matched your search

-29
View File
@@ -1,29 +0,0 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { DEFAULT_KEY_BINDINGS, keyStringToKeyboardEvent } from './models/keyBindings.ts';
import { checkAppShortcut } from '../application/state/useGlobalHotkeys.ts';
function shortcut(key: string, isMac = false, bindings = DEFAULT_KEY_BINDINGS) {
const event = keyStringToKeyboardEvent(key);
assert.ok(event);
return checkAppShortcut(event, bindings, isMac)?.action ?? null;
}
test('default shortcuts leave Ctrl+L available to the terminal on both schemes', () => {
assert.equal(shortcut('Ctrl + L'), null);
assert.equal(shortcut('Ctrl + L', true), null);
});
test('local terminal remains available through New Local Tab and the macOS shortcut', () => {
assert.equal(shortcut('Ctrl + T'), 'newTab');
assert.equal(shortcut('⌘ + T', true), 'newTab');
assert.equal(shortcut('⌘ + L', true), 'openLocal');
});
test('an explicitly configured Ctrl+L still opens a local terminal', () => {
const customBindings = DEFAULT_KEY_BINDINGS.map(binding => binding.id === 'open-local'
? { ...binding, pc: 'Ctrl + L' }
: binding);
assert.equal(shortcut('Ctrl + L', false, customBindings), 'openLocal');
});
+1 -1
View File
@@ -264,7 +264,7 @@ export const DEFAULT_KEY_BINDINGS: KeyBinding[] = [
// App Features
{ id: 'open-hosts', action: 'openHosts', label: 'Open Hosts Page', mac: 'Disabled', pc: 'Disabled', category: 'app' },
{ id: 'open-local', action: 'openLocal', label: 'Open Local Terminal', mac: '⌘ + L', pc: 'Disabled', category: 'app' },
{ id: 'open-local', action: 'openLocal', label: 'Open Local Terminal', mac: '⌘ + L', pc: 'Ctrl + L', category: 'app' },
{ id: 'open-sftp', action: 'openSftp', label: 'Open SFTP', mac: '⌘ + Shift + O', pc: 'Ctrl + Shift + O', category: 'app' },
{ id: 'port-forwarding', action: 'portForwarding', label: 'Open Port Forwarding', mac: '⌘ + P', pc: 'Ctrl + P', category: 'app' },
{ id: 'command-palette', action: 'commandPalette', label: 'Open Command Palette', mac: '⌘ + K', pc: 'Ctrl + K', category: 'app' },
+7 -1
View File
@@ -635,7 +635,13 @@ function createSessionOpsApi(ctx) {
_rc_cwd=$(readlink "/proc/$1/cwd" 2>/dev/null)
if [ -n "$_rc_cwd" ]; then printf '%s\\n' "$_rc_cwd"; return 0; fi
if command -v lsof >/dev/null 2>&1; then
_rc_cwd=$(LC_ALL=C lsof -a -p "$1" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -n1)
# An unknown-type record can put a readlink error in its name field.
# Only a confirmed directory name is usable as an upload destination.
_rc_cwd=$(LC_ALL=C lsof -a -p "$1" -d cwd -Fnt 2>/dev/null | awk '
/^f/ { is_dir=0 }
/^t/ { is_dir=($0 == "tDIR" || $0 == "tVDIR") }
/^n/ && is_dir { print substr($0, 2); exit }
')
if [ -n "$_rc_cwd" ]; then printf 'NETCATTY_LSOF_CWD=%s\\n' "$_rc_cwd"; return 0; fi
fi
return 1
@@ -29,7 +29,7 @@ function makeApi(session, siblingSessions = [], overrides = {}) {
sessions,
setTimeout: overrides.setTimeout || setTimeout,
clearTimeout: overrides.clearTimeout || clearTimeout,
quoteShellArg,
quoteShellArg: overrides.quoteShellArg || quoteShellArg,
log: () => {},
});
}
@@ -123,6 +123,47 @@ test("session cwd probe decodes the marked lsof pathname", async () => {
assert.deepEqual(result, { success: true, cwd: "/srv/中文" });
});
test("lsof cwd fallback accepts only directory records, not diagnostic names (#3237)", async () => {
let script;
const api = makeApi({
shellPid: "4242",
connRef: { count: 1 },
stream: {},
conn: {
exec(_command, callback) {
callback(null, makePwdStream("/srv/app", "4242"));
},
},
}, [], {
quoteShellArg(value) {
if (!script && value.includes("read_shell_cwd()")) script = value;
return quoteShellArg(value);
},
});
await api.getSessionPwd(null, { sessionId: "session-1" });
const helper = script.match(/ read_shell_cwd\(\) \{[\s\S]*?\n \}/)?.[0];
assert.ok(helper, "exercise the actual remote cwd helper");
const { spawnSync } = require("node:child_process");
for (const [output, expected] of [
["p4242\nfcwd\ntunknown\nn/proc/4242/cwd (readlink: Permission denied)\n", null],
["p4242\nfcwd\ntunknown\nn/proc/4242/cwd (readlink: No such file or directory)\n", null],
["p4242\nfcwd\nn/proc/4242/cwd\n", null],
["p4242\nfcwd\ntDIR\nn/srv/app\n", "/srv/app"],
["p4242\nfcwd\ntVDIR\nn/usr/home/alice\n", "/usr/home/alice"],
["p4242\nfcwd\ntDIR\nn/tmp/literal (readlink: Permission denied)\n", "/tmp/literal (readlink: Permission denied)"],
["p4242\nfcwd\ntDIR\nn/tmp/\\xe4\\xb8\\xad\\xe6\\x96\\x87\n", "/tmp/\\xe4\\xb8\\xad\\xe6\\x96\\x87"],
]) {
const result = spawnSync("sh", ["-c", `
readlink() { return 1; }
lsof() { printf '%s' "$LSOF_TEST_OUTPUT"; }
${helper}
read_shell_cwd 4242
`], { encoding: "utf8", env: { ...process.env, LSOF_TEST_OUTPUT: output } });
assert.equal(result.status, expected === null ? 1 : 0, output);
assert.equal(result.stdout, expected === null ? "" : `NETCATTY_LSOF_CWD=${expected}\n`, output);
}
});
test("session cwd probe closes a remote command that exceeds its timeout", async () => {
const stream = new EventEmitter();
stream.stderr = new EventEmitter();