Compare commits

..
Author SHA1 Message Date
bincxz 506dcab734 style(sync): align convergent sync panel with settings 2026-10-18 16:08:57 +08:00
6 changed files with 8 additions and 130 deletions

No files matched your search

-42
View File
@@ -1,42 +0,0 @@
## ✨ Release Highlights
Netcatty **v1.1.84** introduces custom host monitoring dashboards and an integrated host browser, expands AI conversation and model controls, and improves cloud sync, SSH authentication, and everyday terminal workflows.
### 📊 Custom host monitoring dashboards
- Create named **monitoring panels** in the Vault, choose their hosts, and switch between chart and list views. Panel configuration syncs across devices (#3703).
- Inspect CPU, memory, disk, network, and latency readings, with GPU details where available. Choose visible columns, resize them, adjust refresh controls, and open a terminal directly from a monitored host (#3704, #3705, #3713).
- Virtualized host collections keep large monitoring views responsive (#3713).
### 🌐 Browse host services inside Netcatty
- Open the **Browser** tool alongside SFTP and Notes in a terminal side panel. SSH browsing uses the selected host's network path, including services on that host's `localhost` (#3701).
- Work with browser tabs, bookmarks, navigation controls, and downloads without leaving the terminal workspace. Terminal links can open internally or in your system browser, according to your preference (#3701, #3712).
- Browser contexts stay with their terminal, and idle panes avoid unnecessary layout polling (#3701, #3723).
### 🤖 More control over AI conversations
- **Search chat history**, fork a conversation from a completed response, or undo the last turn by branching at a safe boundary while preserving the original conversation (#3601, #3602, #3604).
- Use the new **MiMo Code** agent integration. SDK model pickers gain clearer loading, source and retry states, better live model discovery, and support for manual model IDs (#3500, #3535, #3624, #3642).
- Dock the AI assistant at the bottom, scope external MCP approvals to a connection or host, and let Skills + CLI work with Vault Notes (#3482, #3569, #3512).
- Long-running AI sessions now default to an **8-hour idle timeout**. Catty prompt-cache handling and cached-token usage reporting are improved (#3618, #3714).
### ☁️ Cloud sync and Vault organization
- Choose a **non-destructive merge** strategy that retains entries missing from only one side, and include AI conversation history in cloud sync (#3633).
- Clear local backup history with confirmation, with clearer disconnect and retry behavior and a refreshed convergent-sync settings panel (#3708, #3728).
- Import FinalShell hosts, add Markdown notes to host groups, move selected hosts into a group, search workspace hosts by tag, and test a connection from the host editor (#3331, #3372, #3675, #3337, #2984).
### 🖥️ Terminal and editing workflows
- Save and automatically apply a **default terminal side-panel layout**, and open files in a detached editor window with multiple tabs (#3456, #3606).
- Confirm multiline pastes, recall compose-bar history, save the current terminal screen, and configure tab double-click behavior and cursor width (#3400, #3365, #3364, #3401, #3495).
- Highlight Unix paths with a switchable rule and prepare `tar` or `zip` commands from archive links. Command history gains a clear-all action (#3672, #3682, #3632).
- Toggle window transparency while preserving the chosen opacity (#2987).
### 🔐 SSH, SFTP, and responsiveness
- **FIDO2 OpenSSH security keys** return with touch/PIN prompts, retry handling, and an explicit saved-password fallback when available. Compatible hardware and OpenSSH support are required (#2823, #3709).
- Enable a single-channel SSH option for bastion hosts. SSH auto-reconnect is enabled by default, while automatic terminal closure on exit is disabled by default (#3458, #3617, #3619).
- SFTP gains a **Cancel all** transfer action and local-shell file browsing, with fixes for independent upload cancellation, interrupted downloads, and transfer setup errors (#3574, #3379, #3638, #3702).
- Large SFTP panes, expanded transfer lists, AI history searches, detached editors, and host searches receive responsiveness improvements. Release packages omit duplicate renderer and unused native files (#3716, #3717, #3719, #3721, #3722, #3724, #3726).
+1 -5
View File
@@ -117,9 +117,5 @@ See the
and [Privacy policy](https://github.com/${repo}/blob/${tag}/PRIVACY.md).
`;
// Keep editorial highlights scoped to their release so future tags do not
// accidentally reuse an older version's announcement.
const highlightsPath = path.join(__dirname, '..', 'release-notes', `${path.basename(tag)}.md`);
const highlights = fs.existsSync(highlightsPath) ? fs.readFileSync(highlightsPath, 'utf8').trim() : '';
fs.writeFileSync('release_notes.md', highlights ? `${highlights}\n\n${content.trimStart()}` : content);
fs.writeFileSync('release_notes.md', content);
console.log('Generated release_notes.md');
+1 -4
View File
@@ -745,10 +745,7 @@ function createSessionOpsApi(ctx) {
home=$(id -P 2>/dev/null | awk -F: 'NR == 1 { print $9; exit }')
emit_home "$home"
emit_home "$HOME"
exit 1`.replace(/^[ \t]*#[^\n]*\n/gm, "");
// Omit explanatory comments before quoting this static probe. Dropbear
// closes the entire transport for exec requests over 9000 bytes (#3731).
// Preserve the directory checks above to avoid regressing #3493.
exit 1`;
// Capture sshd's pid in the exec'd POSIX sh (`$PPID` is sshd because
// `exec` replaced the login shell). Do not `exec` the inner posixScript:
// that would skip the watchdog cleanup after it.
@@ -127,10 +127,9 @@ test("session cwd probe decodes the marked lsof pathname", async () => {
assert.deepEqual(result, { success: true, cwd: "/srv/中文" });
});
async function assertLsofCwdFallback(t, remoteSshVersion) {
test("lsof cwd fallback accepts only directory records, not diagnostic names (#3237, #3493)", async (t) => {
let script;
const api = makeApi({
remoteSshVersion,
shellPid: "4242",
connRef: { count: 1 },
stream: {},
@@ -141,7 +140,7 @@ async function assertLsofCwdFallback(t, remoteSshVersion) {
},
}, [], {
quoteShellArg(value) {
if (value.startsWith("SELF=")) script = value;
if (!script && value.includes("read_shell_cwd()")) script = value;
return quoteShellArg(value);
},
});
@@ -189,12 +188,7 @@ async function assertLsofCwdFallback(t, remoteSshVersion) {
assert.equal(result.status, expected === null ? 1 : 0, `${output}\n${result.stderr}`);
assert.equal(result.stdout, expected === null ? "" : `NETCATTY_LSOF_CWD=${expected}\n`, output);
}
}
for (const remoteSshVersion of ["OpenSSH_9.6", "dropbear"]) {
test(`lsof cwd fallback accepts only directories on ${remoteSshVersion} (#3237, #3493)`,
(t) => assertLsofCwdFallback(t, remoteSshVersion));
}
});
test("session cwd probe closes a remote command that exceeds its timeout", async () => {
const stream = new EventEmitter();
@@ -903,52 +897,3 @@ test("getSessionPwd skips exec after the CloudLinker banner stamps the runtime c
assert.match(result.error, /extra exec channels/);
assert.equal(execCalls, 0);
});
for (const shellPid of [undefined, "4242"]) {
test(`cwd probe fits Dropbear's limit with shell pid ${shellPid ?? "unknown"} (#3731)`, async () => {
let command = "";
const session = {
shellPid,
remoteSshVersion: "dropbear",
connRef: { count: 1 },
stream: {},
conn: {
exec(nextCommand, callback) {
command = nextCommand;
callback(null, makePwdStream("/owrt/root", "4242"));
},
},
};
const result = await makeApi(session).getSessionPwd(null, { sessionId: "session-1" });
assert.deepEqual(result, { success: true, cwd: "/owrt/root" });
assert.ok(Buffer.byteLength(command, "utf8") <= 9000);
assert.ok(command.includes('readlink "/proc/$1/cwd"'));
assert.ok(command.includes("-Fnt 2>/dev/null"));
assert.ok(command.includes("_rc_test_cwd"), "preserve annotation validation");
assert.ok(!command.includes("# Find the user's interactive shell"));
});
}
test("OpenSSH peers keep the annotation-aware lsof cwd fallback (#3493)", async () => {
let command = "";
const session = {
shellPid: "4242",
remoteSshVersion: "OpenSSH_9.6",
connRef: { count: 1 },
stream: {},
conn: {
exec(nextCommand, callback) {
command = nextCommand;
callback(null, makePwdStream("/srv/app", "4242"));
},
},
};
const api = makeApi(session);
const result = await api.getSessionPwd(null, { sessionId: "session-1" });
assert.deepEqual(result, { success: true, cwd: "/srv/app" });
assert.ok(command.includes("_rc_test_cwd"), "keep the #3493 annotation decode");
assert.ok(command.includes("-Fnt 2>/dev/null"));
});
+3 -3
View File
@@ -1,14 +1,14 @@
{
version = "1.1.84";
version = "1.1.83";
sources = {
x86_64-linux = {
appImageArch = "x86_64";
hash = "sha256-BqCH0I9iBufzAm+L9STHQxnKxJAYn19NJRD/uXSkS4w=";
hash = "sha256-p83CJ9oQcIJV7OTfgQt2DEqdEG4wiZvqFn5TVir1xdg=";
};
aarch64-linux = {
appImageArch = "arm64";
hash = "sha256-zv/eZbIEa0uIQaUI0AUxfap0TJcWuQGEV3CrnlbZ/rM=";
hash = "sha256-zMULcewEakxVZUz8pKBfTcrONSMAY1b42J6zeDy0drs=";
};
};
}
-18
View File
@@ -34,7 +34,6 @@ test("release notes include Arch pacman downloads for x64 and arm64", (t) => {
/https:\/\/github\.com\/binaricat\/Netcatty\/releases\/download\/v1\.2\.3\/Netcatty-1\.2\.3-linux-aarch64\.pacman/,
);
assert.match(notes, /Code signing policy/);
assert.doesNotMatch(notes, /Release Highlights/);
assert.match(
notes,
/Free code signing provided by SignPath\.io, certificate by SignPath Foundation/,
@@ -44,20 +43,3 @@ test("release notes include Arch pacman downloads for x64 and arm64", (t) => {
/https:\/\/github\.com\/binaricat\/Netcatty\/blob\/v1\.2\.3\/CODE_SIGNING_POLICY\.md/,
);
});
test("release-specific highlights precede downloads without replacing generated metadata", (t) => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-release-note-"));
t.after(() => fs.rmSync(tmp, { recursive: true, force: true }));
const script = path.join(__dirname, "..", ".github", "scripts", "generate-release-note.js");
const highlights = fs.readFileSync(path.join(__dirname, "..", ".github", "release-notes", "v1.1.84.md"), "utf8").trim();
execFileSync(process.execPath, [script], {
cwd: tmp,
env: { ...process.env, VERSION: "1.1.84", GITHUB_REF_NAME: "v1.1.84", GITHUB_REPOSITORY: "binaricat/Netcatty" },
stdio: "pipe",
});
const notes = fs.readFileSync(path.join(tmp, "release_notes.md"), "utf8");
assert.ok(notes.startsWith(`${highlights}\n\n## Download based on your OS:`));
assert.match(notes, /releases\/download\/v1\.1\.84\/Netcatty-1\.1\.84-win-x64\.exe/);
assert.match(notes, /blob\/v1\.1\.84\/CODE_SIGNING_POLICY\.md/);
assert.equal(notes.match(/## ✨ Release Highlights/g).length, 1);
});