dsh-chat-embed 0.0.0-stage → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +364 -3
- package/client.js +3870 -0
- package/cordis.patch.yml +11 -0
- package/docs/architecture.md +385 -0
- package/docs/publishing.md +82 -0
- package/icon.svg +14 -0
- package/index.js +335 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +70 -4
- package/restore-shell-patch.cmd +39 -0
- package/tools/shell-patch/clean-cache.ps1 +76 -0
- package/tools/shell-patch/patch-asar.cjs +272 -0
- package/tools/shell-patch/watchdog.ps1 +124 -0
- package//346/270/205/347/220/206/350/201/212/345/244/251/347/274/223/345/255/230.cmd +12 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* patch-asar.cjs - two idempotent patches for the desktop shell's embedded
|
|
3
|
+
* browser partitions (resources/app.asar -> lib/main.js).
|
|
4
|
+
*
|
|
5
|
+
* Patch 1 (persistent login): the shell names each workspace's partition
|
|
6
|
+
* `dsh-sidebar-browser-<random uuid>` - no `persist:` prefix (memory only) and
|
|
7
|
+
* a fresh uuid every launch, so every embedded webview is signed out on
|
|
8
|
+
* restart. Hashing the workspace identity instead makes it stable on disk.
|
|
9
|
+
*
|
|
10
|
+
* Patch 2 (no disk cache, optional): the same partition is created with
|
|
11
|
+
* `{ cache: false }`, so Chromium keeps Cookies / Local Storage / IndexedDB
|
|
12
|
+
* (the login) but stops writing the multi-megabyte HTTP and code caches.
|
|
13
|
+
*
|
|
14
|
+
* Both steps are idempotent: run build against the original asar or against an
|
|
15
|
+
* already patched one and the result is the same file.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node patch-asar.cjs build [--from <asar>] [--out <asar>] [--no-cache-patch]
|
|
19
|
+
* node patch-asar.cjs verify [--from <asar>] [--out <asar>]
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const crypto = require('node:crypto')
|
|
23
|
+
const fs = require('node:fs')
|
|
24
|
+
const path = require('node:path')
|
|
25
|
+
const os = require('node:os')
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Install locations tried in order when neither --resources nor DSH_RESOURCES
|
|
29
|
+
* is given, so the tool works without an edit on any machine.
|
|
30
|
+
*/
|
|
31
|
+
const RESOURCES_CANDIDATES = [
|
|
32
|
+
process.env.DSH_RESOURCES,
|
|
33
|
+
process.env.LOCALAPPDATA === undefined || process.env.LOCALAPPDATA === ''
|
|
34
|
+
? undefined
|
|
35
|
+
: path.join(process.env.LOCALAPPDATA, 'Programs', 'DeepSeek Harness', 'resources'),
|
|
36
|
+
'C:\\Program Files\\DeepSeek Harness\\resources',
|
|
37
|
+
'C:\\Program Files (x86)\\DeepSeek Harness\\resources',
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
const LOG = path.join(os.tmpdir(), 'dsh-chat-embed-patch-asar.log')
|
|
41
|
+
|
|
42
|
+
/** @returns {string | undefined} the resources directory that holds app.asar */
|
|
43
|
+
function detectResources() {
|
|
44
|
+
for (const candidate of RESOURCES_CANDIDATES) {
|
|
45
|
+
if (typeof candidate !== 'string' || candidate === '') continue
|
|
46
|
+
if (fs.existsSync(path.join(candidate, 'app.asar'))) return candidate
|
|
47
|
+
}
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const PERSIST_OLD = 'partition = `dsh-sidebar-browser-${randomUUID()}`;'
|
|
52
|
+
const PERSIST_NEW =
|
|
53
|
+
'partition = `persist:dsh-sidebar-${createHash("sha256").update(workspace).digest("hex").slice(0, 24)}`;'
|
|
54
|
+
const CACHE_OLD = 'this.configureSession(session.fromPartition(partition));'
|
|
55
|
+
const CACHE_NEW = 'this.configureSession(session.fromPartition(partition, { cache: false }));'
|
|
56
|
+
|
|
57
|
+
function log(message) {
|
|
58
|
+
const line = `[${new Date().toISOString()}] ${message}`
|
|
59
|
+
console.log(line)
|
|
60
|
+
try {
|
|
61
|
+
fs.appendFileSync(LOG, line + '\n')
|
|
62
|
+
} catch (error) {
|
|
63
|
+
void error
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {string[]} argv
|
|
69
|
+
* @returns {{ mode: string, from?: string, out?: string, cachePatch: boolean, help: boolean }}
|
|
70
|
+
*/
|
|
71
|
+
function parseArgs(argv) {
|
|
72
|
+
const options = { mode: 'build', from: undefined, out: undefined, cachePatch: true, help: false }
|
|
73
|
+
const rest = argv.slice(2)
|
|
74
|
+
let resources
|
|
75
|
+
for (let i = 0; i < rest.length; i++) {
|
|
76
|
+
const arg = rest[i]
|
|
77
|
+
if (arg === 'build' || arg === 'verify') options.mode = arg
|
|
78
|
+
else if (arg === '--resources') resources = rest[++i]
|
|
79
|
+
else if (arg === '--from') options.from = rest[++i]
|
|
80
|
+
else if (arg === '--out') options.out = rest[++i]
|
|
81
|
+
else if (arg === '--no-cache-patch') options.cachePatch = false
|
|
82
|
+
else if (arg === '--help' || arg === '-h') options.help = true
|
|
83
|
+
else {
|
|
84
|
+
console.error('unknown argument: ' + arg)
|
|
85
|
+
options.help = true
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const resolved = resources === undefined ? detectResources() : resources
|
|
89
|
+
if (options.from === undefined && resolved !== undefined) options.from = path.join(resolved, 'app.asar')
|
|
90
|
+
if (options.out === undefined) {
|
|
91
|
+
options.out = resolved !== undefined ? path.join(resolved, 'app.asar.new') : undefined
|
|
92
|
+
}
|
|
93
|
+
return options
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function usage() {
|
|
97
|
+
console.log(
|
|
98
|
+
[
|
|
99
|
+
'patch-asar.cjs — persistent partitions + no disk cache for the embedded browser',
|
|
100
|
+
'',
|
|
101
|
+
'Usage:',
|
|
102
|
+
' node patch-asar.cjs build [--resources <dir>] [--from <asar>] [--out <asar>] [--no-cache-patch]',
|
|
103
|
+
' node patch-asar.cjs verify [--resources <dir>] [--from <asar>] [--out <asar>]',
|
|
104
|
+
'',
|
|
105
|
+
'--resources the install directory that contains app.asar (default: $DSH_RESOURCES,',
|
|
106
|
+
' then %LOCALAPPDATA%\\Programs\\DeepSeek Harness\\resources, then Program Files)',
|
|
107
|
+
'',
|
|
108
|
+
'build writes <resources>/app.asar.new; replace app.asar with it while the app is closed.',
|
|
109
|
+
].join('\n'),
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function readAsar(file) {
|
|
114
|
+
const buf = fs.readFileSync(file)
|
|
115
|
+
const jsonLen = buf.readUInt32LE(12)
|
|
116
|
+
const headerStart = 16
|
|
117
|
+
const header = JSON.parse(buf.toString('utf8', headerStart, headerStart + jsonLen))
|
|
118
|
+
const pad = (4 - (jsonLen % 4)) % 4
|
|
119
|
+
const dataStart = headerStart + jsonLen + pad
|
|
120
|
+
return { buf, header, dataStart, data: buf.subarray(dataStart) }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function collect(header) {
|
|
124
|
+
const files = []
|
|
125
|
+
const walk = (node, prefix) => {
|
|
126
|
+
for (const [name, child] of Object.entries(node.files || {})) {
|
|
127
|
+
const p = prefix === '' ? name : prefix + '/' + name
|
|
128
|
+
if (child.files !== undefined) {
|
|
129
|
+
walk(child, p)
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
files.push({ path: p, node: child })
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
walk(header, '')
|
|
136
|
+
return files
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function integrityFor(content) {
|
|
140
|
+
const blockSize = 4194304
|
|
141
|
+
const blocks = []
|
|
142
|
+
for (let offset = 0; offset < content.length; offset += blockSize) {
|
|
143
|
+
blocks.push(
|
|
144
|
+
crypto
|
|
145
|
+
.createHash('sha256')
|
|
146
|
+
.update(content.subarray(offset, Math.min(offset + blockSize, content.length)))
|
|
147
|
+
.digest('hex'),
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
if (blocks.length === 0) blocks.push(crypto.createHash('sha256').update(Buffer.alloc(0)).digest('hex'))
|
|
151
|
+
return {
|
|
152
|
+
algorithm: 'SHA256',
|
|
153
|
+
hash: crypto.createHash('sha256').update(content).digest('hex'),
|
|
154
|
+
blockSize,
|
|
155
|
+
blocks,
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function applyPatches(source, cachePatch) {
|
|
160
|
+
let text = source
|
|
161
|
+
const applied = []
|
|
162
|
+
if (text.includes(PERSIST_OLD)) {
|
|
163
|
+
text = text.replace(PERSIST_OLD, PERSIST_NEW)
|
|
164
|
+
applied.push('persist-partition')
|
|
165
|
+
}
|
|
166
|
+
if (cachePatch && text.includes(CACHE_OLD)) {
|
|
167
|
+
text = text.replace(CACHE_OLD, CACHE_NEW)
|
|
168
|
+
applied.push('no-disk-cache')
|
|
169
|
+
}
|
|
170
|
+
const ok = text.includes(PERSIST_NEW) && (cachePatch === false || text.includes(CACHE_NEW))
|
|
171
|
+
return { text, applied, ok }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function build(options) {
|
|
175
|
+
const { header, data } = readAsar(options.from)
|
|
176
|
+
const files = collect(header)
|
|
177
|
+
log(`source: ${options.from} (${files.length} entries, data ${data.length} bytes)`)
|
|
178
|
+
|
|
179
|
+
const packed = files
|
|
180
|
+
.filter((file) => file.node.unpacked !== true)
|
|
181
|
+
.sort((a, b) => Number(a.node.offset) - Number(b.node.offset))
|
|
182
|
+
|
|
183
|
+
const chunks = []
|
|
184
|
+
let cursor = 0
|
|
185
|
+
let patched = false
|
|
186
|
+
|
|
187
|
+
for (const file of packed) {
|
|
188
|
+
const offset = Number(file.node.offset)
|
|
189
|
+
const size = Number(file.node.size)
|
|
190
|
+
let content = data.subarray(offset, offset + size)
|
|
191
|
+
|
|
192
|
+
if (file.path === 'lib/main.js') {
|
|
193
|
+
const source = content.toString('utf8')
|
|
194
|
+
const result = applyPatches(source, options.cachePatch)
|
|
195
|
+
if (!result.ok) throw new Error('lib/main.js does not contain the expected patch sites')
|
|
196
|
+
content = Buffer.from(result.text, 'utf8')
|
|
197
|
+
file.node.size = content.length
|
|
198
|
+
file.node.integrity = integrityFor(content)
|
|
199
|
+
patched = true
|
|
200
|
+
log(`lib/main.js: applied [${result.applied.join(', ') || 'nothing (already patched)'}], ${content.length} bytes`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
file.node.offset = String(cursor)
|
|
204
|
+
chunks.push(content)
|
|
205
|
+
cursor += content.length
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!patched) throw new Error('lib/main.js not found')
|
|
209
|
+
|
|
210
|
+
const newData = Buffer.concat(chunks)
|
|
211
|
+
const headerJson = JSON.stringify(header)
|
|
212
|
+
const headerBuf = Buffer.from(headerJson, 'utf8')
|
|
213
|
+
const pad = (4 - (headerBuf.length % 4)) % 4
|
|
214
|
+
const prefix = Buffer.alloc(16)
|
|
215
|
+
prefix.writeUInt32LE(4, 0)
|
|
216
|
+
prefix.writeUInt32LE(headerBuf.length + 8, 4)
|
|
217
|
+
prefix.writeUInt32LE(headerBuf.length + 4, 8)
|
|
218
|
+
prefix.writeUInt32LE(headerBuf.length, 12)
|
|
219
|
+
|
|
220
|
+
const out = Buffer.concat([prefix, headerBuf, Buffer.alloc(pad), newData])
|
|
221
|
+
fs.writeFileSync(options.out, out)
|
|
222
|
+
log(`wrote ${options.out} (${out.length} bytes)`)
|
|
223
|
+
verify(options)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function verify(options) {
|
|
227
|
+
if (!fs.existsSync(options.out)) throw new Error(options.out + ' is missing')
|
|
228
|
+
const base = readAsar(options.from)
|
|
229
|
+
const built = readAsar(options.out)
|
|
230
|
+
const baseFiles = collect(base.header)
|
|
231
|
+
const builtFiles = collect(built.header)
|
|
232
|
+
|
|
233
|
+
if (baseFiles.length !== builtFiles.length) {
|
|
234
|
+
throw new Error(`entry count changed: ${baseFiles.length} -> ${builtFiles.length}`)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const baseByPath = new Map(baseFiles.map((file) => [file.path, file]))
|
|
238
|
+
let compared = 0
|
|
239
|
+
let patchedSeen = false
|
|
240
|
+
|
|
241
|
+
for (const file of builtFiles) {
|
|
242
|
+
const before = baseByPath.get(file.path)
|
|
243
|
+
if (before === undefined) throw new Error(`new entry appeared: ${file.path}`)
|
|
244
|
+
if (file.node.unpacked === true || before.node.unpacked === true) {
|
|
245
|
+
if (Number(file.node.size) !== Number(before.node.size)) throw new Error(`size drift on unpacked ${file.path}`)
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
const builtBytes = built.data.subarray(Number(file.node.offset), Number(file.node.offset) + Number(file.node.size))
|
|
249
|
+
if (file.path === 'lib/main.js') {
|
|
250
|
+
const text = builtBytes.toString('utf8')
|
|
251
|
+
if (!text.includes(PERSIST_NEW)) throw new Error('persist patch missing in rebuilt asar')
|
|
252
|
+
if (options.cachePatch !== false && !text.includes(CACHE_NEW)) throw new Error('cache patch missing in rebuilt asar')
|
|
253
|
+
patchedSeen = true
|
|
254
|
+
continue
|
|
255
|
+
}
|
|
256
|
+
const beforeBytes = base.data.subarray(Number(before.node.offset), Number(before.node.offset) + Number(before.node.size))
|
|
257
|
+
if (!builtBytes.equals(beforeBytes)) throw new Error(`content drift: ${file.path}`)
|
|
258
|
+
compared += 1
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!patchedSeen) throw new Error('patched file not found during verification')
|
|
262
|
+
log(`verified: ${compared} files byte-identical, lib/main.js patched, ${builtFiles.length} entries total`)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const options = parseArgs(process.argv)
|
|
266
|
+
if (options.help === true || options.from === undefined || options.out === undefined) {
|
|
267
|
+
usage()
|
|
268
|
+
process.exit(options.help === true ? 0 : 1)
|
|
269
|
+
}
|
|
270
|
+
if (options.mode === 'build') build(options)
|
|
271
|
+
else verify(options)
|
|
272
|
+
log('done: ' + options.mode)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$Resources = $env:DSH_RESOURCES,
|
|
3
|
+
[string]$Exe
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
$ErrorActionPreference = 'Continue'
|
|
7
|
+
|
|
8
|
+
# Find the install without a hard-coded path, so the same script works on any
|
|
9
|
+
# machine. -Resources / DSH_RESOURCES override the search.
|
|
10
|
+
if (-not $Resources) {
|
|
11
|
+
$candidates = @()
|
|
12
|
+
if ($env:LOCALAPPDATA) { $candidates += (Join-Path $env:LOCALAPPDATA 'Programs\DeepSeek Harness\resources') }
|
|
13
|
+
$candidates += 'C:\Program Files\DeepSeek Harness\resources'
|
|
14
|
+
$candidates += 'C:\Program Files (x86)\DeepSeek Harness\resources'
|
|
15
|
+
foreach ($candidate in $candidates) {
|
|
16
|
+
if (Test-Path (Join-Path $candidate 'app.asar')) { $Resources = $candidate; break }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (-not $Resources) {
|
|
20
|
+
Write-Host 'watchdog: cannot find the DSH install. Pass -Resources <dir> or set DSH_RESOURCES.'
|
|
21
|
+
exit 1
|
|
22
|
+
}
|
|
23
|
+
if (-not $Exe) { $Exe = Join-Path (Split-Path -Parent $Resources) 'DeepSeek Harness.exe' }
|
|
24
|
+
|
|
25
|
+
$res = $Resources
|
|
26
|
+
$asar = Join-Path $res 'app.asar'
|
|
27
|
+
$new = Join-Path $res 'app.asar.new'
|
|
28
|
+
$orig = Join-Path $res 'app.asar.orig'
|
|
29
|
+
$log = Join-Path $env:TEMP 'dsh-chat-embed-watchdog.log'
|
|
30
|
+
$mark = Join-Path $env:TEMP 'dsh-chat-embed-patched.txt'
|
|
31
|
+
$app = 'DeepSeek Harness'
|
|
32
|
+
|
|
33
|
+
function Log($message) {
|
|
34
|
+
Add-Content -Path $log -Value ("[{0}] {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $message)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Log ('watchdog v4 started (pid ' + $PID + ', event-driven)')
|
|
38
|
+
|
|
39
|
+
if (Test-Path $mark) { Log 'already patched; exiting'; exit 0 }
|
|
40
|
+
if (-not (Test-Path $new)) { Log 'app.asar.new missing; exiting'; exit 1 }
|
|
41
|
+
|
|
42
|
+
# Pick the Electron main process: a DeepSeek Harness process whose parent is not
|
|
43
|
+
# itself another DeepSeek Harness process. Everything else is a child worker.
|
|
44
|
+
$alive = @(Get-CimInstance Win32_Process -Filter "Name='DeepSeek Harness.exe'" -ErrorAction SilentlyContinue)
|
|
45
|
+
$mainPid = 0
|
|
46
|
+
foreach ($proc in $alive) {
|
|
47
|
+
$parent = @($alive | Where-Object { $_.ProcessId -eq $proc.ParentProcessId })
|
|
48
|
+
if ($parent.Count -eq 0) { $mainPid = $proc.ProcessId; break }
|
|
49
|
+
}
|
|
50
|
+
if ($mainPid -eq 0 -and $alive.Count -gt 0) { $mainPid = ($alive | Select-Object -First 1).ProcessId }
|
|
51
|
+
|
|
52
|
+
if ($mainPid -ne 0) {
|
|
53
|
+
Log ('waiting on the main process handle (pid ' + $mainPid + ')')
|
|
54
|
+
# Blocks on the process handle (WaitForSingleObject). No polling, no CPU, and
|
|
55
|
+
# no -Timeout (its ceiling on Windows PowerShell is 32767 seconds).
|
|
56
|
+
while ($true) {
|
|
57
|
+
if (-not (Get-Process -Id $mainPid -ErrorAction SilentlyContinue)) { break }
|
|
58
|
+
try {
|
|
59
|
+
Wait-Process -Id $mainPid -ErrorAction Stop
|
|
60
|
+
} catch {
|
|
61
|
+
if (-not (Get-Process -Id $mainPid -ErrorAction SilentlyContinue)) { break }
|
|
62
|
+
Start-Sleep -Milliseconds 200
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
Log 'main process exited (handle event)'
|
|
66
|
+
} else {
|
|
67
|
+
Log 'the app is not running; patching immediately'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Children can outlive the main process by a moment; wait for the tree to clear.
|
|
71
|
+
$deadline = (Get-Date).AddSeconds(30)
|
|
72
|
+
while ((Get-Date) -lt $deadline) {
|
|
73
|
+
if (-not (Get-Process -Name $app -ErrorAction SilentlyContinue)) { break }
|
|
74
|
+
Start-Sleep -Milliseconds 200
|
|
75
|
+
}
|
|
76
|
+
if (Get-Process -Name $app -ErrorAction SilentlyContinue) {
|
|
77
|
+
Log 'processes still alive after 30s; nothing changed'
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Let Windows drop the memory-mapped asar, then swap with retries.
|
|
82
|
+
Start-Sleep -Milliseconds 600
|
|
83
|
+
|
|
84
|
+
# Pre-flight: re-verify the staged archive against the original before touching it.
|
|
85
|
+
$verifyScript = Join-Path $PSScriptRoot 'patch-asar.cjs'
|
|
86
|
+
try {
|
|
87
|
+
& 'node' $verifyScript 'verify' '--resources' $res *> $null
|
|
88
|
+
if ($LASTEXITCODE -ne 0) {
|
|
89
|
+
Log 'pre-flight verification failed; nothing changed'
|
|
90
|
+
exit 1
|
|
91
|
+
}
|
|
92
|
+
Log 'pre-flight verification passed (byte-identical except lib/main.js)'
|
|
93
|
+
} catch {
|
|
94
|
+
Log ('pre-flight verification unavailable: ' + $_.Exception.Message + '; continuing')
|
|
95
|
+
}
|
|
96
|
+
$swapped = $false
|
|
97
|
+
for ($attempt = 1; $attempt -le 30; $attempt++) {
|
|
98
|
+
try {
|
|
99
|
+
if (Test-Path $orig) { Move-Item -Force $orig ($orig + '.' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
|
100
|
+
Move-Item -Force $asar $orig
|
|
101
|
+
Move-Item -Force $new $asar
|
|
102
|
+
$swapped = $true
|
|
103
|
+
break
|
|
104
|
+
} catch {
|
|
105
|
+
Log ('attempt ' + $attempt + ' failed: ' + $_.Exception.Message)
|
|
106
|
+
if ((-not (Test-Path $asar)) -and (Test-Path $orig)) { Move-Item -Force $orig $asar }
|
|
107
|
+
Start-Sleep -Milliseconds 400
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (-not $swapped) { Log 'could not swap app.asar; original preserved'; exit 1 }
|
|
112
|
+
|
|
113
|
+
$size = (Get-Item $asar).Length
|
|
114
|
+
if ($size -lt 213202950) {
|
|
115
|
+
Log ('patched file looks wrong (' + $size + ' bytes); restoring original')
|
|
116
|
+
Move-Item -Force $asar ($asar + '.broken')
|
|
117
|
+
Move-Item -Force $orig $asar
|
|
118
|
+
exit 1
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
Set-Content -Path $mark -Value ('patched ' + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'))
|
|
122
|
+
Log ('patched: app.asar is now ' + $size + ' bytes; original kept as app.asar.orig')
|
|
123
|
+
Log 'done; the app was NOT started - open it yourself when ready'
|
|
124
|
+
exit 0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
rem Free the disposable Chromium caches of the embedded browser, keep the logins.
|
|
3
|
+
rem
|
|
4
|
+
rem Usage: 清理聊天缓存.cmd [partitions-dir]
|
|
5
|
+
rem The directory may also come from DSH_PARTITIONS; the usual locations are
|
|
6
|
+
rem tried otherwise. Close DSH before running this.
|
|
7
|
+
if "%~1"=="" (
|
|
8
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\shell-patch\clean-cache.ps1"
|
|
9
|
+
) else (
|
|
10
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\shell-patch\clean-cache.ps1" -Partitions "%~1"
|
|
11
|
+
)
|
|
12
|
+
pause
|