Files
Netcatty/electron-builder.config.cjs
T
38ff20ca80 docs: lossless WebP README screenshots and Trendshift badges (#3812)
* docs: losslessly shrink README images and mirror Trendshift badge

Recompress local images linked from the READMEs only when the file gets
smaller and decoded pixels stay identical. Copy the Trendshift badge from
README.md into the zh-CN, zh-TW, and ja-JP READMEs.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* docs: replace README screenshots with lossless WebP

Store the ten README screenshots as lossless WebP under docs/assets/readme
and point every language README at those files. Keep them out of app.asar.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-10-21 21:20:02 +08:00

350 lines
16 KiB
JavaScript

const { moshExtraResources } = require('./scripts/mosh-extra-resources.cjs');
const { etExtraResources } = require('./scripts/et-extra-resources.cjs');
/**
* @type {import('electron-builder').Configuration}
*/
module.exports = {
appId: 'com.netcatty.app',
productName: 'Netcatty',
artifactName: '${productName}-${version}-${os}-${arch}.${ext}',
protocols: [
{
name: 'SSH URL',
schemes: ['ssh']
},
{
name: 'Telnet URL',
schemes: ['telnet']
},
{
name: 'JumpServer URL',
schemes: ['jms']
}
],
electronLanguages: ['en', 'en-US', 'zh_CN', 'zh-CN', 'zh_TW', 'zh-TW', 'ru'],
// Give the macOS build a unique Mach-O LC_UUID before signing, so macOS
// Local Network privacy treats Netcatty distinctly from every other
// Electron app (which all share Electron's prebuilt LC_UUID) — see #1040
// and scripts/afterPackMacUuid.cjs. No-op on Windows/Linux.
beforePack: './scripts/beforePackCursorSdk.cjs',
afterPack: './scripts/afterPackMacUuid.cjs',
// Platform-split icons (#813):
// - public/icon.png keeps Apple's HIG grid margin so the rendered
// squircle sits at ~88% of the PNG canvas. macOS needs this —
// the dock renders icons with its own rounding/shadow and most
// third-party apps (#803) leave that grid margin alone so the
// squircle lines up with neighbors.
// - public/icon-win.png uses a tight-crop viewBox so the squircle
// fills 100% of the PNG. Windows / Linux taskbars render icons
// full-bleed, so the Apple margin showed up as visible padding,
// making the app icon look smaller than other apps in taskbar /
// Start menu / desktop shortcuts.
icon: 'public/icon.png',
// npmRebuild must stay enabled for macOS and Windows builds — without it,
// node-pty's native module is not recompiled for the Electron ABI, causing
// "posix_spawnp failed" on macOS. Linux builds set npm_config_arch in CI
// and run ensure-node-pty-linux.sh before packaging, so the rebuild is
// redundant but harmless there.
npmRebuild: true,
directories: {
buildResources: 'build',
output: 'release'
},
files: [
'dist/**/*',
'electron/**/*',
// The Windows Hello helper is shipped once through win.extraResources.
// Exclude local per-arch build output from app.asar on every platform.
'!electron/bridges/windowsHelloHelper/build/**/*',
// Runtime smoke fixtures are built test packages, not host resources.
// Keep them out of production ASARs so no example plugin can be
// mistaken for an installed or host-trusted package.
'!electron/plugins/fixtures/**/*',
// Main-process terminal flow control reads shared thresholds from here
// (terminalFlowAck.cjs). Must ship beside electron/ in app.asar.
'infrastructure/config/terminalFlowConstants.cjs',
'infrastructure/config/terminalFlowConstants.json',
// Renderer and Mosh's main-process bootstrap share the same
// fail-closed prompt classifier at runtime.
'domain/terminalPromptSecurity.shared.cjs',
// Main-process SSH, SFTP, and port-forward bridges share this matcher.
// TypeScript under domain/ is excluded below, so the .cjs must be listed.
'domain/systemManager/containerRuntime.shared.cjs',
'domain/singleChannelSshBanner.shared.cjs',
'domain/singleChannelSshBanner.shared.mjs',
'domain/sshServerVendor.mjs',
// sshAuthHelper.cjs requires this at load. TypeScript under domain/
// is excluded below, so the shared TOTP runtime must be listed.
'domain/totp.shared.cjs',
'domain/totpPrompt.shared.cjs',
'lib/**/*.cjs',
'lib/**/*.json',
'skills/**/*',
'!public/**/*',
// README screenshots live under docs/ and are not app resources.
// Keep them out of app.asar if the files globs ever become permissive.
'!docs/assets/readme/**/*',
'!**/*.map',
'!**/*.d.ts',
'!**/*.d.mts',
'!**/*.d.cts',
'!**/*.ts',
'!**/*.tsx',
'!**/*.test.*',
'!**/*.spec.*',
'!**/__tests__/**/*',
'!**/test/**/*',
'!**/tests/**/*',
'!**/example/**/*',
'!**/examples/**/*',
'!node_modules/**/docs/**/*',
'!node_modules/**/doc/**/*',
'!node_modules/**/benchmark/**/*',
'!node_modules/**/benchmarks/**/*',
// Renderer-only packages are compiled into dist by Vite. Keep them
// installed for npm run dev/build, but do not ship the duplicate source
// packages in release artifacts.
'!node_modules/@fontsource/**/*',
'!node_modules/@monaco-editor/**/*',
'!node_modules/@radix-ui/**/*',
'!node_modules/@xterm/**/*',
'!node_modules/lucide-react/**/*',
'!node_modules/monaco-editor/**/*',
'!node_modules/react/**/*',
'!node_modules/react-dom/**/*',
'!node_modules/ai/**/*',
'!node_modules/@ai-sdk/**/*',
'!node_modules/@mdxeditor/**/*',
'!node_modules/streamdown/**/*',
'!node_modules/@streamdown/**/*',
'!node_modules/@tanstack/react-virtual/**/*',
'!node_modules/pinyin-pro/**/*',
// re2js stays in the package: the main-process TOTP prompt matcher
// requires it so custom patterns cannot freeze Electron.
'!node_modules/@eslint-community/regexpp/**/*',
'!node_modules/clsx/**/*',
'!node_modules/tailwind-merge/**/*',
'!node_modules/use-stick-to-bottom/**/*',
'!node_modules/lexical/**/*',
'!node_modules/@lexical/**/*',
'!node_modules/@codemirror/**/*',
'!node_modules/katex/**/*',
'!node_modules/shiki/**/*',
'!node_modules/@shiki/**/*',
// Heavy cloud completion specs are intentionally not bundled. The main
// process filters the same prefixes so dev and packaged builds behave
// consistently.
'!node_modules/@withfig/autocomplete/build/aws.js',
'!node_modules/@withfig/autocomplete/build/aws/**/*',
'!node_modules/@withfig/autocomplete/build/gcloud.js',
'!node_modules/@withfig/autocomplete/build/gcloud/**/*',
'!node_modules/@withfig/autocomplete/build/az/**/*',
// Fig specs are already compiled JavaScript; TypeScript is only pulled
// in by Fig helper packages as build tooling and is not needed at app
// runtime.
'!node_modules/typescript/**/*',
// ── Exclude per-platform native agent binaries (~100s of MB each). ──
// Netcatty is "bring your own CLI": each SDK is pointed at the user's
// system-installed CLI via an absolute path override (claude
// pathToClaudeCodeExecutable / codex codexPathOverride / copilot cliPath).
// Only the SDKs' JS is bundled; the heavy per-arch binaries are dropped.
// NOTE: claude-agent-sdk vendors the `claude` binary as a NESTED package
// (claude-agent-sdk/node_modules/@anthropic-ai/claude-agent-sdk-<arch>),
// so this exclusion must match at any depth (**/), not just top-level.
// The codex/copilot exclusions target only the per-arch binary packages
// (codex-<arch> / copilot-<arch>) — NOT @openai/codex-sdk / copilot-sdk,
// whose JS we DO bundle. @github/copilot is the full ~288MB CLI (with
// per-platform prebuilds); netcatty uses the user's copilot via cliPath,
// so it is excluded entirely.
'!**/@anthropic-ai/claude-agent-sdk-*/**/*',
'!node_modules/@anthropic-ai/claude-code-*/**/*',
'!node_modules/@openai/codex-{darwin,linux,linuxmusl,win32}-*/**/*',
'!node_modules/@github/copilot-{darwin,linux,linuxmusl,win32}-*/**/*',
'!node_modules/@github/copilot/**/*',
'!node_modules/opencode-{darwin,linux,linuxmusl,windows}-*/**/*',
'!node_modules/opencode-ai/**/*',
// CodeBuddy follows the same first-party integration model as the
// other coding agents: Netcatty discovers and passes the user's
// installed CLI path to the SDK. Keep the small SDK wrapper, but do not
// bundle the full CodeBuddy CLI payload (rg vendors + web UI).
'!node_modules/@tencent-ai/agent-sdk/cli/**/*',
// Netcatty loads Cursor SDK through ESM dynamic import, so the duplicate
// CommonJS build and type metadata are not needed at runtime.
'!node_modules/@cursor/sdk/dist/cjs/**/*',
'!node_modules/@cursor/sdk/dist/**/*.d.ts',
'!node_modules/@cursor/sdk/dist/**/*.d.ts.map',
// sqlite3 rebuilds a native module for Electron; its upstream source
// tarball is build-time payload only.
'!node_modules/sqlite3/deps/**/*',
// Native compilation helpers are needed during install/rebuild only.
// Match nested copies too; keep the actual native loaders and binaries.
'!node_modules/**/node-gyp/**/*',
'!node_modules/**/node-addon-api/**/*',
'!node_modules/**/nan/**/*'
],
asarUnpack: [
'node_modules/node-pty/**/*',
// afterPack removes prebuilds for other platforms/architectures. Keep
// these outside ASAR so removing them also removes their payload.
'node_modules/@serialport/bindings-cpp/prebuilds/**/*',
'node_modules/ssh2/**/*',
'node_modules/cpu-features/**/*',
'node_modules/@vscode/windows-process-tree/**/*',
'node_modules/@anthropic-ai/claude-agent-sdk/**/*',
'node_modules/@cursor/sdk-*/**/*',
'node_modules/sqlite3/**/*',
'node_modules/@modelcontextprotocol/sdk/**/*',
'lib/**/*.cjs',
'lib/**/*.json',
'node_modules/zod/**/*',
'node_modules/zod-to-json-schema/**/*',
'node_modules/@netcatty/plugin-cli/**/*',
'node_modules/@netcatty/plugin-contract/**/*',
'node_modules/@netcatty/plugin-sdk/**/*',
'electron/plugins/runtime/**/*',
'node_modules/ajv/**/*',
'node_modules/ajv-formats/**/*',
'node_modules/fast-deep-equal/**/*',
'node_modules/fast-uri/**/*',
'node_modules/json-schema-traverse/**/*',
'electron/cli/**/*',
'electron/capabilities/**/*',
'electron/shared/**/*',
'electron/mcp/**/*',
'skills/**/*'
],
mac: {
// app-builder's PNG-to-ICNS conversion can corrupt the 16px/32px 1x
// representations even though the source PNG is valid RGBA. Use the
// iconutil-generated bundle instead so Finder and app switchers do not
// render those representations as colored noise.
icon: 'build/icon.icns',
target: [
{
target: 'dmg',
arch: ['arm64', 'x64']
},
{
target: 'zip',
arch: ['arm64', 'x64']
}
],
category: 'public.app-category.developer-tools',
hardenedRuntime: true,
notarize: true,
entitlements: 'electron/entitlements.mac.plist',
entitlementsInherit: 'electron/entitlements.mac.plist',
extendInfo: {
NSCameraUsageDescription: 'Netcatty may use the camera for video calls',
NSMicrophoneUsageDescription: 'Netcatty may use the microphone for audio',
NSLocalNetworkUsageDescription: 'Netcatty needs local network access for SSH connections',
CFBundleDocumentTypes: [
{
CFBundleTypeName: 'Folder',
CFBundleTypeRole: 'Viewer',
LSHandlerRank: 'Alternate',
LSItemContentTypes: ['public.folder']
}
]
},
extraResources: [...moshExtraResources('darwin'), ...etExtraResources('darwin')]
},
dmg: {
title: '${productName}',
iconSize: 100,
iconTextSize: 12,
window: {
width: 540,
height: 380
},
contents: [
{ x: 140, y: 158 },
{ x: 400, y: 158, type: 'link', path: '/Applications' }
]
},
win: {
icon: 'public/icon-win.png',
// Do not hard-code arch here. pack:win-x64 / pack:win pass --x64/--arm64,
// and electron-builder unions config arch with the CLI set. Hard-coding
// ['x64', 'arm64'] made the official x64 CI job also emit win-arm64 and
// a universal win.exe whose 32-bit NSIS stub can mis-detect on ARM
// Windows, install to Program Files (x86), and leave Netcatty.exe
// missing (#2570). Keep official releases on pack:win-x64 until win32
// arm64 bundled mosh/et + native rebuilds are ready.
target: ['nsis', 'portable', 'zip'],
extraResources: [
...moshExtraResources('win32'),
...etExtraResources('win32'),
{
from: 'electron/bridges/windowsHelloHelper/build/${arch}/NetcattyWindowsHello.exe',
to: 'windowsHello/NetcattyWindowsHello.exe',
filter: ['**/*']
}
]
},
portable: {
artifactName: '${productName}-${version}-portable-${os}-${arch}.${ext}',
},
nsis: {
include: 'build/installer.nsh',
oneClick: false,
perMachine: false,
allowElevation: true,
allowToChangeInstallationDirectory: true,
createDesktopShortcut: true,
createStartMenuShortcut: true,
shortcutName: 'Netcatty'
},
linux: {
// Linux .deb/.rpm/AppImage icons come from build/icons/* (see
// scripts/generate-linux-icons.sh). Point at the icons directory
// under buildResources — electron-builder still falls back to the
// top-level icon (public/icon.png) when linux.icon is unset, which
// installs only hicolor/1024x1024 and launchers miss the icon (#274,
// #1340). Do NOT set linux.icon to a single 1024px PNG either.
icon: 'icons',
target: ['AppImage', 'deb', 'rpm', 'pacman'],
category: 'Development',
extraResources: [...moshExtraResources('linux'), ...etExtraResources('linux')]
},
deb: {
// Use gzip instead of default xz(lzma) for better compatibility with
// Deepin OS and other distros that have issues with lzma decompression
compression: 'gz',
afterInstall: 'scripts/linux/after-install.tpl'
},
rpm: {
// Default fpm/electron-builder RPM compression is "xzmt" (multi-threaded
// xz). AlmaLinux/RHEL 8 images provide `xz` but not the `xzmt` shim, so
// rpmbuild fails with exit 127 during packaging. gzip is portable and
// matches our deb preference for older distros.
compression: 'gzip',
afterInstall: 'scripts/linux/after-install.tpl',
fpm: [
// Avoid rpm's generated /usr/lib/.build-id symlinks. Those hashes
// are global on the host, so owning them can conflict with other RPMs.
'--rpm-rpmbuild-define', '_build_id_links none',
// Electron ships prebuilt binaries. RHEL/Alma brp post-install
// scripts (strip/compress/etc.) can exit 127 when a helper is
// missing or a gcc-toolset `strip` is on PATH; skip them.
'--rpm-rpmbuild-define', '__os_install_post %{nil}',
]
},
pacman: {
// FPM-generated .pacman packages bypass Arch's alpm hooks that
// normally refresh the hicolor icon cache. Without this, KDE cannot
// resolve Icon=netcatty and shows a generic placeholder (#1358).
afterInstall: 'scripts/linux/after-install.tpl',
afterRemove: 'scripts/linux/after-remove.tpl',
},
publish: [
{
provider: 'github',
owner: 'binaricat',
repo: 'Netcatty',
releaseType: 'release'
}
]
};