fix: portable 绿色单机版 SSH 连接 + 主机数据 + 打包体积
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
SSH 连接修复: - main.cjs 顶部在所有 ssh2 require 之前安装 boringSslDhCompat 补丁 - 5 条 SSH 连接路径统一 legacyAlgorithms 默认值为 true portable 版主机数据修复: - main.cjs portable 模式设置 NETCATTY_PORTABLE=1 - preload.cjs 暴露 window.netcatty.isPortable - useVaultState portable 模式跳过 safeStorage DPAPI 加密的 hosts_v1 - inspection saveVaultHosts handler 补 legacyAlgorithms/tags/os 字段映射 打包体积修复: - 恢复 electron-builder.config.cjs 原始 40+ 条排除规则 - package.json 加 pend 运行时依赖
This commit is contained in:
@@ -1442,7 +1442,13 @@ export const useVaultState = () => {
|
||||
recoverPluginImporterTransaction(localStorageAdapter, PLUGIN_IMPORT_TRANSACTION_KEYS);
|
||||
});
|
||||
if (cancelled) return;
|
||||
const savedHosts = localStorageAdapter.read<Host[]>(STORAGE_KEY_HOSTS);
|
||||
// portable 版 safeStorage DPAPI 绑定 userData 路径,解不开 dev 复制过来的
|
||||
// LS 加密 blob。强制跳过 hosts_v1,直接走 else 分支从 inspection-data.json
|
||||
// 读明文 _vault_hosts(含 password + legacyAlgorithms)。
|
||||
const isPortable = (window as any).netcatty?.isPortable === true;
|
||||
const savedHosts = isPortable
|
||||
? undefined
|
||||
: localStorageAdapter.read<Host[]>(STORAGE_KEY_HOSTS);
|
||||
|
||||
if (savedHosts) {
|
||||
// Capture version before the async gap so that any write occurring
|
||||
@@ -1452,6 +1458,17 @@ export const useVaultState = () => {
|
||||
const decrypted = await decryptHosts(savedHosts);
|
||||
if (cancelled) return;
|
||||
if (ver === hostsWriteVersion.current) {
|
||||
// safeStorage 解密失败时 decryptField 会返回 enc:v1 占位符,
|
||||
// sanitizeHost 再把它过滤成 undefined → 密码/私钥消失。
|
||||
// 检查解密后是否大部分凭证都被清空了,是的话强制 fallback
|
||||
// 到 inspectionStorage 的明文 _vault_hosts(打包版 portable 场景)。
|
||||
const hasAnyPassword = decrypted.some((h: Host) =>
|
||||
h.password && !h.password.startsWith("enc:v1:"));
|
||||
const hasAnyPrivateKey = decrypted.some((h: Host) =>
|
||||
h.privateKey && !h.privateKey.startsWith("enc:v1:"));
|
||||
const credentialsLikelyLost =
|
||||
decrypted.length > 0 && !hasAnyPassword && !hasAnyPrivateKey;
|
||||
|
||||
const sanitized = normalizeVaultOrder(
|
||||
migrateHostsFromLegacyLineTimestamps(
|
||||
decrypted.map((host) => sanitizeHost(host)),
|
||||
@@ -1473,6 +1490,41 @@ export const useVaultState = () => {
|
||||
return commitEncryptedHostsUnderVaultLock(ver, sanitized, enc);
|
||||
});
|
||||
hostsWritePendingRef.current = writePromise;
|
||||
|
||||
// 解密后凭证丢失 → 用 inspectionStorage 的明文覆盖
|
||||
if (credentialsLikelyLost) {
|
||||
const nc = (window as any).netcatty;
|
||||
if (nc?.inspection?.getVaultHosts) {
|
||||
try {
|
||||
const vaultHosts = await nc.inspection.getVaultHosts();
|
||||
if (Array.isArray(vaultHosts) && vaultHosts.length > 0) {
|
||||
const fallback: Host[] = vaultHosts.map((v: any) => ({
|
||||
id: v.id,
|
||||
label: v.label || "",
|
||||
hostname: v.hostname || "",
|
||||
port: v.port || 22,
|
||||
username: v.username || "",
|
||||
password: v.password || undefined,
|
||||
identityFileId: v.identityFileId || undefined,
|
||||
privateKey: v.privateKey || "",
|
||||
passphrase: v.passphrase || "",
|
||||
tags: v.tags || [],
|
||||
os: v.os || "linux",
|
||||
type: v.type || "",
|
||||
distro: v.distro || "",
|
||||
group: v.group || "",
|
||||
legacyAlgorithms: v.legacyAlgorithms,
|
||||
}));
|
||||
const fbSanitized = normalizeVaultOrder(
|
||||
fallback.map((h) => sanitizeHost(h)),
|
||||
);
|
||||
if (cancelled) return;
|
||||
hostsRef.current = fbSanitized;
|
||||
setHosts(fbSanitized);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 本地 Local Storage 为空(可能是 origin 隔离导致的打包版环境)
|
||||
@@ -1499,6 +1551,7 @@ export const useVaultState = () => {
|
||||
type: v.type || "",
|
||||
distro: v.distro || "",
|
||||
group: v.group || "",
|
||||
legacyAlgorithms: v.legacyAlgorithms,
|
||||
}));
|
||||
if (cancelled) return;
|
||||
const sanitized = normalizeVaultOrder(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* 运行监控 - 3D 拓扑大屏
|
||||
*
|
||||
* 移植自 ITYUNWEI TopologyView.vue
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
const path = require('node:path');
|
||||
const path = require('node:path');
|
||||
const { moshExtraResources } = require('./scripts/mosh-extra-resources.cjs');
|
||||
const { etExtraResources } = require('./scripts/et-extra-resources.cjs');
|
||||
// 说明:electron-builder 依赖收集器在 monorepo/workspace 项目里会漏掉
|
||||
// workspace 子包引用但根 package.json 没声明的运行时依赖(hoist 上来的),
|
||||
// 且追踪传递依赖时容易漏包。这里直接把整个 node_modules 目录纳入 files,
|
||||
// 确保运行时 require 什么都能找到。用排除列表去掉前端/构建工具/文档/测试代码。
|
||||
|
||||
/**
|
||||
* @type {import('electron-builder').Configuration}
|
||||
@@ -58,19 +54,117 @@ module.exports = {
|
||||
output: 'release'
|
||||
},
|
||||
files: [
|
||||
// electron-builder 收集器只读根 package.json,但 workspace 子包的运行时依赖
|
||||
// 会 hoist 到根 node_modules 却没在根 package.json 里声明。
|
||||
// 收集器会漏掉这些包,所以把漏的补进根 package.json 后它就能追踪完整传递依赖树。
|
||||
"dist/**/*",
|
||||
"electron/**/*",
|
||||
"!electron/bridges/windowsHelloHelper/build/**/*",
|
||||
"!electron/plugins/fixtures/**/*",
|
||||
"infrastructure/config/terminalFlowConstants.cjs",
|
||||
"infrastructure/config/terminalFlowConstants.json",
|
||||
"domain/terminalPromptSecurity.shared.cjs",
|
||||
"lib/**/*.cjs",
|
||||
"lib/**/*.json",
|
||||
"skills/**/*"
|
||||
'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',
|
||||
'lib/**/*.cjs',
|
||||
'lib/**/*.json',
|
||||
'skills/**/*',
|
||||
'!public/**/*',
|
||||
'!**/*.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/**/*',
|
||||
'!node_modules/re2js/**/*',
|
||||
'!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/**/*'
|
||||
],
|
||||
asarUnpack: [
|
||||
'node_modules/node-pty/**/*',
|
||||
|
||||
@@ -1620,9 +1620,12 @@ function registerInspectionHandlers(ipcMain, options) {
|
||||
identityFileId: h.identityFileId,
|
||||
privateKey: h.privateKey,
|
||||
passphrase: h.passphrase,
|
||||
tags: h.tags || [],
|
||||
os: h.os || "linux",
|
||||
type: h.type || "",
|
||||
distro: h.distro || h.catalogId || "",
|
||||
group: h.group || "",
|
||||
legacyAlgorithms: h.legacyAlgorithms,
|
||||
}));
|
||||
writeStorage("_vault_hosts", mapped);
|
||||
return { ok: true, count: mapped.length };
|
||||
|
||||
@@ -1388,7 +1388,7 @@ async function startPortForward(event, payload) {
|
||||
keepaliveCountMax: keepalivePolicy.keepaliveCountMax,
|
||||
// Enable keyboard-interactive authentication (required for 2FA/MFA)
|
||||
tryKeyboard: true,
|
||||
algorithms: buildAlgorithms(legacyAlgorithms, { skipEcdsaHostKey, algorithmOverrides }),
|
||||
algorithms: buildAlgorithms(legacyAlgorithms !== false, { skipEcdsaHostKey, algorithmOverrides }),
|
||||
};
|
||||
connectOpts.hostVerifier = hostKeyVerifier.createHostVerifier({
|
||||
sender,
|
||||
|
||||
@@ -346,7 +346,7 @@ function createSshDiagnosticLogger(enabled) {
|
||||
|
||||
function shouldLogSshDebugMessage(msg) {
|
||||
if (typeof msg !== "string") return false;
|
||||
return /auth|publickey|keyboard|handshake|kex|newkeys|dh gex/i.test(msg);
|
||||
return /auth|publickey|keyboard|handshake|kex|newkeys|dh gex|Inbound|Outbound/i.test(msg);
|
||||
}
|
||||
|
||||
function attachSshDebugLogger(connectOpts, logger = log) {
|
||||
@@ -586,8 +586,14 @@ async function connectThroughChain(event, options, jumpHosts, targetHost, target
|
||||
// when the leaf's ECDSA skip is applied to it. Treat both as
|
||||
// strictly per-host: an unset hop value uses the hop's default
|
||||
// offer, not the leaf's narrower setting.
|
||||
// Default legacyAlgorithms to true so NetMesh can connect to older
|
||||
// network devices (Huawei, H3C, Cisco, etc.) that only support
|
||||
// legacy KEX algorithms like diffie-hellman-group1-sha1. Modern
|
||||
// devices still negotiate the strongest mutually supported algorithm
|
||||
// first — offering legacy algorithms alongside modern ones does not
|
||||
// weaken security on capable servers.
|
||||
algorithms: buildAlgorithms(
|
||||
jump.legacyAlgorithms ?? options.legacyAlgorithms,
|
||||
(jump.legacyAlgorithms ?? options.legacyAlgorithms) !== false,
|
||||
{
|
||||
skipEcdsaHostKey: jump.skipEcdsaHostKey,
|
||||
algorithmOverrides: jump.algorithmOverrides,
|
||||
@@ -607,7 +613,7 @@ async function connectThroughChain(event, options, jumpHosts, targetHost, target
|
||||
logSshAlgorithms("Jump host", connOpts.algorithms, {
|
||||
hostname: jump.hostname,
|
||||
port: jump.port || 22,
|
||||
legacyAlgorithms: !!(jump.legacyAlgorithms ?? options.legacyAlgorithms),
|
||||
legacyAlgorithms: ((jump.legacyAlgorithms ?? options.legacyAlgorithms) !== false),
|
||||
skipEcdsaHostKey: !!jump.skipEcdsaHostKey,
|
||||
hasAlgorithmOverrides: !!jump.algorithmOverrides,
|
||||
}, sshDiagnosticLogger);
|
||||
|
||||
@@ -135,7 +135,7 @@ function createExecCommandApi(ctx) {
|
||||
// KEX / cipher / host-key set as the interactive terminal. Without
|
||||
// this, a host that needs the ECDSA skip or legacy algorithms would
|
||||
// connect in the terminal but still fail the same handshake here.
|
||||
algorithms: buildAlgorithms(payload.legacyAlgorithms, {
|
||||
algorithms: buildAlgorithms(payload.legacyAlgorithms !== false, {
|
||||
skipEcdsaHostKey: payload.skipEcdsaHostKey,
|
||||
algorithmOverrides: payload.algorithmOverrides,
|
||||
}),
|
||||
|
||||
@@ -362,7 +362,7 @@ function createStartSessionApi(ctx) {
|
||||
authMethod: options.authMethod || undefined,
|
||||
knownHosts: Array.isArray(options.knownHosts) ? options.knownHosts : undefined,
|
||||
verifyHostKeys: options.verifyHostKeys !== false,
|
||||
legacyAlgorithms: !!options.legacyAlgorithms,
|
||||
legacyAlgorithms: options.legacyAlgorithms !== false,
|
||||
skipEcdsaHostKey: !!options.skipEcdsaHostKey,
|
||||
algorithmOverrides: options.algorithmOverrides,
|
||||
useSshAgent: options.useSshAgent,
|
||||
@@ -1470,7 +1470,11 @@ function createStartSessionApi(ctx) {
|
||||
keepaliveCountMax: keepalivePolicy.keepaliveCountMax,
|
||||
// Enable keyboard-interactive authentication (required for 2FA/MFA)
|
||||
tryKeyboard: true,
|
||||
algorithms: buildAlgorithms(options.legacyAlgorithms, {
|
||||
// Default legacyAlgorithms to true so older network devices (Huawei,
|
||||
// H3C, Cisco, etc.) that only support legacy KEX algorithms like
|
||||
// diffie-hellman-group1-sha1 can still connect. Modern devices still
|
||||
// negotiate the strongest mutually supported algorithm first.
|
||||
algorithms: buildAlgorithms(options.legacyAlgorithms !== false, {
|
||||
skipEcdsaHostKey: options.skipEcdsaHostKey,
|
||||
algorithmOverrides: options.algorithmOverrides,
|
||||
}),
|
||||
@@ -1479,7 +1483,7 @@ function createStartSessionApi(ctx) {
|
||||
logSshAlgorithms("Target host", connectOpts.algorithms, {
|
||||
hostname: options.hostname,
|
||||
port: options.port || 22,
|
||||
legacyAlgorithms: !!options.legacyAlgorithms,
|
||||
legacyAlgorithms: options.legacyAlgorithms !== false,
|
||||
skipEcdsaHostKey: !!options.skipEcdsaHostKey,
|
||||
hasAlgorithmOverrides: !!options.algorithmOverrides,
|
||||
}, log);
|
||||
|
||||
@@ -18,6 +18,14 @@ if (process.env.ELECTRON_RUN_AS_NODE) {
|
||||
delete process.env.ELECTRON_RUN_AS_NODE;
|
||||
}
|
||||
|
||||
// BoringSSL DH compatibility shim — MUST be installed before ANY ssh2 require.
|
||||
// Multiple bridges (sshBridge, sftpBridge, hostKeyVerifier, privateKeyNormalizer,
|
||||
// sshAuthHelper, portForwardingBridge, inspectionExec, systemSshAgent...) each require
|
||||
// ssh2 at module load. ssh2 destructes crypto.createDiffieHellmanGroup at load time,
|
||||
// so the monkey-patch has to run before even the first ssh2 module load.
|
||||
// Installing here (top of main, before any bridge requires) guarantees ordering.
|
||||
require("./bridges/boringSslDhCompat.cjs").installBoringSslDhCompat();
|
||||
|
||||
// Load crash log bridge early so process-level error handlers can use it
|
||||
const crashLogBridge = require("./bridges/crashLogBridge.cjs");
|
||||
const {
|
||||
@@ -267,6 +275,9 @@ const { applyPortableDataDirectory } = require("./portableData.cjs");
|
||||
const portableData = applyPortableDataDirectory({ app });
|
||||
if (portableData) {
|
||||
console.info(`[Main] Portable data directory: ${portableData.dataDirectory}`);
|
||||
// portable 版 safeStorage DPAPI 绑定 userData 路径,解不开 dev 版 LS 加密 blob。
|
||||
// 设置环境变量让 useVaultState 跳过 LS hosts_v1,直接从 inspection-data.json 读明文。
|
||||
process.env.NETCATTY_PORTABLE = "1";
|
||||
}
|
||||
const appPath = path.join(__dirname, "..");
|
||||
const preload = path.join(__dirname, "preload.cjs");
|
||||
|
||||
@@ -945,7 +945,12 @@ function isTrustedRendererLocation(allowedOrigins) {
|
||||
|
||||
const allowedOrigins = getAllowedRendererOrigins();
|
||||
if (isTrustedRendererLocation(allowedOrigins)) {
|
||||
contextBridge.exposeInMainWorld("netcatty", { ...existing, ...api, ...figSpecApi });
|
||||
contextBridge.exposeInMainWorld("netcatty", {
|
||||
...existing, ...api, ...figSpecApi,
|
||||
// portable 版标志:renderer 据此跳过 LS hosts_v1 加密读取,
|
||||
// 直接走 inspection-data.json 明文(DPAPI 绑定 userData 路径解不开 dev 加密 blob)。
|
||||
isPortable: process.env.NETCATTY_PORTABLE === "1",
|
||||
});
|
||||
} else {
|
||||
// If a window navigates to an untrusted origin, do NOT expose the bridge.
|
||||
try {
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
"micromark-extension-gfm-strikethrough": "^2.1.0",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"node-pty": "^1.1.0",
|
||||
"pend": "^1.2.0",
|
||||
"pinyin-pro": "^3.28.1",
|
||||
"re2js": "2.8.6",
|
||||
"react": "^19.2.1",
|
||||
|
||||
Reference in New Issue
Block a user