@jaychang1989/dsh-webchat 0.7.0 → 0.7.2
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/README.en.md +9 -4
- package/README.md +9 -4
- package/lib/client.js +73 -40
- package/lib/index.js +52 -21
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -41,7 +41,7 @@ dsh plugin --profile desktop add github:jaychang1989/dsh-webchat
|
|
|
41
41
|
|
|
42
42
|
## Use
|
|
43
43
|
|
|
44
|
-
1. Click the "
|
|
44
|
+
1. Click the "chat DeepSeek" entry — the page loads in the center column; there is no second click.
|
|
45
45
|
2. Sign in to DeepSeek once — the plugin keeps that login across restarts (see below).
|
|
46
46
|
|
|
47
47
|
Click again to collapse the panel. Switching to another panel (Plugins, Automation Tasks, the task board, a session …) only hides the guest, never unmounts it, so coming back neither reloads the page nor drops the session.
|
|
@@ -54,12 +54,17 @@ Click again to collapse the panel. Switching to another panel (Plugins, Automati
|
|
|
54
54
|
|
|
55
55
|
## Staying signed in
|
|
56
56
|
|
|
57
|
-
The host hands browser guests a **process-lifetime** partition (a fresh random name per run, with no `persist:` prefix), so cookies and site storage would die with the app — DSH's own side-card browser behaves the same way. This plugin takes that over
|
|
57
|
+
The host hands browser guests a **process-lifetime** partition (a fresh random name per run, with no `persist:` prefix), so cookies and site storage would die with the app — DSH's own side-card browser behaves the same way. This plugin takes that over through **two channels**, because the host half does not always reach Electron:
|
|
58
|
+
|
|
59
|
+
1. **Site storage** (localStorage) and the **page's own cookies**: carried by the guest itself, generically — no key names, no Electron.
|
|
60
|
+
2. **Partition cookies, HttpOnly included**: only the Electron main process can read those. Used when reachable; `session.electron` in `GET /api/dsh-webchat/state` says plainly whether it is.
|
|
61
|
+
|
|
62
|
+
The order: reserve the lease → restore first (cookies land before the first navigation, so the page's very first request already carries them) → write storage and the page's cookies once the first load finishes → reload once → snapshot as the page is used.
|
|
58
63
|
|
|
59
64
|
- Location: `%USERPROFILE%\.dsh\dsh-webchat\session.json`
|
|
60
65
|
- **Deleting that file logs the plugin's guest out** — it keeps nothing else behind.
|
|
61
66
|
- The file holds live session credentials in **plain text**. It sits in your own profile directory (user-private by default), but it is not as protected as a browser's encrypted cookie store.
|
|
62
|
-
- If DeepSeek
|
|
67
|
+
- If DeepSeek keeps the session in an HttpOnly cookie *and* the host half cannot reach Electron, only the storage part can be kept — signing in again would then still happen.
|
|
63
68
|
|
|
64
69
|
## Known limitations
|
|
65
70
|
|
|
@@ -70,7 +75,7 @@ The host hands browser guests a **process-lifetime** partition (a fresh random n
|
|
|
70
75
|
|
|
71
76
|
| Symptom | Cause / fix |
|
|
72
77
|
| --- | --- |
|
|
73
|
-
| No "DeepSeek
|
|
78
|
+
| No "chat DeepSeek" row in the sidebar | Check that `@jaychang1989/dsh-webchat` is in the profile's `dsh.profile.bundles`, then restart the desktop app |
|
|
74
79
|
| The center column says "载入失败:…" | The host refused the guest (the bridge threw). The text carries the host's reason |
|
|
75
80
|
| The row opens a browser window instead of a panel | This renderer had no `dshDesktop.browser` (a plain web profile, for instance), so the fallback ran |
|
|
76
81
|
| The row opens but the center column is blank | The panel fills the cell the shell allocates; in a very small window, or with the sidebar dragged extremely narrow, that cell can have no area |
|
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ dsh plugin --profile desktop add github:jaychang1989/dsh-webchat
|
|
|
41
41
|
|
|
42
42
|
## 使用
|
|
43
43
|
|
|
44
|
-
1. 点侧边栏的「DeepSeek
|
|
44
|
+
1. 点侧边栏的「chat DeepSeek」入口 —— 页面直接在中栏载入,不需要第二次点击;
|
|
45
45
|
2. 在里面登录一次 DeepSeek,当前这次运行内一直有效。
|
|
46
46
|
|
|
47
47
|
再点一次入口收起面板。切换去别的面板(插件、自动化任务、任务看板、会话……)时访客只是被隐藏、从不被卸载,所以切回来不会重新加载、也不会掉登录。
|
|
@@ -54,12 +54,17 @@ dsh plugin --profile desktop add github:jaychang1989/dsh-webchat
|
|
|
54
54
|
|
|
55
55
|
## 登录状态
|
|
56
56
|
|
|
57
|
-
桌面端给浏览器访客的分区是**进程内**的(每次运行随机命名、不带 `persist:`),所以 cookie 和站点存储本来会随退出一起消失——DSH
|
|
57
|
+
桌面端给浏览器访客的分区是**进程内**的(每次运行随机命名、不带 `persist:`),所以 cookie 和站点存储本来会随退出一起消失——DSH 自带的侧栏浏览器也是这样。本插件把这个接管了,用**两条通道**——因为宿主半区不一定拿得到 Electron:
|
|
58
|
+
|
|
59
|
+
1. **站点存储**(localStorage)与**页面自己的 cookie**:由页面侧通用搬运,不依赖任何键名,也不依赖 Electron;
|
|
60
|
+
2. **分区 cookie(含 HttpOnly)**:只有宿主的 Electron 主进程读得到。能拿到就走这条;`GET /api/dsh-webchat/state` 的 `session.electron` 会如实说明。
|
|
61
|
+
|
|
62
|
+
顺序:拿到租约 → 回灌(cookie 在首次导航前写回,页面第一个请求就带着它)→ 首次加载完成后再写入存储与 cookie → 刷新一次 → 之后按使用情况快照。
|
|
58
63
|
|
|
59
64
|
- 文件位置:`%USERPROFILE%\.dsh\dsh-webchat\session.json`
|
|
60
65
|
- **删掉这个文件就等于退出登录**(本插件不会再有别的残留)。
|
|
61
66
|
- 文件里是**明文**会话凭据。它在你自己的用户目录下(默认只有你的账户可读),但确实不如浏览器那种加密 cookie 库。
|
|
62
|
-
-
|
|
67
|
+
- 如果 DeepSeek 的登录态是 HttpOnly cookie、而宿主又拿不到 Electron,就只能保留存储部分——那种情况下重登仍会发生。
|
|
63
68
|
|
|
64
69
|
## 已知限制
|
|
65
70
|
|
|
@@ -70,7 +75,7 @@ dsh plugin --profile desktop add github:jaychang1989/dsh-webchat
|
|
|
70
75
|
|
|
71
76
|
| 现象 | 原因 / 处理 |
|
|
72
77
|
| --- | --- |
|
|
73
|
-
| 侧边栏没出现「DeepSeek
|
|
78
|
+
| 侧边栏没出现「chat DeepSeek」这一行 | 确认 profile 的 `dsh.profile.bundles` 里有 `@jaychang1989/dsh-webchat`,然后重启桌面端 |
|
|
74
79
|
| 中栏提示「载入失败:…」 | 宿主拒绝了访客(桥接返回异常)。文本里带着宿主给的原因 |
|
|
75
80
|
| 这一行点了但中栏没有页面,反而弹出浏览器窗口 | 说明当前渲染进程拿不到 `dshDesktop.browser`(例如在纯 web 环境),插件走了降级路径 |
|
|
76
81
|
| 这一行点了但中栏是空白 | 面板显示的空间是 shell 分配的那个格子;若窗口极小或侧栏被拖到极窄,格子可能没有面积 |
|
package/lib/client.js
CHANGED
|
@@ -60,24 +60,28 @@ window.__ModuleLoader__.load({
|
|
|
60
60
|
return String(tag).toLowerCase().indexOf("zh") === 0
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Every visible string this plugin owns. The row's name is deliberately
|
|
65
|
+
* the same in both languages: it is the product's own name, not a
|
|
66
|
+
* translation.
|
|
67
|
+
*/
|
|
64
68
|
function copy() {
|
|
65
69
|
return isChinese()
|
|
66
70
|
? {
|
|
67
|
-
label: "DeepSeek
|
|
71
|
+
label: "chat DeepSeek",
|
|
68
72
|
failed: "载入失败",
|
|
69
73
|
fallbackNote: "这个 Harness 没有提供原生浏览器访客(桌面端才有),无法在窗口内显示官方页面。",
|
|
70
74
|
fallbackAction: "在窗口中打开",
|
|
71
|
-
fallbackOpened: "已打开 DeepSeek
|
|
75
|
+
fallbackOpened: "已打开 chat DeepSeek",
|
|
72
76
|
fallbackHanded: "已交给系统默认浏览器打开",
|
|
73
77
|
fallbackFailed: "打开失败",
|
|
74
78
|
}
|
|
75
79
|
: {
|
|
76
|
-
label: "DeepSeek
|
|
80
|
+
label: "chat DeepSeek",
|
|
77
81
|
failed: "Could not load it",
|
|
78
82
|
fallbackNote: "This Harness provides no native browser guest (only the desktop app does), so the official page cannot render in the window.",
|
|
79
83
|
fallbackAction: "Open it in a window",
|
|
80
|
-
fallbackOpened: "DeepSeek
|
|
84
|
+
fallbackOpened: "chat DeepSeek is open",
|
|
81
85
|
fallbackHanded: "Handed to the system default browser",
|
|
82
86
|
fallbackFailed: "Could not open",
|
|
83
87
|
}
|
|
@@ -207,28 +211,50 @@ window.__ModuleLoader__.load({
|
|
|
207
211
|
return box
|
|
208
212
|
}
|
|
209
213
|
|
|
210
|
-
/**
|
|
211
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Read everything the guest page itself can reach: its storage and its
|
|
216
|
+
* own cookie string. That string cannot carry HttpOnly cookies — only the
|
|
217
|
+
* host half can, and only when it reaches Electron — but it is what keeps
|
|
218
|
+
* a login alive when it cannot.
|
|
219
|
+
* @param element - the live webview.
|
|
220
|
+
* @returns the guest state as JSON text.
|
|
221
|
+
*/
|
|
222
|
+
function readGuestState(element) {
|
|
212
223
|
return element.executeJavaScript(
|
|
213
|
-
"JSON.stringify(Object.keys(localStorage).map(function (key) { return [key, localStorage.getItem(key)] }))",
|
|
224
|
+
"JSON.stringify({ storage: Object.keys(localStorage).map(function (key) { return [key, localStorage.getItem(key)] }), cookie: document.cookie })",
|
|
214
225
|
)
|
|
215
226
|
}
|
|
216
227
|
|
|
217
|
-
/**
|
|
218
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Write a guest state back into the page: storage keys, then cookies.
|
|
230
|
+
* Cookies are re-scoped to the site root, which is where a login cookie
|
|
231
|
+
* belongs; the original attributes are not recoverable from
|
|
232
|
+
* `document.cookie`.
|
|
233
|
+
* @param element - the live webview.
|
|
234
|
+
* @param state - `{ storage, cookie }`.
|
|
235
|
+
*/
|
|
236
|
+
function writeGuestState(element, state) {
|
|
219
237
|
return element.executeJavaScript(
|
|
220
|
-
"(function () { var
|
|
221
|
-
+ "
|
|
222
|
-
+ "
|
|
238
|
+
"(function () { var state = " + JSON.stringify(state) + ";"
|
|
239
|
+
+ " var storage = Array.isArray(state.storage) ? state.storage : [];"
|
|
240
|
+
+ " for (var i = 0; i < storage.length; i++) { try { localStorage.setItem(storage[i][0], storage[i][1]) } catch (error) {} }"
|
|
241
|
+
+ " if (typeof state.cookie === 'string' && state.cookie !== '') {"
|
|
242
|
+
+ " var parts = state.cookie.split('; ');"
|
|
243
|
+
+ " for (var j = 0; j < parts.length; j++) {"
|
|
244
|
+
+ " var pair = parts[j];"
|
|
245
|
+
+ " if (pair.indexOf('=') === -1) continue;"
|
|
246
|
+
+ " try { document.cookie = pair + '; path=/' } catch (error) {}"
|
|
247
|
+
+ " } }"
|
|
248
|
+
+ " return true })()",
|
|
223
249
|
)
|
|
224
250
|
}
|
|
225
251
|
|
|
226
252
|
/**
|
|
227
|
-
* Ask the host to
|
|
228
|
-
*
|
|
229
|
-
*
|
|
253
|
+
* Ask the host to put the saved session back before the page is
|
|
254
|
+
* navigated: cookies through the shell's session API where that is
|
|
255
|
+
* reachable, and the guest's own storage and cookie string either way.
|
|
230
256
|
* @param partition - the partition the shell issued for the lease.
|
|
231
|
-
* @returns
|
|
257
|
+
* @returns `{ storage, cookie }` to apply, or null when there is nothing.
|
|
232
258
|
*/
|
|
233
259
|
async function restoreGuestSession(partition) {
|
|
234
260
|
try {
|
|
@@ -238,11 +264,14 @@ window.__ModuleLoader__.load({
|
|
|
238
264
|
body: JSON.stringify({ partition }),
|
|
239
265
|
})
|
|
240
266
|
const payload = await response.json()
|
|
241
|
-
if (payload === null || typeof payload !== "object"
|
|
242
|
-
|
|
243
|
-
|
|
267
|
+
if (payload === null || typeof payload !== "object") return null
|
|
268
|
+
if (payload.ok !== true) {
|
|
269
|
+
console.warn("[dsh-webchat] session cookies:", payload.error || "not restored")
|
|
244
270
|
}
|
|
245
|
-
|
|
271
|
+
const storage = Array.isArray(payload.storage) ? payload.storage : []
|
|
272
|
+
const cookie = typeof payload.cookie === "string" ? payload.cookie : ""
|
|
273
|
+
if (storage.length === 0 && cookie === "") return null
|
|
274
|
+
return { storage, cookie }
|
|
246
275
|
} catch (error) {
|
|
247
276
|
console.warn("[dsh-webchat] session restore failed:", error)
|
|
248
277
|
return null
|
|
@@ -250,23 +279,26 @@ window.__ModuleLoader__.load({
|
|
|
250
279
|
}
|
|
251
280
|
|
|
252
281
|
/**
|
|
253
|
-
* Ask the host to refresh the snapshot.
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* @param
|
|
282
|
+
* Ask the host to refresh the snapshot. `withState` ships what only the
|
|
283
|
+
* guest can read; the unload beacon deliberately skips it (the host then
|
|
284
|
+
* keeps the previous values).
|
|
285
|
+
* @param withState - whether to include the guest's own state.
|
|
257
286
|
* @param target - the guest record; defaults to the live one, which
|
|
258
287
|
* disposal clears before its last save.
|
|
259
288
|
*/
|
|
260
|
-
async function saveGuestSession(
|
|
289
|
+
async function saveGuestSession(withState, target) {
|
|
261
290
|
const record = target === undefined ? guest : target
|
|
262
291
|
if (record === null || record === undefined) return
|
|
263
292
|
const body = { partition: record.partition }
|
|
264
|
-
if (
|
|
293
|
+
if (withState) {
|
|
265
294
|
try {
|
|
266
|
-
const
|
|
267
|
-
if (
|
|
295
|
+
const state = JSON.parse(await readGuestState(record.element))
|
|
296
|
+
if (state !== null && typeof state === "object") {
|
|
297
|
+
if (Array.isArray(state.storage)) body.storage = state.storage
|
|
298
|
+
if (typeof state.cookie === "string") body.cookie = state.cookie
|
|
299
|
+
}
|
|
268
300
|
} catch (error) {
|
|
269
|
-
// Not ready
|
|
301
|
+
// Not ready: the host still snapshots whatever it can reach.
|
|
270
302
|
}
|
|
271
303
|
}
|
|
272
304
|
try {
|
|
@@ -294,8 +326,9 @@ window.__ModuleLoader__.load({
|
|
|
294
326
|
|| typeof reservation.lease !== "string" || typeof reservation.partition !== "string") {
|
|
295
327
|
throw new Error("the desktop browser bridge returned no reservation")
|
|
296
328
|
}
|
|
297
|
-
// Cookies land on the partition before anything is requested
|
|
298
|
-
|
|
329
|
+
// Cookies land on the partition before anything is requested; the
|
|
330
|
+
// guest's own storage and cookie string come back with them.
|
|
331
|
+
const restored = await restoreGuestSession(reservation.partition)
|
|
299
332
|
const element = document.createElement("webview")
|
|
300
333
|
element.setAttribute("name", reservation.lease)
|
|
301
334
|
element.setAttribute("partition", reservation.partition)
|
|
@@ -317,17 +350,17 @@ window.__ModuleLoader__.load({
|
|
|
317
350
|
failure = messageOf(error)
|
|
318
351
|
})
|
|
319
352
|
}, { once: true })
|
|
320
|
-
//
|
|
321
|
-
// origin, so
|
|
322
|
-
// page is asked to load once more — this time already
|
|
323
|
-
// Until that settles, snapshots are skipped: the
|
|
324
|
-
// page still has empty storage, and saving that
|
|
325
|
-
// very snapshot being restored.
|
|
326
|
-
let storageState =
|
|
353
|
+
// Storage and cookies can only be written while the page is on
|
|
354
|
+
// its own origin, so both are restored after the first load and
|
|
355
|
+
// the page is then asked to load once more — this time already
|
|
356
|
+
// signed in. Until that settles, snapshots are skipped: the
|
|
357
|
+
// freshly loaded page still has empty storage, and saving that
|
|
358
|
+
// would erase the very snapshot being restored.
|
|
359
|
+
let storageState = restored === null ? "none" : "pending"
|
|
327
360
|
element.addEventListener("did-finish-load", function () {
|
|
328
361
|
if (storageState !== "pending") return
|
|
329
362
|
storageState = "restoring"
|
|
330
|
-
Promise.resolve(
|
|
363
|
+
Promise.resolve(writeGuestState(element, restored))
|
|
331
364
|
.then(function () {
|
|
332
365
|
storageState = "done"
|
|
333
366
|
element.reload()
|
package/lib/index.js
CHANGED
|
@@ -134,6 +134,10 @@ export function readSnapshot(file) {
|
|
|
134
134
|
return {
|
|
135
135
|
cookies: Array.isArray(parsed.cookies) ? parsed.cookies : [],
|
|
136
136
|
storage: Array.isArray(parsed.storage) ? parsed.storage : [],
|
|
137
|
+
// The page's own `document.cookie` string, kept by the browser half. It
|
|
138
|
+
// cannot carry HttpOnly cookies, but it is what survives when this process
|
|
139
|
+
// has no Electron session API at all.
|
|
140
|
+
cookie: typeof parsed.cookie === 'string' ? parsed.cookie : '',
|
|
137
141
|
savedAt: typeof parsed.savedAt === 'string' ? parsed.savedAt : '',
|
|
138
142
|
}
|
|
139
143
|
} catch (error) {
|
|
@@ -288,8 +292,17 @@ function messageOf(error) {
|
|
|
288
292
|
*/
|
|
289
293
|
async function electronApi() {
|
|
290
294
|
if (electronLoader !== null) return electronLoader()
|
|
291
|
-
|
|
292
|
-
|
|
295
|
+
// Electron's ESM namespace can carry the real API under `default` when its
|
|
296
|
+
// named exports are not statically detectable, and this loader hands back
|
|
297
|
+
// exactly that shape, so both forms are accepted.
|
|
298
|
+
const usable = (mod) => {
|
|
299
|
+
if (mod === null || typeof mod !== 'object') return undefined
|
|
300
|
+
if (mod.BrowserWindow !== undefined || mod.session !== undefined) return mod
|
|
301
|
+
const inner = mod.default
|
|
302
|
+
if (inner !== null && typeof inner === 'object'
|
|
303
|
+
&& (inner.BrowserWindow !== undefined || inner.session !== undefined)) return inner
|
|
304
|
+
return undefined
|
|
305
|
+
}
|
|
293
306
|
const failures = []
|
|
294
307
|
try {
|
|
295
308
|
const fromImport = usable(await import('electron'))
|
|
@@ -506,18 +519,28 @@ export function apply(ctx) {
|
|
|
506
519
|
}
|
|
507
520
|
const snapshot = readSnapshot(SESSION_FILE())
|
|
508
521
|
if (snapshot === null) {
|
|
509
|
-
json(res, 200, { ok: true, cookies: 0, storage: null, savedAt: '' })
|
|
522
|
+
json(res, 200, { ok: true, cookies: 0, storage: null, cookie: '', savedAt: '' })
|
|
510
523
|
return
|
|
511
524
|
}
|
|
525
|
+
let restored = 0
|
|
526
|
+
let error = ''
|
|
512
527
|
try {
|
|
513
528
|
const session = await partitionSession(partition)
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
|
|
529
|
+
restored = await restoreCookies(session.cookies, snapshot)
|
|
530
|
+
} catch (failure) {
|
|
531
|
+
// Without a session API the HttpOnly cookies cannot come back, but the
|
|
532
|
+
// guest still gets its storage and its own cookie string, so this is
|
|
533
|
+
// reported rather than fatal.
|
|
534
|
+
error = messageOf(failure)
|
|
520
535
|
}
|
|
536
|
+
json(res, 200, {
|
|
537
|
+
ok: error === '',
|
|
538
|
+
error,
|
|
539
|
+
cookies: restored,
|
|
540
|
+
storage: snapshot.storage,
|
|
541
|
+
cookie: snapshot.cookie,
|
|
542
|
+
savedAt: snapshot.savedAt,
|
|
543
|
+
})
|
|
521
544
|
},
|
|
522
545
|
}), 'dsh-webchat: session restore route')
|
|
523
546
|
|
|
@@ -537,21 +560,29 @@ export function apply(ctx) {
|
|
|
537
560
|
json(res, 400, { ok: false, error: 'dsh-webchat: not a browser-guest partition' })
|
|
538
561
|
return
|
|
539
562
|
}
|
|
563
|
+
const file = SESSION_FILE()
|
|
564
|
+
const previous = readSnapshot(file)
|
|
565
|
+
// A save without site storage (the unload beacon) must not erase what an
|
|
566
|
+
// earlier save captured.
|
|
567
|
+
const storage = body !== null && Array.isArray(body.storage)
|
|
568
|
+
? body.storage
|
|
569
|
+
: (previous === null ? [] : previous.storage)
|
|
570
|
+
const cookie = body !== null && typeof body.cookie === 'string'
|
|
571
|
+
? body.cookie
|
|
572
|
+
: (previous === null ? '' : previous.cookie)
|
|
573
|
+
// The snapshot is written even when this process cannot reach Electron:
|
|
574
|
+
// site storage and the page's own cookie string are carried by the guest
|
|
575
|
+
// and are still worth keeping.
|
|
576
|
+
let cookies = []
|
|
577
|
+
let error = ''
|
|
540
578
|
try {
|
|
541
579
|
const session = await partitionSession(partition)
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
// A save without site storage (the unload beacon) must not erase what an
|
|
546
|
-
// earlier save captured.
|
|
547
|
-
const storage = body !== null && Array.isArray(body.storage)
|
|
548
|
-
? body.storage
|
|
549
|
-
: (previous === null ? [] : previous.storage)
|
|
550
|
-
writeSnapshot(file, { version: 1, savedAt: new Date().toISOString(), cookies, storage })
|
|
551
|
-
json(res, 200, { ok: true, cookies: cookies.length, storage: storage.length })
|
|
552
|
-
} catch (error) {
|
|
553
|
-
json(res, 502, { ok: false, error: messageOf(error) })
|
|
580
|
+
cookies = await captureCookies(session.cookies)
|
|
581
|
+
} catch (failure) {
|
|
582
|
+
error = messageOf(failure)
|
|
554
583
|
}
|
|
584
|
+
writeSnapshot(file, { version: 1, savedAt: new Date().toISOString(), cookies, storage, cookie })
|
|
585
|
+
json(res, 200, { ok: true, cookies: cookies.length, storage: storage.length, cookie: cookie !== '', error })
|
|
555
586
|
},
|
|
556
587
|
}), 'dsh-webchat: session save route')
|
|
557
588
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaychang1989/dsh-webchat",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Opens the official DeepSeek web app (chat.deepseek.com) inside DeepSeek Harness: one sidebar entry renders the real page in the center column, through the same native browser guest the built-in side-card browser uses. No chat UI, no agent tools, no runtime dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|