@windypro-rourou/dsh-logcat 0.4.0 → 0.4.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.md CHANGED
@@ -43,6 +43,11 @@ DSH Web GUI 的安卓实机调试面板(类似 Android Studio 的 Logcat 视
43
43
  # 方式一(推荐,npm 安装):
44
44
  dsh plugin --profile web add @windypro-rourou/dsh-logcat
45
45
 
46
+ # 更新到最新版(不会自动更新;装旧版时面板/agent 通告会提示有新版本):
47
+ dsh plugin --profile web update # 升到当前 major 内最新
48
+ # 或强制最新:dsh plugin --profile web add @windypro-rourou/dsh-logcat@latest
49
+ # 更新后重启 GUI(dsh web)生效
50
+
46
51
  # 方式二(源码本地链接,实时生效无需重启):把插件链进 web profile,
47
52
  # 并在 ~/.dsh/profiles/web/cordis.patch.yml 增加一行:
48
53
  pnpm --dir "%USERPROFILE%\.dsh\profiles\web" add link:F:\dsh-logcat
package/lib/client.js CHANGED
@@ -114,6 +114,9 @@ window.__ModuleLoader__.load({
114
114
  const [currentPackage, setCurrentPackage] = useState("");
115
115
  const [pkgInput, setPkgInput] = useState("");
116
116
  const [installingAdb, setInstallingAdb] = useState(false);
117
+ const [currentVersion, setCurrentVersion] = useState("");
118
+ const [latestVersion, setLatestVersion] = useState("");
119
+ const [updateAvailable, setUpdateAvailable] = useState(false);
117
120
  const [level, setLevel] = useState("");
118
121
  const [keyword, setKeyword] = useState("");
119
122
  const [autoScroll, setAutoScroll] = useState(true);
@@ -249,6 +252,9 @@ window.__ModuleLoader__.load({
249
252
  setDevices(body.devices ?? []);
250
253
  setStreaming(body.streaming ?? []);
251
254
  setCurrentPackage(body.currentPackage ?? "");
255
+ setCurrentVersion(body.currentVersion ?? "");
256
+ setLatestVersion(body.latestVersion ?? "");
257
+ setUpdateAvailable(body.updateAvailable === true);
252
258
  devicesRef.current = body.devices ?? [];
253
259
  if (serialRef.current === "" || !(body.devices ?? []).some((d) => d.serial === serialRef.current)) {
254
260
  const first = (body.devices ?? []).find((d) => d.state === "device");
@@ -449,6 +455,12 @@ window.__ModuleLoader__.load({
449
455
  : null),
450
456
  h("span", null, "设备 " + devices.length + " · 在线 " + devices.filter((d) => d.state === "device").length),
451
457
  h("span", null, "显示 " + filtered.length + " / 缓冲 " + entries.length + " 行"),
458
+ h("span", { title: "插件版本" }, "v" + (currentVersion || "?")),
459
+ updateAvailable === true
460
+ ? h("span", { style: { color: "#fbc02d" } },
461
+ h("b", null, "新版本 " + latestVersion + " 可更新"),
462
+ "(dsh plugin --profile web update 后重启 GUI)")
463
+ : null,
452
464
  currentPackage !== ""
453
465
  ? h("span", { title: "当前测试应用包名(agent 通过 logcat_set_package 设置)" }, "测试: " + currentPackage)
454
466
  : null,
package/lib/index.js CHANGED
@@ -15,13 +15,32 @@
15
15
  */
16
16
 
17
17
  import { spawn, execFile } from 'node:child_process'
18
- import { existsSync } from 'node:fs'
18
+ import { existsSync, readFileSync } from 'node:fs'
19
19
  import { homedir } from 'node:os'
20
20
  import { dirname, join } from 'node:path'
21
21
  import { createInterface } from 'node:readline'
22
22
  import { WebSocket, WebSocketServer } from 'ws'
23
23
  import { defineTool } from '@deepseek-ai/dsh-tools'
24
24
 
25
+ /** This package's manifest (name + version), read from the installed location. */
26
+ const OWN_MANIFEST = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
27
+
28
+ /** Latest published version on npm (cached 1h; null until first check). */
29
+ let latestVersion = null
30
+ let lastVersionCheck = 0
31
+
32
+ /** Refresh the cached latest npm version of this package (offline-safe). */
33
+ async function ensureVersionCheck() {
34
+ if (latestVersion !== null && Date.now() - lastVersionCheck < 3600_000) return
35
+ lastVersionCheck = Date.now()
36
+ try {
37
+ const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(OWN_MANIFEST.name)}/latest`, {
38
+ signal: AbortSignal.timeout(8000),
39
+ })
40
+ if (res.ok) latestVersion = (await res.json()).version ?? null
41
+ } catch { /* offline / registry unreachable — keep whatever we had */ }
42
+ }
43
+
25
44
  /** Stable cordis plugin name. */
26
45
  export const name = 'logcat'
27
46
 
@@ -41,14 +60,14 @@ export const LOGCAT_GUIDANCE =
41
60
  '设备与状态:logcat_devices(列出设备)、device_info(型号/版本/SDK/分辨率/内存/电量)、device_stats(CPU/内存/电量采样);' +
42
61
  '部署与执行:adb_install(本地 APK 装真机)、adb_exec(任意 adb shell 命令)、adb_pull(拉文件)、input_tap/input_swipe/input_text(模拟点击/滑动/输入)、ui_dump(界面层级 XML);' +
43
62
  '日志与崩溃:logcat_recent(按级别/关键词/包名读日志)、logcat_crash(崩溃/ANR 捕获+上下文)、logcat_set_package(锁定当前测试包名);' +
44
- '逆向与内存:proc_list(进程列表)、proc_maps(内存映射+so 模块基址)、proc_status(进程状态/内存摘要)、mem_dump(指定地址读内存 hex)、frida_server(frida-server 部署/启停)。' +
63
+ '逆向与内存:proc_list(进程列表)、proc_maps(内存映射+so 模块基址)、proc_status(进程状态/内存摘要)、proc_smaps(内存占用明细 Top 区域)、mem_dump(指定地址读内存 hex)、mem_search(内存搜 hex 模式/字符串)、frida_server(frida-server 部署/启停)、frida_script(hook/trace/scan/bypass/dump 常用脚本模板)。' +
45
64
  '工作流建议:构建安卓应用 → 确认设备在线后 adb_install 部署、adb_exec 启动、logcat_set_package 锁包、logcat_recent/logcat_crash 看崩溃、ui_dump+input_* 做界面自动化;' +
46
65
  '逆向分析 → proc_list 找进程、proc_maps 拿模块基址、mem_dump 读内存、frida_server 起 frida。' +
47
66
  '限制:需设备开启 USB 调试并授权本机;读其他应用内存/maps 需要 root 或 debuggable 应用(run-as);logcat 输出可能含敏感信息;' +
48
67
  'adb 命令消耗真实设备资源,破坏性操作(卸载/重启/清数据/杀进程)先向用户确认再执行。' +
49
68
  '用户提到「Logcat / 安卓日志 / 实机调试 / adb 日志 / 逆向 / 读内存 / 抓包定位」时即指本插件,请据此主动协作。'
50
69
 
51
- /** Dynamic model-facing announcement: base guidance plus currently attached devices. */
70
+ /** Dynamic model-facing announcement: base guidance plus live device/package/version facts. */
52
71
  function logcatGuidance(engine) {
53
72
  return () => {
54
73
  const devices = engine.deviceList()
@@ -59,7 +78,10 @@ function logcatGuidance(engine) {
59
78
  const pkgLine = engine.currentPackage !== ''
60
79
  ? `当前测试应用包名:${engine.currentPackage}(logcat_recent 默认按它过滤日志)。`
61
80
  : ''
62
- return `${LOGCAT_GUIDANCE}\n${deviceLine}${pkgLine !== '' ? `\n${pkgLine}` : ''}`
81
+ const versionLine = latestVersion !== null && latestVersion !== OWN_MANIFEST.version
82
+ ? `dsh-logcat 有新版本:已装 ${OWN_MANIFEST.version},最新 ${latestVersion}(用户可执行 dsh plugin --profile web update 后重启 GUI 升级)。`
83
+ : ''
84
+ return `${LOGCAT_GUIDANCE}\n${deviceLine}${pkgLine !== '' ? `\n${pkgLine}` : ''}${versionLine !== '' ? `\n${versionLine}` : ''}`
63
85
  }
64
86
  }
65
87
 
@@ -484,6 +506,7 @@ function makeRoutes(engine) {
484
506
  handler: async (req, res) => {
485
507
  if (!isLoopbackRequest(req)) { writeJson(res, 403, { error: 'forbidden: loopback-only' }); return }
486
508
  if ((req.method ?? 'GET') !== 'GET') { writeJson(res, 405, { error: 'method not allowed' }); return }
509
+ await ensureVersionCheck()
487
510
  writeJson(res, 200, {
488
511
  adbPath: engine.adb,
489
512
  adbVersion: engine.adbVersion,
@@ -491,6 +514,9 @@ function makeRoutes(engine) {
491
514
  devices: engine.deviceList(),
492
515
  streaming: [...engine.streams.keys()],
493
516
  currentPackage: engine.currentPackage,
517
+ currentVersion: OWN_MANIFEST.version,
518
+ latestVersion,
519
+ updateAvailable: latestVersion !== null && latestVersion !== OWN_MANIFEST.version,
494
520
  })
495
521
  },
496
522
  },
@@ -1830,6 +1856,7 @@ export function apply(ctx, config) {
1830
1856
  const initOnce = () => {
1831
1857
  if (inited) return
1832
1858
  inited = true
1859
+ void ensureVersionCheck()
1833
1860
  void engine.init().then((ok) => {
1834
1861
  if (ok) engine.startPolling()
1835
1862
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@windypro-rourou/dsh-logcat",
3
3
  "description": "Android Logcat viewer for the dsh web GUI: auto-connects to any adb device in debug mode, live logcat stream with level/keyword filters, pause/clear/export, plus agent tools (logcat_recent). Hot-pluggable — mounted via ~/.dsh/cordis.patch.yml + a profile node_modules copy, no dsh source changes.",
4
- "version": "0.4.0",
4
+ "version": "0.4.2",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.22.0",
7
7
  "engines": {