mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 19:47:08 +08:00
feat: 支持 Windows WCDB 解密与打包
This commit is contained in:
@@ -21,8 +21,13 @@ extraResources:
|
||||
filter:
|
||||
- '**/*'
|
||||
win:
|
||||
executableName: wechatexplorer
|
||||
# WCDB's Windows runtime checks the host executable name. The dev runtime is
|
||||
# electron.exe, so keep the packaged executable compatible while preserving
|
||||
# WechatExplorer as the product/shortcut name.
|
||||
executableName: electron
|
||||
nsis:
|
||||
oneClick: false
|
||||
allowToChangeInstallationDirectory: true
|
||||
artifactName: ${name}-${version}-setup.${ext}
|
||||
shortcutName: ${productName}
|
||||
uninstallDisplayName: ${productName}
|
||||
|
||||
+13
-1
@@ -3,7 +3,19 @@ import { defineConfig } from 'electron-vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
main: {},
|
||||
main: {
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
index: resolve('src/main/index.ts')
|
||||
},
|
||||
output: {
|
||||
entryFileNames: '[name].js'
|
||||
},
|
||||
external: ['koffi']
|
||||
}
|
||||
}
|
||||
},
|
||||
preload: {},
|
||||
renderer: {
|
||||
resolve: {
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "npm run typecheck && electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"postinstall": "electron-builder install-app-deps && node scripts/prepare-electron-runtime.cjs",
|
||||
"build:unpack": "npm run build && electron-builder --config electron-builder.yml --dir",
|
||||
"build:win": "npm run build && electron-builder --config electron-builder.yml --win",
|
||||
"build:mac:x64": "electron-vite build && electron-builder --config electron-builder.yml --mac --x64",
|
||||
@@ -33,7 +33,7 @@
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"fs-extra": "^11.3.2",
|
||||
"fzstd": "^0.1.1",
|
||||
"koffi": "^2.9.0",
|
||||
"koffi": "^3.1.0",
|
||||
"openai": "^6.10.0",
|
||||
"silk-wasm": "^3.7.1"
|
||||
},
|
||||
@@ -47,7 +47,7 @@
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"electron": "^39.2.6",
|
||||
"electron": "^43.0.0",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-vite": "^5.0.0",
|
||||
"eslint": "^9.39.1",
|
||||
|
||||
Generated
+201
-148
@@ -16,7 +16,7 @@ specifiers:
|
||||
'@types/react': ^19.2.7
|
||||
'@types/react-dom': ^19.2.3
|
||||
'@vitejs/plugin-react': ^5.1.1
|
||||
electron: ^39.2.6
|
||||
electron: ^43.0.0
|
||||
electron-builder: ^26.0.12
|
||||
electron-vite: ^5.0.0
|
||||
eslint: ^9.39.1
|
||||
@@ -25,7 +25,7 @@ specifiers:
|
||||
eslint-plugin-react-refresh: ^0.4.24
|
||||
fs-extra: ^11.3.2
|
||||
fzstd: ^0.1.1
|
||||
koffi: ^2.9.0
|
||||
koffi: ^3.1.0
|
||||
openai: ^6.10.0
|
||||
prettier: ^3.7.4
|
||||
react: ^19.2.1
|
||||
@@ -35,11 +35,11 @@ specifiers:
|
||||
vite: ^7.2.6
|
||||
|
||||
dependencies:
|
||||
'@electron-toolkit/preload': 3.0.2_electron@39.2.6
|
||||
'@electron-toolkit/utils': 4.0.0_electron@39.2.6
|
||||
'@electron-toolkit/preload': 3.0.2_electron@43.1.0
|
||||
'@electron-toolkit/utils': 4.0.0_electron@43.1.0
|
||||
fs-extra: 11.3.2
|
||||
fzstd: 0.1.1
|
||||
koffi: 2.16.2
|
||||
koffi: 3.1.0
|
||||
openai: 6.10.0
|
||||
silk-wasm: 3.7.1
|
||||
|
||||
@@ -53,7 +53,7 @@ devDependencies:
|
||||
'@types/react': 19.2.7
|
||||
'@types/react-dom': 19.2.3_@types+react@19.2.7
|
||||
'@vitejs/plugin-react': 5.1.2_vite@7.2.7
|
||||
electron: 39.2.6
|
||||
electron: 43.1.0
|
||||
electron-builder: 26.0.12
|
||||
electron-vite: 5.0.0_vite@7.2.7
|
||||
eslint: 9.39.1
|
||||
@@ -266,6 +266,10 @@ packages:
|
||||
ajv-keywords: 3.5.2_ajv@6.12.6
|
||||
dev: true
|
||||
|
||||
/@electron-internal/extract-zip/1.0.4:
|
||||
resolution: {integrity: sha512-Zr1Vs7E9tpCNhZHDAbFVXc2gEVCG9RqPDjrno5+bdgB6LRAuvgyMHJut4NCVyYwtAieapMzc3fiQ3CSTi75ARg==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
|
||||
/@electron-toolkit/eslint-config-prettier/3.0.0_fiitszekoa4sqtbwyewxi6kyy4:
|
||||
resolution: {integrity: sha512-YapmIOVkbYdHLuTa+ad1SAVtcqYL9A/SJsc7cxQokmhcwAwonGevNom37jBf9slXegcZ/Slh01I/JARG1yhNFw==}
|
||||
peerDependencies:
|
||||
@@ -298,12 +302,12 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@electron-toolkit/preload/3.0.2_electron@39.2.6:
|
||||
/@electron-toolkit/preload/3.0.2_electron@43.1.0:
|
||||
resolution: {integrity: sha512-TWWPToXd8qPRfSXwzf5KVhpXMfONaUuRAZJHsKthKgZR/+LqX1dZVSSClQ8OTAEduvLGdecljCsoT2jSshfoUg==}
|
||||
peerDependencies:
|
||||
electron: '>=13.0.0'
|
||||
dependencies:
|
||||
electron: 39.2.6
|
||||
electron: 43.1.0
|
||||
dev: false
|
||||
|
||||
/@electron-toolkit/tsconfig/2.0.0_@types+node@22.19.2:
|
||||
@@ -314,12 +318,12 @@ packages:
|
||||
'@types/node': 22.19.2
|
||||
dev: true
|
||||
|
||||
/@electron-toolkit/utils/4.0.0_electron@39.2.6:
|
||||
/@electron-toolkit/utils/4.0.0_electron@43.1.0:
|
||||
resolution: {integrity: sha512-qXSntwEzluSzKl4z5yFNBknmPGjPa3zFhE4mp9+h0cgokY5ornAeP+CJQDBhKsL1S58aOQfcwkD3NwLZCl+64g==}
|
||||
peerDependencies:
|
||||
electron: '>=13.0.0'
|
||||
dependencies:
|
||||
electron: 39.2.6
|
||||
electron: 43.1.0
|
||||
dev: false
|
||||
|
||||
/@electron/asar/3.2.18:
|
||||
@@ -341,19 +345,18 @@ packages:
|
||||
minimist: 1.2.8
|
||||
dev: true
|
||||
|
||||
/@electron/get/2.0.3:
|
||||
resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==}
|
||||
engines: {node: '>=12'}
|
||||
/@electron/get/5.0.0:
|
||||
resolution: {integrity: sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
env-paths: 2.2.1
|
||||
fs-extra: 8.1.0
|
||||
got: 11.8.6
|
||||
env-paths: 3.0.0
|
||||
graceful-fs: 4.2.11
|
||||
progress: 2.0.3
|
||||
semver: 6.3.1
|
||||
semver: 7.7.3
|
||||
sumchecker: 3.0.1
|
||||
optionalDependencies:
|
||||
global-agent: 3.0.0
|
||||
undici: 7.28.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -786,6 +789,104 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
dev: true
|
||||
|
||||
/@koromix/koffi-darwin-arm64/3.1.0:
|
||||
resolution: {integrity: sha512-VEt5r3fXTfbejr83PnuOP0H7s9Zmazcs+lofu96DOcRkistlMsn59wYyWiKpyAjs9PCgm0Ykh62ChZ3CGMmIOg==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-darwin-x64/3.1.0:
|
||||
resolution: {integrity: sha512-n/tVRB9xIzdXT5H3zZt8ueThgWTSDL+yU7PWnU8wbZPBSawP/otx3swQyd6nMOqj1bmHgSHopiKSBXRS9pllmg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-freebsd-arm64/3.1.0:
|
||||
resolution: {integrity: sha512-vazoPYIhOAlXZksVIqDRMIID4VeUZKx8F3dR90hOobT2ATyOkqNS5dv5UCV7Q7DSq22lQTrdbvENBAhROzCp0w==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-freebsd-ia32/3.1.0:
|
||||
resolution: {integrity: sha512-Vm7Uc97ru6RTSVmae2zCZZQeaizqVZ8WoU4+gG4H03Qe+WOj7kbKt/MxT7VBzdbPYIU5ZJeG/ZED1YlZyab6eQ==}
|
||||
cpu: [ia32]
|
||||
os: [freebsd]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-freebsd-x64/3.1.0:
|
||||
resolution: {integrity: sha512-N+VuVWjoiYPy1Go5mRadZ3B6RM5Qz+eCLhj2LXrMlefbUJ+O4gg7teCUGvPGfBEHDgmSN4yYUrfQmdJC10vOYw==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-linux-arm64/3.1.0:
|
||||
resolution: {integrity: sha512-Wx5iOkeALe2ympLdiYwRpIg5qUkyQIv8N2foZ9rRker0uE7ZtXew2RRkbEgMir4b0yDYR1zyXd6B62GUzLtZ/g==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-linux-ia32/3.1.0:
|
||||
resolution: {integrity: sha512-1DjYm1QehXU0dgn0uE+FGYOb3Of7GiTMqLS+ZI2gbl1b+h76sz4LRBvDVrQyAmSMVVU8/7696S21YgE/iBhBVg==}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-linux-loong64/3.1.0:
|
||||
resolution: {integrity: sha512-NOa0LdyltdESz3oeTqUH6MErHVoJOHoeXIsEp6xIMTUh4eKXEtlDQeoK6EYqo0DnBt83Xud95qLvi4Aw12pG4Q==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-linux-riscv64/3.1.0:
|
||||
resolution: {integrity: sha512-Ye6kiXZCGxGtAIXSly6XuOP5tJZNYOZ2eVg33k1MilKrzimAy9Mpw4d6e9+Sfsc1jesgeNYs1sb5iaI8HS3ncA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-linux-x64/3.1.0:
|
||||
resolution: {integrity: sha512-3yQTOkQrMna4VX+yeyfYImBjLlGrItMpsWyfaW1uSiz/A6GRydqdwYH7DWnp4Z+RSGYZpsewkf7byMc8pOOQKA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-openbsd-ia32/3.1.0:
|
||||
resolution: {integrity: sha512-/cDoFHb9yx4+yoT3GUpnKnfi3W2drG+/Ewo0TTZaQHb4PsxnYYyT6V8+t4cL5XXbQcTTcOsZxpmBRrn0NBa3dA==}
|
||||
cpu: [ia32]
|
||||
os: [openbsd]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-openbsd-x64/3.1.0:
|
||||
resolution: {integrity: sha512-CoQdqgnKvWgTXXZlUst8cBRQEov7QsxlTN2WAsu9wez01Xe6gEcH/zYePANualzzCbnaELfe5P0rA80QkoDuPA==}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-win32-ia32/3.1.0:
|
||||
resolution: {integrity: sha512-WjrA+DEkpy0xEHu48+NSOboHhTnzkIfsFuq3d/WrSs+T9WflWRng3jC7mdJxmR4eHb6i6BqjW3k/U0mNUTjFPA==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@koromix/koffi-win32-x64/3.1.0:
|
||||
resolution: {integrity: sha512-tnK5+IkzQBauQAQSzuyjso8OOIQRlaTZS39xIWpfqVYDLVDIuLDQk/WwHcOrR5yxlDrZq9ygiebBTOfcJFia7w==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@malept/cross-spawn-promise/2.0.0:
|
||||
resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
@@ -1011,12 +1112,14 @@ packages:
|
||||
/@sindresorhus/is/4.6.0:
|
||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/@szmarczak/http-timer/4.0.6:
|
||||
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
defer-to-connect: 2.0.1
|
||||
dev: true
|
||||
|
||||
/@tootallnate/once/2.0.0:
|
||||
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
|
||||
@@ -1059,6 +1162,7 @@ packages:
|
||||
'@types/keyv': 3.1.4
|
||||
'@types/node': 22.19.2
|
||||
'@types/responselike': 1.0.3
|
||||
dev: true
|
||||
|
||||
/@types/debug/4.1.12:
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
@@ -1085,6 +1189,7 @@ packages:
|
||||
|
||||
/@types/http-cache-semantics/4.0.4:
|
||||
resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
|
||||
dev: true
|
||||
|
||||
/@types/json-schema/7.0.15:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
@@ -1100,6 +1205,7 @@ packages:
|
||||
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
||||
dependencies:
|
||||
'@types/node': 22.19.2
|
||||
dev: true
|
||||
|
||||
/@types/ms/2.1.0:
|
||||
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||
@@ -1109,6 +1215,12 @@ packages:
|
||||
resolution: {integrity: sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==}
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
dev: true
|
||||
|
||||
/@types/node/24.13.3:
|
||||
resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==}
|
||||
dependencies:
|
||||
undici-types: 7.18.2
|
||||
|
||||
/@types/plist/3.0.5:
|
||||
resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==}
|
||||
@@ -1136,18 +1248,13 @@ packages:
|
||||
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
|
||||
dependencies:
|
||||
'@types/node': 22.19.2
|
||||
dev: true
|
||||
|
||||
/@types/verror/1.10.11:
|
||||
resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==}
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@types/yauzl/2.10.3:
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
dependencies:
|
||||
'@types/node': 22.19.2
|
||||
optional: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/8.49.0_nj5tjtfh637kziiwuhl3v5a4iq:
|
||||
resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@@ -1591,11 +1698,6 @@ packages:
|
||||
readable-stream: 3.6.2
|
||||
dev: true
|
||||
|
||||
/boolean/3.2.0:
|
||||
resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==}
|
||||
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
||||
optional: true
|
||||
|
||||
/brace-expansion/1.1.12:
|
||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||
dependencies:
|
||||
@@ -1621,9 +1723,6 @@ packages:
|
||||
update-browserslist-db: 1.2.2_browserslist@4.28.1
|
||||
dev: true
|
||||
|
||||
/buffer-crc32/0.2.13:
|
||||
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
|
||||
|
||||
/buffer-from/1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
dev: true
|
||||
@@ -1703,6 +1802,7 @@ packages:
|
||||
/cacheable-lookup/5.0.4:
|
||||
resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
|
||||
engines: {node: '>=10.6.0'}
|
||||
dev: true
|
||||
|
||||
/cacheable-request/7.0.4:
|
||||
resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
|
||||
@@ -1715,6 +1815,7 @@ packages:
|
||||
lowercase-keys: 2.0.0
|
||||
normalize-url: 6.1.0
|
||||
responselike: 2.0.1
|
||||
dev: true
|
||||
|
||||
/call-bind-apply-helpers/1.0.2:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
@@ -1812,6 +1913,7 @@ packages:
|
||||
resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
|
||||
dependencies:
|
||||
mimic-response: 1.0.1
|
||||
dev: true
|
||||
|
||||
/clone/1.0.4:
|
||||
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
||||
@@ -1929,6 +2031,7 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
mimic-response: 3.1.0
|
||||
dev: true
|
||||
|
||||
/deep-is/0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
@@ -1943,6 +2046,7 @@ packages:
|
||||
/defer-to-connect/2.0.1:
|
||||
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/define-data-property/1.1.4:
|
||||
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
|
||||
@@ -1951,6 +2055,7 @@ packages:
|
||||
es-define-property: 1.0.1
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
dev: true
|
||||
|
||||
/define-properties/1.2.1:
|
||||
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
|
||||
@@ -1959,6 +2064,7 @@ packages:
|
||||
define-data-property: 1.1.4
|
||||
has-property-descriptors: 1.0.2
|
||||
object-keys: 1.1.1
|
||||
dev: true
|
||||
|
||||
/delayed-stream/1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
@@ -1970,10 +2076,6 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/detect-node/2.1.0:
|
||||
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
|
||||
optional: true
|
||||
|
||||
/dir-compare/4.2.0:
|
||||
resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==}
|
||||
dependencies:
|
||||
@@ -2117,15 +2219,14 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/electron/39.2.6:
|
||||
resolution: {integrity: sha512-dHBgTodWBZd+tL1Dt0PSh/CFLHeDkFCTKCTXu1dgPhlE9Z3k2zzlBQ9B2oW55CFsKanBDHiUomHJNw0XaSdQpA==}
|
||||
engines: {node: '>= 12.20.55'}
|
||||
/electron/43.1.0:
|
||||
resolution: {integrity: sha512-DPfxpQLd4NL3BJ8DBxYAfmLUKKesF5Rx9dQx5FyczAP8bhOPScjHE48GArVeXu68LlAainuwkmQTQvdZwpIIAQ==}
|
||||
engines: {node: '>= 22.12.0'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@electron/get': 2.0.3
|
||||
'@types/node': 22.19.2
|
||||
extract-zip: 2.0.1
|
||||
'@electron-internal/extract-zip': 1.0.4
|
||||
'@electron/get': 5.0.0
|
||||
'@types/node': 24.13.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -2148,10 +2249,16 @@ packages:
|
||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
dev: true
|
||||
|
||||
/env-paths/2.2.1:
|
||||
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/env-paths/3.0.0:
|
||||
resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
/err-code/2.0.3:
|
||||
resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
|
||||
@@ -2220,10 +2327,12 @@ packages:
|
||||
/es-define-property/1.0.1:
|
||||
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/es-errors/1.3.0:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/es-iterator-helpers/1.2.1:
|
||||
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
|
||||
@@ -2280,10 +2389,6 @@ packages:
|
||||
is-symbol: 1.1.1
|
||||
dev: true
|
||||
|
||||
/es6-error/4.1.1:
|
||||
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
|
||||
optional: true
|
||||
|
||||
/esbuild/0.25.12:
|
||||
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -2326,6 +2431,7 @@ packages:
|
||||
/escape-string-regexp/4.0.0:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/eslint-config-prettier/10.1.8_eslint@9.39.1:
|
||||
resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==}
|
||||
@@ -2511,19 +2617,6 @@ packages:
|
||||
resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==}
|
||||
dev: true
|
||||
|
||||
/extract-zip/2.0.1:
|
||||
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
|
||||
engines: {node: '>= 10.17.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
get-stream: 5.2.0
|
||||
yauzl: 2.10.0
|
||||
optionalDependencies:
|
||||
'@types/yauzl': 2.10.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/extsprintf/1.4.1:
|
||||
resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==}
|
||||
engines: {'0': node >=0.6.0}
|
||||
@@ -2546,11 +2639,6 @@ packages:
|
||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||
dev: true
|
||||
|
||||
/fd-slicer/1.1.0:
|
||||
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
|
||||
dependencies:
|
||||
pend: 1.2.0
|
||||
|
||||
/fdir/6.5.0_picomatch@4.0.3:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -2639,14 +2727,6 @@ packages:
|
||||
jsonfile: 6.2.0
|
||||
universalify: 2.0.1
|
||||
|
||||
/fs-extra/8.1.0:
|
||||
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
|
||||
engines: {node: '>=6 <7 || >=8'}
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
jsonfile: 4.0.0
|
||||
universalify: 0.1.2
|
||||
|
||||
/fs-extra/9.1.0:
|
||||
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -2743,6 +2823,7 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
pump: 3.0.3
|
||||
dev: true
|
||||
|
||||
/get-symbol-description/1.1.0:
|
||||
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
|
||||
@@ -2796,18 +2877,6 @@ packages:
|
||||
once: 1.4.0
|
||||
dev: true
|
||||
|
||||
/global-agent/3.0.0:
|
||||
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
|
||||
engines: {node: '>=10.0'}
|
||||
dependencies:
|
||||
boolean: 3.2.0
|
||||
es6-error: 4.1.1
|
||||
matcher: 3.0.0
|
||||
roarr: 2.15.4
|
||||
semver: 7.7.3
|
||||
serialize-error: 7.0.1
|
||||
optional: true
|
||||
|
||||
/globals/14.0.0:
|
||||
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -2824,10 +2893,12 @@ packages:
|
||||
dependencies:
|
||||
define-properties: 1.2.1
|
||||
gopd: 1.2.0
|
||||
dev: true
|
||||
|
||||
/gopd/1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/got/11.8.6:
|
||||
resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
|
||||
@@ -2844,6 +2915,7 @@ packages:
|
||||
lowercase-keys: 2.0.0
|
||||
p-cancelable: 2.1.1
|
||||
responselike: 2.0.1
|
||||
dev: true
|
||||
|
||||
/graceful-fs/4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
@@ -2862,6 +2934,7 @@ packages:
|
||||
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
|
||||
dependencies:
|
||||
es-define-property: 1.0.1
|
||||
dev: true
|
||||
|
||||
/has-proto/1.2.0:
|
||||
resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
|
||||
@@ -2908,6 +2981,7 @@ packages:
|
||||
|
||||
/http-cache-semantics/4.2.0:
|
||||
resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==}
|
||||
dev: true
|
||||
|
||||
/http-proxy-agent/5.0.0:
|
||||
resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
|
||||
@@ -2936,6 +3010,7 @@ packages:
|
||||
dependencies:
|
||||
quick-lru: 5.1.1
|
||||
resolve-alpn: 1.2.1
|
||||
dev: true
|
||||
|
||||
/https-proxy-agent/5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
@@ -3313,6 +3388,7 @@ packages:
|
||||
|
||||
/json-buffer/3.0.1:
|
||||
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||
dev: true
|
||||
|
||||
/json-schema-traverse/0.4.1:
|
||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||
@@ -3322,21 +3398,12 @@ packages:
|
||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||
dev: true
|
||||
|
||||
/json-stringify-safe/5.0.1:
|
||||
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
||||
optional: true
|
||||
|
||||
/json5/2.2.3:
|
||||
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/jsonfile/4.0.0:
|
||||
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
/jsonfile/6.2.0:
|
||||
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
|
||||
dependencies:
|
||||
@@ -3358,9 +3425,25 @@ packages:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
dev: true
|
||||
|
||||
/koffi/2.16.2:
|
||||
resolution: {integrity: sha512-owU0MRwv6xkrVqCd+33uw6BaYppkTRXbO/rVdJNI2dvZG0gzyRhYwW25eWtc5pauwK8TGh3AbkFONSezdykfSA==}
|
||||
/koffi/3.1.0:
|
||||
resolution: {integrity: sha512-0mCvdjTJBXioiaKNz0vajAEdWtfM5qyhVXSq+wQrrU3odzNvl/J7Cqna79QpNo9mfoKpQgGsyFFDRtDACCwGrQ==}
|
||||
optionalDependencies:
|
||||
'@koromix/koffi-darwin-arm64': 3.1.0
|
||||
'@koromix/koffi-darwin-x64': 3.1.0
|
||||
'@koromix/koffi-freebsd-arm64': 3.1.0
|
||||
'@koromix/koffi-freebsd-ia32': 3.1.0
|
||||
'@koromix/koffi-freebsd-x64': 3.1.0
|
||||
'@koromix/koffi-linux-arm64': 3.1.0
|
||||
'@koromix/koffi-linux-ia32': 3.1.0
|
||||
'@koromix/koffi-linux-loong64': 3.1.0
|
||||
'@koromix/koffi-linux-riscv64': 3.1.0
|
||||
'@koromix/koffi-linux-x64': 3.1.0
|
||||
'@koromix/koffi-openbsd-ia32': 3.1.0
|
||||
'@koromix/koffi-openbsd-x64': 3.1.0
|
||||
'@koromix/koffi-win32-ia32': 3.1.0
|
||||
'@koromix/koffi-win32-x64': 3.1.0
|
||||
dev: false
|
||||
|
||||
/lazy-val/1.0.5:
|
||||
@@ -3408,6 +3491,7 @@ packages:
|
||||
/lowercase-keys/2.0.0:
|
||||
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/lru-cache/10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
@@ -3462,13 +3546,6 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/matcher/3.0.0:
|
||||
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
escape-string-regexp: 4.0.0
|
||||
optional: true
|
||||
|
||||
/math-intrinsics/1.1.0:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3500,10 +3577,12 @@ packages:
|
||||
/mimic-response/1.0.1:
|
||||
resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/mimic-response/3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/minimatch/10.1.1:
|
||||
resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
|
||||
@@ -3657,6 +3736,7 @@ packages:
|
||||
/normalize-url/6.1.0:
|
||||
resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/object-assign/4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
@@ -3671,6 +3751,7 @@ packages:
|
||||
/object-keys/1.1.1:
|
||||
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/object.assign/4.1.7:
|
||||
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
|
||||
@@ -3718,6 +3799,7 @@ packages:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
dev: true
|
||||
|
||||
/onetime/5.1.2:
|
||||
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||
@@ -3778,6 +3860,7 @@ packages:
|
||||
/p-cancelable/2.1.1:
|
||||
resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/p-limit/3.1.0:
|
||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||
@@ -3843,9 +3926,6 @@ packages:
|
||||
engines: {node: '>=12', npm: '>=6'}
|
||||
dev: true
|
||||
|
||||
/pend/1.2.0:
|
||||
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
|
||||
|
||||
/picocolors/1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
dev: true
|
||||
@@ -3935,6 +4015,7 @@ packages:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.5
|
||||
once: 1.4.0
|
||||
dev: true
|
||||
|
||||
/punycode/2.3.1:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
@@ -3944,6 +4025,7 @@ packages:
|
||||
/quick-lru/5.1.1:
|
||||
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/react-dom/19.2.1_react@19.2.1:
|
||||
resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==}
|
||||
@@ -4026,6 +4108,7 @@ packages:
|
||||
|
||||
/resolve-alpn/1.2.1:
|
||||
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
||||
dev: true
|
||||
|
||||
/resolve-from/4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||
@@ -4045,6 +4128,7 @@ packages:
|
||||
resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
|
||||
dependencies:
|
||||
lowercase-keys: 2.0.0
|
||||
dev: true
|
||||
|
||||
/restore-cursor/3.1.0:
|
||||
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
||||
@@ -4067,18 +4151,6 @@ packages:
|
||||
glob: 7.2.3
|
||||
dev: true
|
||||
|
||||
/roarr/2.15.4:
|
||||
resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
|
||||
engines: {node: '>=8.0'}
|
||||
dependencies:
|
||||
boolean: 3.2.0
|
||||
detect-node: 2.1.0
|
||||
globalthis: 1.0.4
|
||||
json-stringify-safe: 5.0.1
|
||||
semver-compare: 1.0.0
|
||||
sprintf-js: 1.1.3
|
||||
optional: true
|
||||
|
||||
/rollup/4.53.3:
|
||||
resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
@@ -4161,10 +4233,6 @@ packages:
|
||||
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
|
||||
dev: true
|
||||
|
||||
/semver-compare/1.0.0:
|
||||
resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
|
||||
optional: true
|
||||
|
||||
/semver/5.7.2:
|
||||
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
|
||||
hasBin: true
|
||||
@@ -4173,19 +4241,13 @@ packages:
|
||||
/semver/6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/semver/7.7.3:
|
||||
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
/serialize-error/7.0.1:
|
||||
resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
type-fest: 0.13.1
|
||||
optional: true
|
||||
|
||||
/set-function-length/1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4341,10 +4403,6 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/sprintf-js/1.1.3:
|
||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||
optional: true
|
||||
|
||||
/ssri/9.0.1:
|
||||
resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==}
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
@@ -4559,11 +4617,6 @@ packages:
|
||||
prelude-ls: 1.2.1
|
||||
dev: true
|
||||
|
||||
/type-fest/0.13.1:
|
||||
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
|
||||
engines: {node: '>=10'}
|
||||
optional: true
|
||||
|
||||
/typed-array-buffer/1.0.3:
|
||||
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4644,6 +4697,15 @@ packages:
|
||||
|
||||
/undici-types/6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
dev: true
|
||||
|
||||
/undici-types/7.18.2:
|
||||
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
||||
|
||||
/undici/7.28.0:
|
||||
resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
optional: true
|
||||
|
||||
/unique-filename/2.0.1:
|
||||
resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==}
|
||||
@@ -4659,10 +4721,6 @@ packages:
|
||||
imurmurhash: 0.1.4
|
||||
dev: true
|
||||
|
||||
/universalify/0.1.2:
|
||||
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
||||
/universalify/2.0.1:
|
||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -4845,6 +4903,7 @@ packages:
|
||||
|
||||
/wrappy/1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
dev: true
|
||||
|
||||
/xmlbuilder/15.1.1:
|
||||
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
|
||||
@@ -4882,12 +4941,6 @@ packages:
|
||||
yargs-parser: 21.1.1
|
||||
dev: true
|
||||
|
||||
/yauzl/2.10.0:
|
||||
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
|
||||
dependencies:
|
||||
buffer-crc32: 0.2.13
|
||||
fd-slicer: 1.1.0
|
||||
|
||||
/yocto-queue/0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,48 @@
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
|
||||
const runtimeNames = [
|
||||
'msvcp140.dll',
|
||||
'msvcp140_1.dll',
|
||||
'vcruntime140.dll',
|
||||
'vcruntime140_1.dll'
|
||||
]
|
||||
|
||||
function copyIfDifferent(sourcePath, targetPath) {
|
||||
const source = fs.statSync(sourcePath)
|
||||
const targetExists = fs.existsSync(targetPath)
|
||||
|
||||
if (targetExists) {
|
||||
const target = fs.statSync(targetPath)
|
||||
if (target.size === source.size && target.mtimeMs >= source.mtimeMs) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fs.copyFileSync(sourcePath, targetPath)
|
||||
return true
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (process.platform !== 'win32') return
|
||||
|
||||
const projectRoot = path.resolve(__dirname, '..')
|
||||
const sourceDir = path.join(projectRoot, 'resources', 'runtime', 'win32')
|
||||
const targetDir = path.join(projectRoot, 'node_modules', 'electron', 'dist')
|
||||
|
||||
if (!fs.existsSync(sourceDir) || !fs.existsSync(targetDir)) return
|
||||
|
||||
let copiedCount = 0
|
||||
for (const name of runtimeNames) {
|
||||
const sourcePath = path.join(sourceDir, name)
|
||||
const targetPath = path.join(targetDir, name)
|
||||
if (!fs.existsSync(sourcePath)) continue
|
||||
if (copyIfDifferent(sourcePath, targetPath)) copiedCount += 1
|
||||
}
|
||||
|
||||
if (copiedCount > 0) {
|
||||
console.log(`[prepare-electron-runtime] synced ${copiedCount} runtime DLL(s) to ${targetDir}`)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
+53
-7
@@ -1,8 +1,10 @@
|
||||
import './preload-env'
|
||||
import { app, shell, BrowserWindow, ipcMain, nativeImage, clipboard, Menu, Tray } from 'electron'
|
||||
import { join } from 'path'
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
import { WechatDb } from './wechat-db'
|
||||
import { bootstrapWcdbNative } from './wcdb4-client'
|
||||
import { VoiceService } from './voice-service'
|
||||
import { StickerService } from './sticker-service'
|
||||
import { parseMessageContent } from './message-parser'
|
||||
@@ -11,6 +13,7 @@ import { exportGroupReport } from './group-report-service'
|
||||
import { GroupReportExportRequest } from '../shared/group-report'
|
||||
import { DatabaseKeyStore } from './database-key-store'
|
||||
import { KeyServiceMac } from './key-service-mac'
|
||||
import { KeyService as KeyServiceWin } from './key-service-win'
|
||||
import * as chat from './services/chat-service'
|
||||
import {
|
||||
apiServer
|
||||
@@ -33,14 +36,17 @@ let imageDecryptService: ImageDecryptService | null = null
|
||||
let stickerService: StickerService | null = null
|
||||
const databaseKeyStore = new DatabaseKeyStore()
|
||||
const keyServiceMac = new KeyServiceMac()
|
||||
const keyServiceWin = new KeyServiceWin()
|
||||
let tray: Tray | null = null
|
||||
|
||||
// WCDB's Windows runtime checks the host application name during wcdb_init.
|
||||
// Mirroring WeFlow's name unblocks the -1006 init failure on Windows.
|
||||
app.setName(process.platform === 'win32' ? 'WeFlow' : 'WechatExplorer')
|
||||
let dbInitInFlight: Promise<{ success: boolean; monitoring?: boolean; error?: string }> | null = null
|
||||
const BUILD_MARK = 'wechat4-local-http-api-2026-07-03'
|
||||
const TRAY_MODE =
|
||||
process.argv.includes('--tray') || (process.env['WXE_TRAY'] || '').toString() === '1'
|
||||
|
||||
// WechatExplorer's WCDB native library runs InitProtection before wcdb_init.
|
||||
// In dev, matching the host app name avoids failing the native protection gate.
|
||||
app.setName('WechatExplorer')
|
||||
|
||||
function createWindow(): void {
|
||||
// 创建浏览器窗口
|
||||
@@ -78,6 +84,17 @@ function createWindow(): void {
|
||||
// 某些 API 只能在此事件发生后使用
|
||||
app.whenReady().then(async () => {
|
||||
console.log(`WechatExplorer main build: ${BUILD_MARK}`)
|
||||
|
||||
// WCDB's Windows runtime returns -1006 if wcdb_init is called more than once
|
||||
// per process. Bootstrap native once here so any later Wcdb4Client instance
|
||||
// reuses the already-initialized library and skips wcdb_init.
|
||||
try {
|
||||
bootstrapWcdbNative()
|
||||
console.log('[WCDB4] bootstrap complete at whenReady top')
|
||||
} catch (bootstrapError) {
|
||||
console.error('[WCDB4] bootstrap failed at whenReady top:', bootstrapError)
|
||||
}
|
||||
|
||||
// 为窗口设置应用程序用户模型 ID
|
||||
electronApp.setAppUserModelId('com.electron')
|
||||
|
||||
@@ -91,11 +108,27 @@ app.whenReady().then(async () => {
|
||||
// IPC test
|
||||
ipcMain.on('ping', () => console.log('pong'))
|
||||
|
||||
ipcMain.handle('db:init', (_, key: string) => {
|
||||
ipcMain.handle('db:init', async (_, key: string) => {
|
||||
if (dbInitInFlight) return dbInitInFlight
|
||||
|
||||
dbInitInFlight = (async () => {
|
||||
try {
|
||||
const trimmedKey = String(key || '').trim()
|
||||
console.log(`db:init build=${BUILD_MARK} keyLength=${trimmedKey.length}`)
|
||||
const nextWechatDb = new WechatDb(key)
|
||||
const settings = loadSettings()
|
||||
if (
|
||||
chat.isReady() &&
|
||||
chat.getCurrentKey().replace(/^0x/i, '').trim() === trimmedKey.replace(/^0x/i, '') &&
|
||||
(!settings.dbRoot || chat.getCurrentAccountRoot() === settings.dbRoot)
|
||||
) {
|
||||
console.log('[WCDB4] db:init reuse current connection')
|
||||
return { success: true, monitoring: true }
|
||||
}
|
||||
const nextWechatDb = await WechatDb.create(key, settings.dbRoot)
|
||||
const resolvedRoot = nextWechatDb.getWcdb4Client().getAccountRoot()
|
||||
if (resolvedRoot && resolvedRoot !== settings.dbRoot) {
|
||||
saveSettings({ ...settings, dbRoot: resolvedRoot })
|
||||
}
|
||||
chat.setChatDb(nextWechatDb)
|
||||
const wcdb4Client = nextWechatDb.getWcdb4Client()
|
||||
voiceService = new VoiceService(wcdb4Client)
|
||||
@@ -110,7 +143,12 @@ app.whenReady().then(async () => {
|
||||
} catch (error) {
|
||||
console.error('Failed to init DB:', error)
|
||||
return { success: false, error: error instanceof Error ? error.message : String(error) }
|
||||
} finally {
|
||||
dbInitInFlight = null
|
||||
}
|
||||
})()
|
||||
|
||||
return dbInitInFlight
|
||||
})
|
||||
|
||||
ipcMain.handle('key:getSavedDbKey', async () => databaseKeyStore.load())
|
||||
@@ -125,9 +163,13 @@ app.whenReady().then(async () => {
|
||||
ipcMain.handle('key:clearSavedDbKey', async () => databaseKeyStore.clear())
|
||||
|
||||
ipcMain.handle('key:autoGetDbKey', async (event) => {
|
||||
const result = await keyServiceMac.autoGetDbKey((message) => {
|
||||
const onStatus = (message: string): void => {
|
||||
if (!event.sender.isDestroyed()) event.sender.send('key:dbKeyStatus', { message })
|
||||
})
|
||||
}
|
||||
const result =
|
||||
process.platform === 'win32'
|
||||
? await keyServiceWin.autoGetDbKey(60_000, onStatus)
|
||||
: await keyServiceMac.autoGetDbKey(onStatus)
|
||||
if (!result.success || !result.key) return result
|
||||
|
||||
const saved = await databaseKeyStore.save(result.key)
|
||||
@@ -140,6 +182,10 @@ app.whenReady().then(async () => {
|
||||
|
||||
ipcMain.handle('db:getContacts', (_, filter?: string) => chat.listContacts(filter))
|
||||
|
||||
ipcMain.handle('db:getContactAvatars', (_, usernames: string[]) =>
|
||||
chat.getContactAvatars(usernames)
|
||||
)
|
||||
|
||||
ipcMain.handle('db:getMessages', (_, userMd5: string, startTime?: number, endTime?: number) =>
|
||||
chat.listMessages(userMd5, startTime, endTime)
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
function prependPath(values: string[]): void {
|
||||
if (process.platform !== 'win32') return
|
||||
|
||||
const existing = process.env.PATH || ''
|
||||
const next = Array.from(new Set(values.filter(Boolean))).join(path.delimiter)
|
||||
process.env.PATH = next ? `${next}${path.delimiter}${existing}` : existing
|
||||
process.env.Path = process.env.PATH
|
||||
}
|
||||
|
||||
try {
|
||||
const archDir = process.arch === 'arm64' ? 'arm64' : 'x64'
|
||||
const resourceRoots = [
|
||||
path.join(process.cwd(), 'resources'),
|
||||
path.join(process.cwd(), 'resources', 'resources'),
|
||||
path.join(process.resourcesPath || '', 'resources'),
|
||||
process.resourcesPath || ''
|
||||
].filter((value, index, list) => value && list.indexOf(value) === index && fs.existsSync(value))
|
||||
|
||||
const resourcesRoot = resourceRoots[0] || path.join(process.cwd(), 'resources')
|
||||
const dllDirs = resourceRoots.flatMap((root) => [
|
||||
root,
|
||||
path.join(root, 'wcdb', 'win32', archDir),
|
||||
path.join(root, 'wcdb', 'win32', 'x64'),
|
||||
path.join(root, 'key', 'win32', archDir),
|
||||
path.join(root, 'key', 'win32', 'x64'),
|
||||
path.join(root, 'runtime', 'win32')
|
||||
])
|
||||
|
||||
process.env.WCDB_RESOURCES_PATH = process.env.WCDB_RESOURCES_PATH || resourcesRoot
|
||||
process.env.WEFLOW_PROJECT_NAME = process.env.WEFLOW_PROJECT_NAME || 'WeFlow'
|
||||
prependPath(dllDirs.filter((dir) => fs.existsSync(dir)))
|
||||
} catch (error) {
|
||||
console.error('[WechatExplorer] failed to enforce local DLL priority:', error)
|
||||
}
|
||||
@@ -14,6 +14,15 @@ export function getCurrentKey(): string {
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentAccountRoot(): string {
|
||||
if (!dbRef) return ''
|
||||
try {
|
||||
return dbRef.getWcdb4Client().getAccountRoot()
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export interface FormattedContact {
|
||||
m_nsUsrName: string
|
||||
m_nsNickName: string
|
||||
@@ -31,6 +40,7 @@ export interface FormattedMessage {
|
||||
isSender: boolean
|
||||
img?: string
|
||||
name?: string
|
||||
senderId?: string
|
||||
contentData?: ReturnType<typeof parseMessageContent>
|
||||
voiceDataUrl?: string
|
||||
voiceDuration?: number
|
||||
@@ -133,6 +143,15 @@ export function listContacts(filter?: string): FormattedContact[] {
|
||||
return contacts
|
||||
}
|
||||
|
||||
export function getContactAvatars(usernames: string[]): Record<string, string> {
|
||||
if (!dbRef) return {}
|
||||
const normalized = Array.from(
|
||||
new Set((usernames || []).map((username) => String(username || '').trim()).filter(Boolean))
|
||||
)
|
||||
if (normalized.length === 0) return {}
|
||||
return dbRef.getWcdb4Client().getAvatarUrls(normalized)
|
||||
}
|
||||
|
||||
export function listMessages(
|
||||
userMd5: string,
|
||||
startTime?: number,
|
||||
@@ -140,16 +159,18 @@ export function listMessages(
|
||||
): FormattedMessage[] {
|
||||
if (!dbRef) return []
|
||||
|
||||
const startedAt = Date.now()
|
||||
const wcdb4Client = dbRef.getWcdb4Client()
|
||||
const username = wcdb4Client.getUsernameByMd5(userMd5)
|
||||
console.log(
|
||||
`[ChatService] listMessages begin md5=${userMd5} username=${username || ''} start=${startTime || 0} end=${endTime || 0}`
|
||||
)
|
||||
const rawMessages = dbRef.getUserMessages(userMd5, startTime, endTime)
|
||||
const groupMembers = dbRef.getGroupMembersForChat(userMd5)
|
||||
const myAvatar = dbRef.getMyAvatarUrl()
|
||||
const myGroupNickname = username?.endsWith('@chatroom')
|
||||
? wcdb4Client.getMyGroupNickname(username)
|
||||
: undefined
|
||||
console.log(
|
||||
`[ChatService] listMessages native done md5=${userMd5} raw=${rawMessages.length} cost=${Date.now() - startedAt}ms`
|
||||
)
|
||||
|
||||
return rawMessages.map((msg: WechatMessage) => {
|
||||
const formatted = rawMessages.map((msg: WechatMessage) => {
|
||||
const rawMsgType = parseInt(msg.messageType)
|
||||
const msgType = normalizeMsgType(msg.messageType)
|
||||
const createTime = parseInt(msg.msgCreateTime)
|
||||
@@ -160,9 +181,9 @@ export function listMessages(
|
||||
let content = msg.msgContent
|
||||
let img = ''
|
||||
let name = ''
|
||||
let senderId = typeof msg.sender === 'string' ? msg.sender : ''
|
||||
if (isMine) {
|
||||
if (myAvatar) img = myAvatar
|
||||
name = myGroupNickname || (typeof msg.senderNickname === 'string' ? msg.senderNickname : '')
|
||||
name = typeof msg.senderNickname === 'string' ? msg.senderNickname : ''
|
||||
} else {
|
||||
if (typeof msg.senderAvatar === 'string') img = msg.senderAvatar
|
||||
if (typeof msg.senderNickname === 'string') name = msg.senderNickname
|
||||
@@ -172,15 +193,13 @@ export function listMessages(
|
||||
if (colonIndex > 0) {
|
||||
const potentialWxid = content.substring(0, colonIndex)
|
||||
if (potentialWxid.startsWith('wxid_')) {
|
||||
const member = dbRef!.getGroupMember(potentialWxid)
|
||||
if (member) img = member.m_nsHeadImgUrl
|
||||
if (groupMembers[potentialWxid]) {
|
||||
name = groupMembers[potentialWxid]
|
||||
senderId = senderId || potentialWxid
|
||||
name = name || potentialWxid
|
||||
content = content.substring(colonIndex + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isMine && !name && senderId) name = senderId
|
||||
|
||||
let contentData: ReturnType<typeof parseMessageContent> | undefined
|
||||
let displayType = MSG_TYPE_DICT[msgType] || msg.messageType
|
||||
@@ -245,12 +264,18 @@ export function listMessages(
|
||||
content,
|
||||
img,
|
||||
name,
|
||||
senderId,
|
||||
sessionId: username,
|
||||
localId,
|
||||
createTime,
|
||||
contentData
|
||||
}
|
||||
})
|
||||
|
||||
console.log(
|
||||
`[ChatService] listMessages end md5=${userMd5} formatted=${formatted.length} cost=${Date.now() - startedAt}ms`
|
||||
)
|
||||
return formatted
|
||||
}
|
||||
|
||||
export function getGroupSnapshot(userMd5: string): GroupSnapshot | null {
|
||||
|
||||
@@ -10,11 +10,100 @@ export interface AppSettings {
|
||||
apiPort: number
|
||||
}
|
||||
|
||||
function getDefaultDbRoot(): string {
|
||||
const home = os.homedir()
|
||||
const candidates = getDefaultDbRootCandidates(home)
|
||||
return candidates.find((candidate) => isUsableDbRoot(candidate)) || candidates[0]
|
||||
}
|
||||
|
||||
function getDefaultDbRootCandidates(home: string): string[] {
|
||||
if (process.platform !== 'win32') {
|
||||
return [path.join(home, 'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files')]
|
||||
}
|
||||
|
||||
const candidates = [
|
||||
...getWeflowDbPathCandidates(home),
|
||||
path.join(home, 'Documents', 'WeChat Files'),
|
||||
path.join(home, 'Documents', 'xwechat_files'),
|
||||
path.join(home, 'WeChat Files'),
|
||||
path.join(os.homedir(), 'AppData', 'Roaming', 'Tencent', 'xwechat_files')
|
||||
]
|
||||
|
||||
for (const drive of getWindowsDrives()) {
|
||||
candidates.push(path.join(`${drive}:\\`, 'xwechat_files'))
|
||||
candidates.push(path.join(`${drive}:\\`, 'WeChat Files'))
|
||||
for (const child of listDirectories(`${drive}:\\`)) {
|
||||
candidates.push(path.join(child, 'xwechat_files'))
|
||||
candidates.push(path.join(child, 'WeChat Files'))
|
||||
}
|
||||
}
|
||||
|
||||
return unique(candidates)
|
||||
}
|
||||
|
||||
function getWeflowDbPathCandidates(home: string): string[] {
|
||||
const configPaths = [
|
||||
path.join(home, 'AppData', 'Roaming', 'weflow', 'WeFlow-config.json'),
|
||||
path.join(home, 'AppData', 'Roaming', 'WeFlow', 'WeFlow-config.json')
|
||||
]
|
||||
const candidates: string[] = []
|
||||
for (const configPath of configPaths) {
|
||||
try {
|
||||
const config = fs.readJsonSync(configPath) as { dbPath?: unknown }
|
||||
if (typeof config.dbPath === 'string' && config.dbPath.trim()) {
|
||||
candidates.push(config.dbPath.trim())
|
||||
}
|
||||
} catch {
|
||||
// WeFlow is optional; ignore missing or unreadable config.
|
||||
}
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
function getWindowsDrives(): string[] {
|
||||
const drives: string[] = []
|
||||
for (let code = 67; code <= 90; code += 1) {
|
||||
const drive = String.fromCharCode(code)
|
||||
if (fs.existsSync(`${drive}:\\`)) drives.push(drive)
|
||||
}
|
||||
return drives
|
||||
}
|
||||
|
||||
function listDirectories(root: string): string[] {
|
||||
try {
|
||||
return fs
|
||||
.readdirSync(root)
|
||||
.map((name) => path.join(root, name))
|
||||
.filter((candidate) => {
|
||||
try {
|
||||
return fs.statSync(candidate).isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function unique(values: string[]): string[] {
|
||||
return Array.from(new Set(values))
|
||||
}
|
||||
|
||||
function isUsableDbRoot(candidate?: string): boolean {
|
||||
if (!candidate || !fs.existsSync(candidate)) return false
|
||||
if (fs.existsSync(path.join(candidate, 'db_storage'))) return true
|
||||
try {
|
||||
return fs
|
||||
.readdirSync(candidate)
|
||||
.some((name) => fs.existsSync(path.join(candidate, name, 'db_storage')))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: AppSettings = {
|
||||
dbRoot: path.join(
|
||||
os.homedir(),
|
||||
'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files'
|
||||
),
|
||||
dbRoot: getDefaultDbRoot(),
|
||||
apiEnabled: true,
|
||||
apiHost: '127.0.0.1',
|
||||
apiPort: 6131
|
||||
@@ -37,6 +126,9 @@ export function loadSettings(): AppSettings {
|
||||
if (fs.existsSync(SETTINGS_FILE)) {
|
||||
const raw = fs.readJsonSync(SETTINGS_FILE) as Partial<AppSettings>
|
||||
cache = { ...DEFAULT_SETTINGS, ...raw }
|
||||
if (process.platform === 'win32' && !isUsableDbRoot(cache.dbRoot)) {
|
||||
cache.dbRoot = getDefaultDbRoot()
|
||||
}
|
||||
return cache
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
+408
-71
@@ -41,6 +41,83 @@ type KoffiModule = {
|
||||
decode: (ptr: unknown, type: string, length: number) => string
|
||||
}
|
||||
|
||||
// Module-level singleton for WCDB native handle. WCDB's Windows runtime
|
||||
// returns -1006 if wcdb_init is called more than once per process, so we
|
||||
// perform InitProtection + wcdb_init exactly once and cache the resulting
|
||||
// library reference for every Wcdb4Client instance.
|
||||
let wcdbBootstrapLib: KoffiLibrary | null = null
|
||||
|
||||
export function bootstrapWcdbNative(libPath?: string, libDirOverride?: string): KoffiLibrary {
|
||||
if (wcdbBootstrapLib) return wcdbBootstrapLib
|
||||
|
||||
const koffi = nodeRequire('koffi') as KoffiModule
|
||||
const resolvedLibPath = libPath || Wcdb4Client.resolveNativeLibrary()
|
||||
const libDir = libDirOverride || path.dirname(resolvedLibPath)
|
||||
console.log(
|
||||
`[WCDB4] bootstrap koffi.load begin ${resolvedLibPath} cwd=${process.cwd()} resourcesPath=${process.resourcesPath || ''}`
|
||||
)
|
||||
for (const name of process.platform === 'win32'
|
||||
? ['WCDB.dll', 'SDL2.dll']
|
||||
: process.platform === 'darwin'
|
||||
? ['libWCDB.dylib']
|
||||
: []) {
|
||||
const preloadPath = path.join(libDir, name)
|
||||
if (!fs.existsSync(preloadPath)) continue
|
||||
try {
|
||||
koffi.load(preloadPath)
|
||||
console.log(`[WCDB4] bootstrap preload ok ${preloadPath}`)
|
||||
} catch {
|
||||
console.warn(`[WCDB4] bootstrap preload failed ${preloadPath}`)
|
||||
}
|
||||
}
|
||||
const lib = koffi.load(resolvedLibPath)
|
||||
console.log(`[WCDB4] bootstrap koffi.load ok ${resolvedLibPath}`)
|
||||
|
||||
const initProtection = lib.func('int32 InitProtection(const char* resourcePath)') as (
|
||||
resourcePath: string
|
||||
) => number
|
||||
const resourceRoots = Array.from(
|
||||
new Set([
|
||||
libDir,
|
||||
path.dirname(libDir),
|
||||
process.env.WCDB_RESOURCES_PATH || '',
|
||||
path.join(process.resourcesPath || process.cwd(), 'resources'),
|
||||
process.resourcesPath || process.cwd(),
|
||||
path.join(process.cwd(), 'resources')
|
||||
])
|
||||
)
|
||||
let lastCode = -1
|
||||
let initOk = false
|
||||
for (const resourceRoot of resourceRoots) {
|
||||
try {
|
||||
console.log(`[WCDB4] bootstrap InitProtection call ${resourceRoot}`)
|
||||
lastCode = Number(initProtection(resourceRoot))
|
||||
console.log(`[WCDB4] bootstrap InitProtection rc=${lastCode} path=${resourceRoot}`)
|
||||
if (lastCode === 0) {
|
||||
initOk = true
|
||||
console.log(`[WCDB4] bootstrap InitProtection ok path=${resourceRoot}`)
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[WCDB4] bootstrap InitProtection exception path=${resourceRoot}`, error)
|
||||
}
|
||||
}
|
||||
if (!initOk) {
|
||||
throw new Error(`InitProtection 失败,错误码: ${lastCode}; tried=${resourceRoots.join(' | ')}`)
|
||||
}
|
||||
|
||||
const wcdbInit = lib.func('int32 wcdb_init()') as () => number
|
||||
console.log('[WCDB4] bootstrap wcdb_init begin')
|
||||
const initRc = Number(wcdbInit())
|
||||
console.log(`[WCDB4] bootstrap wcdb_init rc=${initRc}`)
|
||||
if (initRc !== 0) {
|
||||
throw new Error(`wcdb_init 失败,错误码: ${initRc}`)
|
||||
}
|
||||
|
||||
wcdbBootstrapLib = lib
|
||||
return lib
|
||||
}
|
||||
|
||||
type KoffiLibrary = {
|
||||
func: (signature: string) => (...args: unknown[]) => unknown
|
||||
}
|
||||
@@ -51,10 +128,7 @@ type WcdbHandleOut = [number]
|
||||
const nodeRequire = createRequire(import.meta.url)
|
||||
|
||||
export class Wcdb4Client {
|
||||
static readonly defaultRoot = path.join(
|
||||
os.homedir(),
|
||||
'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files'
|
||||
)
|
||||
static readonly defaultRoot = Wcdb4Client.findExistingDefaultRoot()
|
||||
|
||||
private readonly key: string
|
||||
private readonly accountRoot: string
|
||||
@@ -63,13 +137,12 @@ export class Wcdb4Client {
|
||||
private readonly sessionDbPath: string
|
||||
private koffi: KoffiModule | null = null
|
||||
private handle: number | null = null
|
||||
private initialized = false
|
||||
private displayNameCache = new Map<string, string>()
|
||||
private avatarCache = new Map<string, string>()
|
||||
private groupNicknameCache = new Map<string, Map<string, string>>()
|
||||
private cachedSessions: Wcdb4Session[] | null = null
|
||||
private cachedChatTables: { name: string; db_number: string }[] | null = null
|
||||
|
||||
private wcdbInit: (() => number) | null = null
|
||||
private wcdbShutdown: (() => number) | null = null
|
||||
private wcdbOpenAccount:
|
||||
| ((sessionDbPath: string, key: string, handleOut: WcdbHandleOut) => number)
|
||||
@@ -86,6 +159,9 @@ export class Wcdb4Client {
|
||||
outJson: WcdbVoidOut
|
||||
) => number)
|
||||
| null = null
|
||||
private wcdbGetMessageTableStats:
|
||||
| ((handle: number, username: string, outJson: WcdbVoidOut) => number)
|
||||
| null = null
|
||||
private wcdbGetDisplayNames:
|
||||
| ((handle: number, usernamesJson: string, outJson: WcdbVoidOut) => number)
|
||||
| null = null
|
||||
@@ -158,11 +234,11 @@ export class Wcdb4Client {
|
||||
}
|
||||
|
||||
static resolveAccountRoot(accountRoot: string): string {
|
||||
const target = (accountRoot || '').trim().replace(/\/+$/, '')
|
||||
const target = (accountRoot || '').trim().replace(/[\\/]+$/, '')
|
||||
if (!target) {
|
||||
throw new Error('微信 4.0 账号目录不能为空')
|
||||
}
|
||||
if (fs.existsSync(path.join(target, 'db_storage'))) {
|
||||
if (Wcdb4Client.hasDbStorage(target)) {
|
||||
return target
|
||||
}
|
||||
if (!fs.existsSync(target)) {
|
||||
@@ -171,17 +247,8 @@ export class Wcdb4Client {
|
||||
const candidates = fs
|
||||
.readdirSync(target)
|
||||
.map((name) => path.join(target, name))
|
||||
.filter((candidate) => {
|
||||
try {
|
||||
return (
|
||||
fs.statSync(candidate).isDirectory() &&
|
||||
fs.existsSync(path.join(candidate, 'db_storage'))
|
||||
)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)
|
||||
.filter((candidate) => Wcdb4Client.hasDbStorage(candidate))
|
||||
.sort(Wcdb4Client.compareAccountDirs)
|
||||
if (!candidates[0]) {
|
||||
throw new Error(`未找到包含 db_storage 的微信 4.0 账号目录: ${target}`)
|
||||
}
|
||||
@@ -194,24 +261,15 @@ export class Wcdb4Client {
|
||||
throw new Error(`未找到微信 4.0 数据目录: ${root}`)
|
||||
}
|
||||
|
||||
if (fs.existsSync(path.join(root, 'db_storage'))) {
|
||||
if (Wcdb4Client.hasDbStorage(root)) {
|
||||
return root
|
||||
}
|
||||
|
||||
const candidates = fs
|
||||
.readdirSync(root)
|
||||
.map((name) => path.join(root, name))
|
||||
.filter((candidate) => {
|
||||
try {
|
||||
return (
|
||||
fs.statSync(candidate).isDirectory() &&
|
||||
fs.existsSync(path.join(candidate, 'db_storage'))
|
||||
)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)
|
||||
.filter((candidate) => Wcdb4Client.hasDbStorage(candidate))
|
||||
.sort(Wcdb4Client.compareAccountDirs)
|
||||
|
||||
if (!candidates[0]) {
|
||||
throw new Error(`未找到包含 db_storage 的微信 4.0 账号目录: ${root}`)
|
||||
@@ -220,6 +278,117 @@ export class Wcdb4Client {
|
||||
return candidates[0]
|
||||
}
|
||||
|
||||
private static findExistingDefaultRoot(): string {
|
||||
const roots = Wcdb4Client.getDefaultRootCandidates()
|
||||
return roots.find((root) => Wcdb4Client.isUsableRoot(root)) || roots[0]
|
||||
}
|
||||
|
||||
private static getDefaultRootCandidates(): string[] {
|
||||
const home = os.homedir()
|
||||
if (process.platform === 'win32') {
|
||||
const candidates = [
|
||||
...Wcdb4Client.getWeflowDbPathCandidates(home),
|
||||
path.join(home, 'Documents', 'WeChat Files'),
|
||||
path.join(home, 'Documents', 'xwechat_files'),
|
||||
path.join(home, 'WeChat Files'),
|
||||
path.join(home, 'AppData', 'Roaming', 'Tencent', 'xwechat_files')
|
||||
]
|
||||
for (const drive of Wcdb4Client.getWindowsDrives()) {
|
||||
candidates.push(path.join(`${drive}:\\`, 'xwechat_files'))
|
||||
candidates.push(path.join(`${drive}:\\`, 'WeChat Files'))
|
||||
for (const child of Wcdb4Client.listDirectories(`${drive}:\\`)) {
|
||||
candidates.push(path.join(child, 'xwechat_files'))
|
||||
candidates.push(path.join(child, 'WeChat Files'))
|
||||
}
|
||||
}
|
||||
return Array.from(new Set(candidates))
|
||||
}
|
||||
return [path.join(home, 'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files')]
|
||||
}
|
||||
|
||||
private static getWeflowDbPathCandidates(home: string): string[] {
|
||||
const configPaths = [
|
||||
path.join(home, 'AppData', 'Roaming', 'weflow', 'WeFlow-config.json'),
|
||||
path.join(home, 'AppData', 'Roaming', 'WeFlow', 'WeFlow-config.json')
|
||||
]
|
||||
const candidates: string[] = []
|
||||
for (const configPath of configPaths) {
|
||||
try {
|
||||
const config = fs.readJsonSync(configPath) as { dbPath?: unknown }
|
||||
if (typeof config.dbPath === 'string' && config.dbPath.trim()) {
|
||||
candidates.push(config.dbPath.trim())
|
||||
}
|
||||
} catch {
|
||||
// WeFlow is optional; ignore missing or unreadable config.
|
||||
}
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
private static getWindowsDrives(): string[] {
|
||||
const drives: string[] = []
|
||||
for (let code = 67; code <= 90; code += 1) {
|
||||
const drive = String.fromCharCode(code)
|
||||
if (fs.existsSync(`${drive}:\\`)) drives.push(drive)
|
||||
}
|
||||
return drives
|
||||
}
|
||||
|
||||
private static listDirectories(root: string): string[] {
|
||||
try {
|
||||
return fs
|
||||
.readdirSync(root)
|
||||
.map((name) => path.join(root, name))
|
||||
.filter((candidate) => {
|
||||
try {
|
||||
return fs.statSync(candidate).isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private static hasDbStorage(candidate: string): boolean {
|
||||
try {
|
||||
return fs.statSync(candidate).isDirectory() && fs.existsSync(path.join(candidate, 'db_storage'))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private static isUsableRoot(candidate: string): boolean {
|
||||
if (!fs.existsSync(candidate)) return false
|
||||
if (Wcdb4Client.hasDbStorage(candidate)) return true
|
||||
try {
|
||||
return fs
|
||||
.readdirSync(candidate)
|
||||
.some((name) => Wcdb4Client.hasDbStorage(path.join(candidate, name)))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private static hasSessionDb(candidate: string): boolean {
|
||||
return (
|
||||
fs.existsSync(path.join(candidate, 'db_storage', 'session', 'session.db')) ||
|
||||
fs.existsSync(path.join(candidate, 'db_storage', 'session.db'))
|
||||
)
|
||||
}
|
||||
|
||||
private static compareAccountDirs(a: string, b: string): number {
|
||||
const aHasSession = Wcdb4Client.hasSessionDb(a)
|
||||
const bHasSession = Wcdb4Client.hasSessionDb(b)
|
||||
if (aHasSession !== bHasSession) return aHasSession ? -1 : 1
|
||||
try {
|
||||
return fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
private static cleanAccountDirName(dirName: string): string {
|
||||
const trimmed = dirName.trim()
|
||||
if (!trimmed) return trimmed
|
||||
@@ -236,24 +405,21 @@ export class Wcdb4Client {
|
||||
|
||||
open(): void {
|
||||
this.loadNativeLibrary()
|
||||
if (!this.wcdbInit || !this.wcdbOpenAccount) {
|
||||
if (!this.wcdbOpenAccount) {
|
||||
throw new Error('WCDB 4.0 native 接口未就绪')
|
||||
}
|
||||
|
||||
if (!this.initialized) {
|
||||
const initResult = this.wcdbInit()
|
||||
if (initResult !== 0) {
|
||||
console.warn(`wcdb_init 返回 ${initResult},继续尝试 wcdb_open_account`)
|
||||
} else {
|
||||
this.initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
let openResult = -1
|
||||
const handleOut: WcdbHandleOut = [0]
|
||||
const openResult = this.wcdbOpenAccount(this.sessionDbPath, this.key, handleOut)
|
||||
openResult = this.wcdbOpenAccount(this.sessionDbPath, this.key, handleOut)
|
||||
|
||||
if (openResult !== 0 || handleOut[0] <= 0) {
|
||||
const hint =
|
||||
openResult === -1005
|
||||
? ';这通常表示密钥与当前账号数据库不匹配,请确认微信已登录目标账号,并重新自动获取密钥'
|
||||
: ''
|
||||
throw new Error(
|
||||
`wcdb_open_account 失败,错误码: ${openResult}; sessionDb=${this.sessionDbPath}; accountRoot=${this.accountRoot}; wxid=${this.wxid}`
|
||||
`wcdb_open_account 失败,错误码: ${openResult}${hint}; sessionDb=${this.sessionDbPath}; accountRoot=${this.accountRoot}; wxid=${this.wxid}; keyLength=${this.key.length}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -269,16 +435,17 @@ export class Wcdb4Client {
|
||||
|
||||
close(): void {
|
||||
this.stopMonitor()
|
||||
if (!this.initialized || !this.wcdbShutdown) return
|
||||
if (this.handle === null || !this.wcdbShutdown) return
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
try {
|
||||
this.wcdbShutdown()
|
||||
} catch {
|
||||
// Mirror WechatExplorer: shutdown is best-effort on app close.
|
||||
// Shutdown is best-effort on app close.
|
||||
}
|
||||
}
|
||||
|
||||
this.handle = null
|
||||
this.initialized = false
|
||||
this.cachedSessions = null
|
||||
this.displayNameCache.clear()
|
||||
this.avatarCache.clear()
|
||||
@@ -425,36 +592,51 @@ export class Wcdb4Client {
|
||||
if (this.cachedSessions) return this.cachedSessions
|
||||
if (!this.wcdbGetSessions) return []
|
||||
|
||||
const rows = this.callJson<Record<string, unknown>[]>((handle, outJson) =>
|
||||
this.wcdbGetSessions!(handle, outJson)
|
||||
)
|
||||
|
||||
const rows = this.readSessionRows()
|
||||
const sessions = (Array.isArray(rows) ? rows : [])
|
||||
.map((row) => this.normalizeSession(row))
|
||||
.filter((session) => session.username)
|
||||
|
||||
const sessionUsernames = sessions.map((session) => session.username)
|
||||
this.hydrateDisplayNames(sessionUsernames)
|
||||
this.hydrateAvatarUrls(sessionUsernames)
|
||||
this.cachedSessions = sessions.map((session) => ({
|
||||
...session,
|
||||
nickname: this.displayNameCache.get(session.username) || session.nickname || session.username,
|
||||
avatar: this.avatarCache.get(session.username)
|
||||
nickname: this.displayNameCache.get(session.username) || session.nickname || session.username
|
||||
}))
|
||||
|
||||
return this.cachedSessions
|
||||
}
|
||||
|
||||
getChatTables(): { name: string; db_number: string }[] {
|
||||
return this.getSessions().map((session) => ({
|
||||
if (this.cachedChatTables) return this.cachedChatTables
|
||||
const sessions =
|
||||
this.cachedSessions ||
|
||||
this.readSessionRows()
|
||||
.map((row) => this.normalizeSession(row))
|
||||
.filter((session) => session.username)
|
||||
this.cachedChatTables = sessions.map((session) => ({
|
||||
name: `Chat_${this.md5(session.username)}`,
|
||||
db_number: session.username
|
||||
}))
|
||||
return this.cachedChatTables
|
||||
}
|
||||
|
||||
getMessages(username: string, startTime?: number, endTime?: number): Wcdb4Message[] {
|
||||
const startedAt = Date.now()
|
||||
console.log(
|
||||
`[WCDB4] getMessages begin username=${username} start=${startTime || 0} end=${endTime || 0}`
|
||||
)
|
||||
try {
|
||||
const cursorMessages = this.getMessagesByCursor(username, startTime, endTime)
|
||||
if (cursorMessages) return cursorMessages
|
||||
if (cursorMessages) {
|
||||
console.log(
|
||||
`[WCDB4] getMessages cursor ok username=${username} rows=${cursorMessages.length} cost=${Date.now() - startedAt}ms`
|
||||
)
|
||||
return cursorMessages
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[WCDB4] cursor messages failed username=${username}:`, error)
|
||||
}
|
||||
|
||||
if (!this.wcdbGetMessages) return []
|
||||
|
||||
@@ -463,15 +645,96 @@ export class Wcdb4Client {
|
||||
let offset = 0
|
||||
|
||||
while (true) {
|
||||
const rows = this.callJson<Record<string, unknown>[]>((handle, outJson) =>
|
||||
let rows: Record<string, unknown>[]
|
||||
try {
|
||||
rows = this.callJson<Record<string, unknown>[]>((handle, outJson) =>
|
||||
this.wcdbGetMessages!(handle, username, limit, offset, outJson)
|
||||
)
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[WCDB4] get_messages failed username=${username} offset=${offset}:`,
|
||||
error
|
||||
)
|
||||
break
|
||||
}
|
||||
const batch = Array.isArray(rows) ? rows : []
|
||||
allRows.push(...batch)
|
||||
if (batch.length < limit) break
|
||||
offset += limit
|
||||
}
|
||||
|
||||
if (allRows.length === 0) {
|
||||
const tableRows = this.getMessagesByTableScan(username, startTime, endTime)
|
||||
if (tableRows.length > 0) {
|
||||
console.log(
|
||||
`[WCDB4] getMessages table scan ok username=${username} rows=${tableRows.length} cost=${Date.now() - startedAt}ms`
|
||||
)
|
||||
return tableRows
|
||||
}
|
||||
}
|
||||
|
||||
const messages = this.finalizeMessages(username, allRows, startTime, endTime)
|
||||
console.log(
|
||||
`[WCDB4] getMessages direct ok username=${username} rows=${messages.length} cost=${Date.now() - startedAt}ms`
|
||||
)
|
||||
return messages
|
||||
}
|
||||
|
||||
private readSessionRows(): Record<string, unknown>[] {
|
||||
if (!this.wcdbGetSessions) return []
|
||||
const rows = this.callJson<Record<string, unknown>[]>((handle, outJson) =>
|
||||
this.wcdbGetSessions!(handle, outJson)
|
||||
)
|
||||
return Array.isArray(rows) ? rows : []
|
||||
}
|
||||
|
||||
private getMessagesByTableScan(
|
||||
username: string,
|
||||
startTime?: number,
|
||||
endTime?: number
|
||||
): Wcdb4Message[] {
|
||||
if (!this.wcdbGetMessageTableStats || !this.wcdbExecQuery) return []
|
||||
|
||||
let tables: { tableName: string; dbPath: string }[] = []
|
||||
try {
|
||||
const rows = this.callJson<Record<string, unknown>[]>((handle, outJson) =>
|
||||
this.wcdbGetMessageTableStats!(handle, username, outJson)
|
||||
)
|
||||
tables = (Array.isArray(rows) ? rows : [])
|
||||
.map((row) => ({
|
||||
tableName: this.pickString(row, ['table_name', 'tableName', 'name']),
|
||||
dbPath: this.pickString(row, ['db_path', 'dbPath', 'path'])
|
||||
}))
|
||||
.filter((row) => row.tableName && row.dbPath)
|
||||
} catch (error) {
|
||||
console.warn(`[WCDB4] message table stats failed username=${username}:`, error)
|
||||
return []
|
||||
}
|
||||
|
||||
const allRows: Record<string, unknown>[] = []
|
||||
const begin = this.normalizeTimestamp(startTime || 0)
|
||||
const end = this.normalizeTimestamp(endTime || 0)
|
||||
const where = [
|
||||
begin > 0 ? `"create_time" >= ${begin}` : '',
|
||||
end > 0 ? `"create_time" <= ${end}` : ''
|
||||
].filter(Boolean)
|
||||
const whereSql = where.length ? ` WHERE ${where.join(' AND ')}` : ''
|
||||
|
||||
for (const table of tables) {
|
||||
try {
|
||||
const sql = `SELECT * FROM ${this.quoteSqlIdentifier(table.tableName)}${whereSql} ORDER BY "create_time" ASC LIMIT 5000`
|
||||
const rows = this.callJson<Record<string, unknown>[]>((handle, outJson) =>
|
||||
this.wcdbExecQuery!(handle, 'message', table.dbPath, sql, outJson)
|
||||
)
|
||||
if (Array.isArray(rows)) allRows.push(...rows)
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[WCDB4] message table scan failed username=${username} db=${table.dbPath} table=${table.tableName}:`,
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return this.finalizeMessages(username, allRows, startTime, endTime)
|
||||
}
|
||||
|
||||
@@ -487,6 +750,17 @@ export class Wcdb4Client {
|
||||
return undefined
|
||||
}
|
||||
|
||||
getAvatarUrls(usernames: string[]): Record<string, string> {
|
||||
const normalized = this.uniq(usernames)
|
||||
this.hydrateAvatarUrls(normalized)
|
||||
const result: Record<string, string> = {}
|
||||
for (const username of normalized) {
|
||||
const avatar = this.avatarCache.get(username)
|
||||
if (avatar) result[username] = avatar
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
getMyGroupNickname(chatroomId: string): string | undefined {
|
||||
const groupNicknames = this.getGroupNicknames(chatroomId)
|
||||
for (const candidate of this.getMyUsernameCandidates()) {
|
||||
@@ -565,9 +839,6 @@ export class Wcdb4Client {
|
||||
endTime?: number
|
||||
): Wcdb4Message[] {
|
||||
const messages = rows.map((row) => this.normalizeMessage(row))
|
||||
const senderIds = messages.map((message) => message.sender || '').filter(Boolean)
|
||||
this.hydrateDisplayNames(senderIds)
|
||||
this.hydrateAvatarUrls(senderIds)
|
||||
|
||||
return messages
|
||||
.filter((message) => {
|
||||
@@ -650,6 +921,12 @@ export class Wcdb4Client {
|
||||
.map((member) => member.m_nsUsrName)
|
||||
.filter(Boolean)
|
||||
this.hydrateDisplayNames(missingDisplayNames)
|
||||
this.hydrateAvatarUrls(
|
||||
members
|
||||
.filter((member) => !member.m_nsHeadImgUrl)
|
||||
.map((member) => member.m_nsUsrName)
|
||||
.filter(Boolean)
|
||||
)
|
||||
return members.map((member) => ({
|
||||
...member,
|
||||
nickname:
|
||||
@@ -815,19 +1092,49 @@ export class Wcdb4Client {
|
||||
|
||||
const libPath = this.findNativeLibrary()
|
||||
const libDir = path.dirname(libPath)
|
||||
const wcdbCorePath = path.join(libDir, 'libWCDB.dylib')
|
||||
if (fs.existsSync(wcdbCorePath)) {
|
||||
console.log(
|
||||
`[WCDB4] loadNativeLibrary platform=${process.platform} arch=${process.arch} libPath=${libPath} cwd=${process.cwd()} resourcesPath=${process.resourcesPath || ''} WCDB_RESOURCES_PATH=${process.env.WCDB_RESOURCES_PATH || ''}`
|
||||
)
|
||||
|
||||
// Reuse the module-level bootstrap if main.ts already performed
|
||||
// InitProtection + wcdb_init; WCDB returns -1006 if wcdb_init is called
|
||||
// twice in the same process.
|
||||
let lib: KoffiLibrary
|
||||
if (wcdbBootstrapLib) {
|
||||
console.log('[WCDB4] reusing bootstrap lib, skip koffi.load and wcdb_init')
|
||||
lib = wcdbBootstrapLib
|
||||
} else {
|
||||
const preloadLibraries =
|
||||
process.platform === 'win32'
|
||||
? ['WCDB.dll', 'SDL2.dll']
|
||||
: process.platform === 'darwin'
|
||||
? ['libWCDB.dylib']
|
||||
: []
|
||||
for (const name of preloadLibraries) {
|
||||
const preloadPath = path.join(libDir, name)
|
||||
if (!fs.existsSync(preloadPath)) continue
|
||||
try {
|
||||
koffi.load(wcdbCorePath)
|
||||
koffi.load(preloadPath)
|
||||
console.log(`[WCDB4] preload ok ${preloadPath}`)
|
||||
} catch {
|
||||
// Some builds resolve this dependency through rpath.
|
||||
console.warn(`[WCDB4] preload failed ${preloadPath}`)
|
||||
// Some builds resolve dependencies through the platform loader path.
|
||||
}
|
||||
}
|
||||
|
||||
const lib = koffi.load(libPath)
|
||||
console.log(`[WCDB4] koffi.load begin ${libPath}`)
|
||||
lib = koffi.load(libPath)
|
||||
console.log(`[WCDB4] koffi.load ok ${libPath}`)
|
||||
this.initProtection(lib, libDir)
|
||||
const wcdbInit = lib.func('int32 wcdb_init()') as () => number
|
||||
console.log('[WCDB4] wcdb_init begin')
|
||||
const initResult = wcdbInit()
|
||||
console.log(`[WCDB4] wcdb_init rc=${initResult}`)
|
||||
if (initResult !== 0) {
|
||||
throw new Error(`wcdb_init 失败,错误码: ${initResult}`)
|
||||
}
|
||||
}
|
||||
|
||||
this.wcdbInit = lib.func('int32 wcdb_init()') as () => number
|
||||
this.wcdbShutdown = lib.func('int32 wcdb_shutdown()') as () => number
|
||||
this.wcdbOpenAccount = lib.func(
|
||||
'int32 wcdb_open_account(const char* path, const char* key, _Out_ int64* handle)'
|
||||
@@ -845,6 +1152,13 @@ export class Wcdb4Client {
|
||||
offset: number,
|
||||
outJson: WcdbVoidOut
|
||||
) => number
|
||||
try {
|
||||
this.wcdbGetMessageTableStats = lib.func(
|
||||
'int32 wcdb_get_message_table_stats(int64 handle, const char* sessionId, _Out_ void** outJson)'
|
||||
) as (handle: number, username: string, outJson: WcdbVoidOut) => number
|
||||
} catch {
|
||||
this.wcdbGetMessageTableStats = null
|
||||
}
|
||||
this.wcdbGetDisplayNames = lib.func(
|
||||
'int32 wcdb_get_display_names(int64 handle, const char* usernamesJson, _Out_ void** outJson)'
|
||||
) as (handle: number, usernamesJson: string, outJson: WcdbVoidOut) => number
|
||||
@@ -986,29 +1300,43 @@ export class Wcdb4Client {
|
||||
let lastCode = -1
|
||||
for (const resourceRoot of resourceRoots) {
|
||||
try {
|
||||
console.log(`[WCDB4] InitProtection call ${resourceRoot}`)
|
||||
lastCode = initProtection(resourceRoot)
|
||||
if (lastCode === 0) return
|
||||
} catch {
|
||||
console.log(`[WCDB4] InitProtection rc=${lastCode} path=${resourceRoot}`)
|
||||
if (lastCode === 0) {
|
||||
console.log(`[WCDB4] InitProtection ok path=${resourceRoot}`)
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[WCDB4] InitProtection exception path=${resourceRoot}`, error)
|
||||
// Try next candidate.
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(
|
||||
`InitProtection 返回 ${lastCode},继续尝试 wcdb_init/open; tried=${resourceRoots.join(' | ')}`
|
||||
)
|
||||
throw new Error(`InitProtection 失败,错误码: ${lastCode}; tried=${resourceRoots.join(' | ')}`)
|
||||
}
|
||||
|
||||
private findNativeLibrary(): string {
|
||||
return Wcdb4Client.resolveNativeLibrary()
|
||||
}
|
||||
|
||||
static resolveNativeLibrary(): string {
|
||||
const libName =
|
||||
process.platform === 'darwin'
|
||||
? 'libwcdb_api.dylib'
|
||||
: process.platform === 'linux'
|
||||
? 'libwcdb_api.so'
|
||||
: 'wcdb_api.dll'
|
||||
const platformDir = process.platform === 'darwin' ? 'macos' : process.platform
|
||||
const platformDir =
|
||||
process.platform === 'darwin' ? 'macos' : process.platform === 'win32' ? 'win32' : process.platform
|
||||
const archDir = process.arch === 'arm64' ? 'arm64' : 'x64'
|
||||
const resourcesPath = process.resourcesPath || process.cwd()
|
||||
const candidates = [
|
||||
process.env.WCDB_DLL_PATH,
|
||||
path.join(resourcesPath, 'resources', 'wcdb', platformDir, archDir, libName),
|
||||
path.join(resourcesPath, 'resources', 'wcdb', platformDir, 'x64', libName),
|
||||
path.join(process.cwd(), 'resources', 'wcdb', platformDir, archDir, libName),
|
||||
path.join(process.cwd(), 'resources', 'wcdb', platformDir, 'x64', libName),
|
||||
path.join(resourcesPath, 'resources', platformDir, libName),
|
||||
path.join(resourcesPath, 'resources', libName),
|
||||
path.join(process.cwd(), 'resources', platformDir, libName),
|
||||
@@ -1019,6 +1347,11 @@ export class Wcdb4Client {
|
||||
if (!found) {
|
||||
throw new Error(`找不到 WCDB native 库: ${candidates.join(', ')}`)
|
||||
}
|
||||
if (process.platform === 'win32') {
|
||||
const runtimeDir = path.join(resourcesPath, 'resources', 'runtime', 'win32')
|
||||
const dllDir = path.dirname(found)
|
||||
process.env.PATH = [dllDir, runtimeDir, process.env.PATH || ''].filter(Boolean).join(path.delimiter)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
@@ -1437,4 +1770,8 @@ export class Wcdb4Client {
|
||||
const normalized = input > 1e12 ? Math.floor(input / 1000) : Math.floor(input)
|
||||
return Math.min(Math.max(normalized, 0), 2147483647)
|
||||
}
|
||||
|
||||
private quoteSqlIdentifier(identifier: string): string {
|
||||
return `"${String(identifier || '').replace(/"/g, '""')}"`
|
||||
}
|
||||
}
|
||||
|
||||
+24
-3
@@ -34,12 +34,33 @@ export class WechatDb {
|
||||
private wcdb4Client: Wcdb4Client
|
||||
private chatMd5ToUsername = new Map<string, string>()
|
||||
|
||||
constructor(rawKey: string, accountRoot?: string) {
|
||||
console.log(`Initializing WechatDb with key length: ${rawKey.trim().length}`)
|
||||
static async create(rawKey: string, accountRoot?: string): Promise<WechatDb> {
|
||||
// WCDB native init must run on the Electron main process; worker threads
|
||||
// get -1006 from wcdb_init. Initialize the client synchronously here
|
||||
// (and keep create() async for callers that already await it).
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const client = new Wcdb4Client(rawKey, accountRoot)
|
||||
client.open()
|
||||
resolve(new WechatDb(rawKey, accountRoot, client))
|
||||
} catch (error) {
|
||||
reject(error instanceof Error ? error : new Error(String(error)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
constructor(
|
||||
rawKey: string,
|
||||
accountRoot?: string,
|
||||
clientOverride?: Wcdb4Client,
|
||||
initialChatTables?: { name: string; db_number: string }[]
|
||||
) {
|
||||
console.log(`Initializing WechatDb with key length: ${rawKey.trim().length}`)
|
||||
const client =
|
||||
clientOverride || new Wcdb4Client(rawKey, accountRoot)
|
||||
if (!clientOverride) client.open()
|
||||
this.wcdb4Client = client
|
||||
for (const table of client.getChatTables()) {
|
||||
for (const table of initialChatTables || client.getChatTables()) {
|
||||
if (table.name.startsWith('Chat_')) {
|
||||
this.chatMd5ToUsername.set(table.name.substring(5), table.db_number)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
||||
onDbKeyChange,
|
||||
onDbRootChanged
|
||||
}) => {
|
||||
const isWindows = window.electron.process.platform === 'win32'
|
||||
const dbRootPlaceholder = isWindows
|
||||
? 'C:\\Users\\你\\Documents\\WeChat Files'
|
||||
: '~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files'
|
||||
const [settings, setSettings] = useState<AppSettings | null>(null)
|
||||
const [settingsPath, setSettingsPath] = useState('')
|
||||
const [apiState, setApiState] = useState<ApiState | null>(null)
|
||||
@@ -208,7 +212,7 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-hint">
|
||||
密钥保存在本机 macOS Keychain(safeStorage 加密),不会上传任何服务器。
|
||||
密钥通过系统 safeStorage 加密保存在本机,不会上传任何服务器。
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -222,7 +226,7 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
||||
value={settings?.dbRoot ?? ''}
|
||||
onChange={(e) => setSettings(settings ? { ...settings, dbRoot: e.target.value } : null)}
|
||||
onBlur={(e) => handleSave({ dbRoot: e.target.value })}
|
||||
placeholder="~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files"
|
||||
placeholder={dbRootPlaceholder}
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
@@ -237,7 +241,7 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
||||
{reopenStatus && <span className="settings-status">{reopenStatus}</span>}
|
||||
</div>
|
||||
<div className="settings-hint">
|
||||
指向 xwechat_files 目录,内部包含 db_storage/。修改后需重新初始化才能生效。
|
||||
可填写微信数据总目录或具体账号目录。Windows 通常是 Documents\WeChat Files,macOS 通常是 xwechat_files;程序会自动选择包含 db_storage/session.db 的账号目录。
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user