@cxyhhhhh/openclaw-qqbot 1.6.7-alpha.1

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.
Files changed (218) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +470 -0
  3. package/README.zh.md +465 -0
  4. package/bin/qqbot-cli.js +243 -0
  5. package/dist/index.d.ts +17 -0
  6. package/dist/index.js +26 -0
  7. package/dist/src/admin-resolver.d.ts +33 -0
  8. package/dist/src/admin-resolver.js +157 -0
  9. package/dist/src/api.d.ts +264 -0
  10. package/dist/src/api.js +777 -0
  11. package/dist/src/channel.d.ts +29 -0
  12. package/dist/src/channel.js +452 -0
  13. package/dist/src/config.d.ts +56 -0
  14. package/dist/src/config.js +278 -0
  15. package/dist/src/credential-backup.d.ts +31 -0
  16. package/dist/src/credential-backup.js +66 -0
  17. package/dist/src/deliver-debounce.d.ts +74 -0
  18. package/dist/src/deliver-debounce.js +174 -0
  19. package/dist/src/gateway.d.ts +18 -0
  20. package/dist/src/gateway.js +2021 -0
  21. package/dist/src/group-history.d.ts +136 -0
  22. package/dist/src/group-history.js +226 -0
  23. package/dist/src/image-server.d.ts +87 -0
  24. package/dist/src/image-server.js +570 -0
  25. package/dist/src/inbound-attachments.d.ts +60 -0
  26. package/dist/src/inbound-attachments.js +248 -0
  27. package/dist/src/known-users.d.ts +100 -0
  28. package/dist/src/known-users.js +263 -0
  29. package/dist/src/message-gating.d.ts +53 -0
  30. package/dist/src/message-gating.js +107 -0
  31. package/dist/src/message-queue.d.ts +86 -0
  32. package/dist/src/message-queue.js +257 -0
  33. package/dist/src/onboarding.d.ts +10 -0
  34. package/dist/src/onboarding.js +203 -0
  35. package/dist/src/outbound-deliver.d.ts +48 -0
  36. package/dist/src/outbound-deliver.js +392 -0
  37. package/dist/src/outbound.d.ts +205 -0
  38. package/dist/src/outbound.js +926 -0
  39. package/dist/src/proactive.d.ts +170 -0
  40. package/dist/src/proactive.js +399 -0
  41. package/dist/src/ref-index-store.d.ts +70 -0
  42. package/dist/src/ref-index-store.js +250 -0
  43. package/dist/src/reply-dispatcher.d.ts +35 -0
  44. package/dist/src/reply-dispatcher.js +311 -0
  45. package/dist/src/request-context.d.ts +18 -0
  46. package/dist/src/request-context.js +30 -0
  47. package/dist/src/runtime.d.ts +3 -0
  48. package/dist/src/runtime.js +10 -0
  49. package/dist/src/session-store.d.ts +52 -0
  50. package/dist/src/session-store.js +254 -0
  51. package/dist/src/slash-commands.d.ts +77 -0
  52. package/dist/src/slash-commands.js +1461 -0
  53. package/dist/src/startup-greeting.d.ts +30 -0
  54. package/dist/src/startup-greeting.js +97 -0
  55. package/dist/src/streaming.d.ts +250 -0
  56. package/dist/src/streaming.js +914 -0
  57. package/dist/src/stt.d.ts +21 -0
  58. package/dist/src/stt.js +70 -0
  59. package/dist/src/tools/channel.d.ts +16 -0
  60. package/dist/src/tools/channel.js +234 -0
  61. package/dist/src/tools/remind.d.ts +2 -0
  62. package/dist/src/tools/remind.js +248 -0
  63. package/dist/src/types.d.ts +364 -0
  64. package/dist/src/types.js +17 -0
  65. package/dist/src/typing-keepalive.d.ts +27 -0
  66. package/dist/src/typing-keepalive.js +64 -0
  67. package/dist/src/update-checker.d.ts +34 -0
  68. package/dist/src/update-checker.js +160 -0
  69. package/dist/src/utils/audio-convert.d.ts +98 -0
  70. package/dist/src/utils/audio-convert.js +755 -0
  71. package/dist/src/utils/chunked-upload.d.ts +59 -0
  72. package/dist/src/utils/chunked-upload.js +289 -0
  73. package/dist/src/utils/file-utils.d.ts +61 -0
  74. package/dist/src/utils/file-utils.js +172 -0
  75. package/dist/src/utils/image-size.d.ts +51 -0
  76. package/dist/src/utils/image-size.js +234 -0
  77. package/dist/src/utils/media-send.d.ts +148 -0
  78. package/dist/src/utils/media-send.js +456 -0
  79. package/dist/src/utils/media-tags.d.ts +14 -0
  80. package/dist/src/utils/media-tags.js +164 -0
  81. package/dist/src/utils/payload.d.ts +112 -0
  82. package/dist/src/utils/payload.js +186 -0
  83. package/dist/src/utils/pkg-version.d.ts +5 -0
  84. package/dist/src/utils/pkg-version.js +51 -0
  85. package/dist/src/utils/platform.d.ts +137 -0
  86. package/dist/src/utils/platform.js +390 -0
  87. package/dist/src/utils/ssrf-guard.d.ts +25 -0
  88. package/dist/src/utils/ssrf-guard.js +91 -0
  89. package/dist/src/utils/text-parsing.d.ts +32 -0
  90. package/dist/src/utils/text-parsing.js +69 -0
  91. package/dist/src/utils/upload-cache.d.ts +34 -0
  92. package/dist/src/utils/upload-cache.js +93 -0
  93. package/index.ts +31 -0
  94. package/node_modules/@eshaz/web-worker/LICENSE +201 -0
  95. package/node_modules/@eshaz/web-worker/README.md +134 -0
  96. package/node_modules/@eshaz/web-worker/browser.js +17 -0
  97. package/node_modules/@eshaz/web-worker/cjs/browser.js +16 -0
  98. package/node_modules/@eshaz/web-worker/cjs/node.js +219 -0
  99. package/node_modules/@eshaz/web-worker/index.d.ts +4 -0
  100. package/node_modules/@eshaz/web-worker/node.js +223 -0
  101. package/node_modules/@eshaz/web-worker/package.json +54 -0
  102. package/node_modules/@wasm-audio-decoders/common/index.js +5 -0
  103. package/node_modules/@wasm-audio-decoders/common/package.json +36 -0
  104. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +231 -0
  105. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +129 -0
  106. package/node_modules/@wasm-audio-decoders/common/src/puff/README +67 -0
  107. package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +31 -0
  108. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +863 -0
  109. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +35 -0
  110. package/node_modules/@wasm-audio-decoders/common/src/utilities.js +3 -0
  111. package/node_modules/@wasm-audio-decoders/common/types.d.ts +7 -0
  112. package/node_modules/mpg123-decoder/README.md +265 -0
  113. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +185 -0
  114. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +1 -0
  115. package/node_modules/mpg123-decoder/index.js +8 -0
  116. package/node_modules/mpg123-decoder/package.json +58 -0
  117. package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +464 -0
  118. package/node_modules/mpg123-decoder/src/MPEGDecoder.js +200 -0
  119. package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +21 -0
  120. package/node_modules/mpg123-decoder/types.d.ts +30 -0
  121. package/node_modules/silk-wasm/LICENSE +21 -0
  122. package/node_modules/silk-wasm/README.md +85 -0
  123. package/node_modules/silk-wasm/lib/index.cjs +16 -0
  124. package/node_modules/silk-wasm/lib/index.d.ts +70 -0
  125. package/node_modules/silk-wasm/lib/index.mjs +16 -0
  126. package/node_modules/silk-wasm/lib/silk.wasm +0 -0
  127. package/node_modules/silk-wasm/lib/utils.d.ts +4 -0
  128. package/node_modules/silk-wasm/package.json +39 -0
  129. package/node_modules/simple-yenc/.github/FUNDING.yml +1 -0
  130. package/node_modules/simple-yenc/.prettierignore +1 -0
  131. package/node_modules/simple-yenc/LICENSE +7 -0
  132. package/node_modules/simple-yenc/README.md +163 -0
  133. package/node_modules/simple-yenc/dist/esm.js +1 -0
  134. package/node_modules/simple-yenc/dist/index.js +1 -0
  135. package/node_modules/simple-yenc/package.json +50 -0
  136. package/node_modules/simple-yenc/rollup.config.js +27 -0
  137. package/node_modules/simple-yenc/src/simple-yenc.js +302 -0
  138. package/node_modules/ws/LICENSE +20 -0
  139. package/node_modules/ws/README.md +548 -0
  140. package/node_modules/ws/browser.js +8 -0
  141. package/node_modules/ws/index.js +13 -0
  142. package/node_modules/ws/lib/buffer-util.js +131 -0
  143. package/node_modules/ws/lib/constants.js +19 -0
  144. package/node_modules/ws/lib/event-target.js +292 -0
  145. package/node_modules/ws/lib/extension.js +203 -0
  146. package/node_modules/ws/lib/limiter.js +55 -0
  147. package/node_modules/ws/lib/permessage-deflate.js +528 -0
  148. package/node_modules/ws/lib/receiver.js +706 -0
  149. package/node_modules/ws/lib/sender.js +602 -0
  150. package/node_modules/ws/lib/stream.js +161 -0
  151. package/node_modules/ws/lib/subprotocol.js +62 -0
  152. package/node_modules/ws/lib/validation.js +152 -0
  153. package/node_modules/ws/lib/websocket-server.js +554 -0
  154. package/node_modules/ws/lib/websocket.js +1393 -0
  155. package/node_modules/ws/package.json +69 -0
  156. package/node_modules/ws/wrapper.mjs +8 -0
  157. package/openclaw.plugin.json +17 -0
  158. package/package.json +67 -0
  159. package/preload.cjs +33 -0
  160. package/scripts/cleanup-legacy-plugins.sh +124 -0
  161. package/scripts/link-sdk-core.cjs +185 -0
  162. package/scripts/postinstall-link-sdk.js +113 -0
  163. package/scripts/proactive-api-server.ts +369 -0
  164. package/scripts/send-proactive.ts +293 -0
  165. package/scripts/set-markdown.sh +156 -0
  166. package/scripts/test-sendmedia.ts +116 -0
  167. package/scripts/upgrade-via-npm.ps1 +451 -0
  168. package/scripts/upgrade-via-npm.sh +528 -0
  169. package/scripts/upgrade-via-source.sh +916 -0
  170. package/skills/qqbot-channel/SKILL.md +263 -0
  171. package/skills/qqbot-channel/references/api_references.md +521 -0
  172. package/skills/qqbot-media/SKILL.md +60 -0
  173. package/skills/qqbot-remind/SKILL.md +149 -0
  174. package/src/admin-resolver.ts +181 -0
  175. package/src/api.ts +1138 -0
  176. package/src/channel.ts +477 -0
  177. package/src/config.ts +347 -0
  178. package/src/credential-backup.ts +72 -0
  179. package/src/deliver-debounce.ts +229 -0
  180. package/src/gateway.ts +2257 -0
  181. package/src/group-history.ts +328 -0
  182. package/src/image-server.ts +675 -0
  183. package/src/inbound-attachments.ts +321 -0
  184. package/src/known-users.ts +353 -0
  185. package/src/message-gating.ts +190 -0
  186. package/src/message-queue.ts +349 -0
  187. package/src/onboarding.ts +274 -0
  188. package/src/openclaw-plugin-sdk.d.ts +587 -0
  189. package/src/outbound-deliver.ts +473 -0
  190. package/src/outbound.ts +1119 -0
  191. package/src/proactive.ts +530 -0
  192. package/src/ref-index-store.ts +335 -0
  193. package/src/reply-dispatcher.ts +334 -0
  194. package/src/request-context.ts +39 -0
  195. package/src/runtime.ts +14 -0
  196. package/src/session-store.ts +303 -0
  197. package/src/slash-commands.ts +1615 -0
  198. package/src/startup-greeting.ts +120 -0
  199. package/src/streaming.ts +1102 -0
  200. package/src/stt.ts +86 -0
  201. package/src/tools/channel.ts +281 -0
  202. package/src/tools/remind.ts +300 -0
  203. package/src/types.ts +386 -0
  204. package/src/typing-keepalive.ts +59 -0
  205. package/src/update-checker.ts +174 -0
  206. package/src/utils/audio-convert.ts +859 -0
  207. package/src/utils/chunked-upload.ts +419 -0
  208. package/src/utils/file-utils.ts +193 -0
  209. package/src/utils/image-size.ts +266 -0
  210. package/src/utils/media-send.ts +585 -0
  211. package/src/utils/media-tags.ts +182 -0
  212. package/src/utils/payload.ts +265 -0
  213. package/src/utils/pkg-version.ts +54 -0
  214. package/src/utils/platform.ts +435 -0
  215. package/src/utils/ssrf-guard.ts +102 -0
  216. package/src/utils/text-parsing.ts +75 -0
  217. package/src/utils/upload-cache.ts +128 -0
  218. package/tsconfig.json +16 -0
@@ -0,0 +1,451 @@
1
+ # qqbot upgrade via npm package (Windows PowerShell)
2
+ #
3
+ # Windows-native equivalent of upgrade-via-npm.sh.
4
+ # No bash / Git Bash / WSL required.
5
+ #
6
+ # Usage:
7
+ # .\upgrade-via-npm.ps1 # upgrade to latest (default)
8
+ # .\upgrade-via-npm.ps1 -Version <version> # upgrade to specific version
9
+ # .\upgrade-via-npm.ps1 -SelfVersion # upgrade to local package.json version
10
+ # .\upgrade-via-npm.ps1 -AppId <appid> -Secret <secret> # configure on first install
11
+ # .\upgrade-via-npm.ps1 -NoRestart # file replacement only (for hot-upgrade)
12
+
13
+ param(
14
+ [string]$Version = "",
15
+ [switch]$SelfVersion,
16
+ [string]$AppId = "",
17
+ [string]$Secret = "",
18
+ [switch]$NoRestart,
19
+ [string]$Tag = "",
20
+ [switch]$Help
21
+ )
22
+
23
+ $ErrorActionPreference = "Stop"
24
+ $PKG_NAME = "@tencent-connect/openclaw-qqbot"
25
+ $SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Definition
26
+ $PROJECT_DIR = Split-Path -Parent $SCRIPT_DIR
27
+
28
+ # Read local version
29
+ $LOCAL_VERSION = ""
30
+ try {
31
+ $pkgPath = Join-Path $PROJECT_DIR "package.json"
32
+ if (Test-Path $pkgPath) {
33
+ $pkg = Get-Content $pkgPath -Raw | ConvertFrom-Json
34
+ $LOCAL_VERSION = $pkg.version
35
+ }
36
+ } catch {}
37
+
38
+ if ($Help) {
39
+ Write-Host "Usage:"
40
+ Write-Host " .\upgrade-via-npm.ps1 # upgrade to latest (default)"
41
+ Write-Host " .\upgrade-via-npm.ps1 -Version [version] # upgrade to specific version"
42
+ Write-Host " .\upgrade-via-npm.ps1 -SelfVersion # upgrade to repo version ($LOCAL_VERSION)"
43
+ Write-Host ""
44
+ Write-Host " -AppId [appid] QQ bot appid (required on first install)"
45
+ Write-Host " -Secret [secret] QQ bot secret (required on first install)"
46
+ exit 0
47
+ }
48
+
49
+ # Determine install source
50
+ $INSTALL_SRC = ""
51
+ if ($Tag) {
52
+ $INSTALL_SRC = "${PKG_NAME}@${Tag}"
53
+ } elseif ($Version) {
54
+ $INSTALL_SRC = "${PKG_NAME}@${Version}"
55
+ } elseif ($SelfVersion) {
56
+ if (-not $LOCAL_VERSION) {
57
+ Write-Host "[ERROR] Cannot read version from package.json" -ForegroundColor Red
58
+ exit 1
59
+ }
60
+ $INSTALL_SRC = "${PKG_NAME}@${LOCAL_VERSION}"
61
+ } else {
62
+ $INSTALL_SRC = "${PKG_NAME}@latest"
63
+ }
64
+
65
+ # Environment variable fallback
66
+ if (-not $AppId) { $AppId = $env:QQBOT_APPID }
67
+ if (-not $Secret) { $Secret = $env:QQBOT_SECRET }
68
+ if ((-not $AppId) -and (-not $Secret) -and $env:QQBOT_TOKEN) {
69
+ $parts = $env:QQBOT_TOKEN -split ":", 2
70
+ $AppId = $parts[0]
71
+ $Secret = $parts[1]
72
+ }
73
+
74
+ # Detect CLI
75
+ $CMD = ""
76
+ foreach ($name in @("openclaw", "clawdbot", "moltbot")) {
77
+ try {
78
+ $null = Get-Command $name -ErrorAction Stop
79
+ $CMD = $name
80
+ break
81
+ } catch {}
82
+ }
83
+ if (-not $CMD) {
84
+ Write-Host "[ERROR] openclaw / clawdbot / moltbot not found" -ForegroundColor Red
85
+ exit 1
86
+ }
87
+
88
+ $HOME_DIR = $env:USERPROFILE
89
+ if (-not $HOME_DIR) { $HOME_DIR = [Environment]::GetFolderPath("UserProfile") }
90
+ $EXTENSIONS_DIR = Join-Path (Join-Path $HOME_DIR ".$CMD") "extensions"
91
+
92
+ Write-Host "==========================================="
93
+ Write-Host " qqbot npm upgrade: $INSTALL_SRC"
94
+ Write-Host "==========================================="
95
+ Write-Host ""
96
+
97
+ # [1/3] Download and extract new version
98
+ Write-Host "[1/5] Downloading new version..."
99
+ $TMPDIR_PACK = Join-Path ([System.IO.Path]::GetTempPath()) "qqbot-pack-$([guid]::NewGuid().ToString('N').Substring(0,8))"
100
+ $EXTRACT_DIR = Join-Path ([System.IO.Path]::GetTempPath()) "qqbot-extract-$([guid]::NewGuid().ToString('N').Substring(0,8))"
101
+ New-Item -ItemType Directory -Path $TMPDIR_PACK -Force | Out-Null
102
+ New-Item -ItemType Directory -Path $EXTRACT_DIR -Force | Out-Null
103
+
104
+ try {
105
+ Push-Location $TMPDIR_PACK
106
+
107
+ # Multi-registry fallback
108
+ $PACK_OK = $false
109
+ $registries = @("https://registry.npmjs.org/", "https://registry.npmmirror.com/", "")
110
+ foreach ($registry in $registries) {
111
+ try {
112
+ if ($registry) {
113
+ Write-Host " Trying registry: $registry"
114
+ & npm pack $INSTALL_SRC --registry $registry --quiet 2>&1 | Out-Null
115
+ } else {
116
+ Write-Host " Trying default registry..."
117
+ & npm pack $INSTALL_SRC --quiet 2>&1 | Out-Null
118
+ }
119
+ if ($LASTEXITCODE -eq 0) {
120
+ $PACK_OK = $true
121
+ break
122
+ }
123
+ } catch {}
124
+ }
125
+
126
+ if (-not $PACK_OK) {
127
+ Write-Host "[ERROR] npm pack failed (all registries unavailable)" -ForegroundColor Red
128
+ exit 1
129
+ }
130
+
131
+ $TGZ_FILE = Get-ChildItem -Path $TMPDIR_PACK -Filter "*.tgz" | Select-Object -First 1
132
+ if (-not $TGZ_FILE) {
133
+ Write-Host "[ERROR] Downloaded tgz file not found" -ForegroundColor Red
134
+ exit 1
135
+ }
136
+ Write-Host " Downloaded: $($TGZ_FILE.Name)"
137
+
138
+ # Extract tgz (tar is built-in on Windows 10+)
139
+ & tar xzf $TGZ_FILE.FullName -C $EXTRACT_DIR
140
+ $PACKAGE_DIR = Join-Path $EXTRACT_DIR "package"
141
+ if (-not (Test-Path $PACKAGE_DIR)) {
142
+ Write-Host "[ERROR] Extraction failed, package directory not found" -ForegroundColor Red
143
+ exit 1
144
+ }
145
+
146
+ Pop-Location
147
+
148
+ # Prepare staging directory
149
+ $STAGING_DIR = Join-Path (Split-Path $EXTENSIONS_DIR -Parent) ".qqbot-upgrade-staging"
150
+ if (Test-Path $STAGING_DIR) { Remove-Item -Recurse -Force $STAGING_DIR }
151
+ Copy-Item -Recurse -Force $PACKAGE_DIR $STAGING_DIR
152
+
153
+ # Check bundled dependencies
154
+ $nmDir = Join-Path $STAGING_DIR "node_modules"
155
+ if (Test-Path $nmDir) {
156
+ $bundledCount = (Get-ChildItem -Directory $nmDir -ErrorAction SilentlyContinue | Measure-Object).Count
157
+ # Count scoped packages
158
+ Get-ChildItem -Directory $nmDir -Filter "@*" -ErrorAction SilentlyContinue | ForEach-Object {
159
+ $bundledCount += (Get-ChildItem -Directory $_.FullName -ErrorAction SilentlyContinue | Measure-Object).Count - 1
160
+ }
161
+ Write-Host " Bundled dependencies ready (${bundledCount} packages)"
162
+ } else {
163
+ Write-Host " [WARN] Bundled node_modules not found, installing dependencies..."
164
+ Push-Location $STAGING_DIR
165
+ try { & npm install --omit=dev --omit=peer --ignore-scripts --quiet 2>&1 | Out-Null } catch {}
166
+ Pop-Location
167
+ }
168
+
169
+ } finally {
170
+ # Clean up temp files
171
+ if (Test-Path $TMPDIR_PACK) { Remove-Item -Recurse -Force $TMPDIR_PACK -ErrorAction SilentlyContinue }
172
+ if (Test-Path $EXTRACT_DIR) { Remove-Item -Recurse -Force $EXTRACT_DIR -ErrorAction SilentlyContinue }
173
+ }
174
+
175
+ # ── Preflight: validate new package before writing to extensions ──
176
+ Write-Host ""
177
+ Write-Host "[2/5] Preflight checks..."
178
+ $PreflightOK = $true
179
+
180
+ # (a) package.json exists and has version
181
+ $StagingPkg = Join-Path $STAGING_DIR "package.json"
182
+ $StagingVersion = ""
183
+ if (-not (Test-Path $StagingPkg)) {
184
+ Write-Host " [FAIL] New package missing package.json" -ForegroundColor Red
185
+ $PreflightOK = $false
186
+ } else {
187
+ try {
188
+ $spkg = Get-Content $StagingPkg -Raw | ConvertFrom-Json
189
+ $StagingVersion = $spkg.version
190
+ if (-not $StagingVersion) { throw "no version" }
191
+ Write-Host " [OK] Version: $StagingVersion"
192
+ } catch {
193
+ Write-Host " [FAIL] package.json unreadable or missing version" -ForegroundColor Red
194
+ $PreflightOK = $false
195
+ }
196
+ }
197
+
198
+ # (b) Entry file exists
199
+ $EntryFile = ""
200
+ foreach ($candidate in @("dist\index.js", "index.js")) {
201
+ if (Test-Path (Join-Path $STAGING_DIR $candidate)) {
202
+ $EntryFile = $candidate
203
+ break
204
+ }
205
+ }
206
+ if (-not $EntryFile) {
207
+ Write-Host " [FAIL] Missing entry file (dist\index.js or index.js)" -ForegroundColor Red
208
+ $PreflightOK = $false
209
+ } else {
210
+ Write-Host " [OK] Entry file: $EntryFile"
211
+ }
212
+
213
+ # (c) Core directory dist/src
214
+ $CoreSrcDir = Join-Path $STAGING_DIR "dist" "src"
215
+ if (-not (Test-Path $CoreSrcDir)) {
216
+ Write-Host " [FAIL] Missing core directory dist\src\" -ForegroundColor Red
217
+ $PreflightOK = $false
218
+ } else {
219
+ $CoreJsCount = (Get-ChildItem -Path $CoreSrcDir -Filter "*.js" -File -Recurse -ErrorAction SilentlyContinue | Measure-Object).Count
220
+ Write-Host " [OK] dist\src\ contains $CoreJsCount JS files"
221
+ if ($CoreJsCount -lt 5) {
222
+ Write-Host " [FAIL] JS file count too low (expected >= 5, got $CoreJsCount)" -ForegroundColor Red
223
+ $PreflightOK = $false
224
+ }
225
+ }
226
+
227
+ # (d) Critical module files
228
+ $MissingModules = @()
229
+ foreach ($mod in @("dist\src\gateway.js", "dist\src\api.js", "dist\src\admin-resolver.js")) {
230
+ if (-not (Test-Path (Join-Path $STAGING_DIR $mod))) {
231
+ $MissingModules += $mod
232
+ }
233
+ }
234
+ if ($MissingModules.Count -gt 0) {
235
+ Write-Host " [FAIL] Missing critical modules: $($MissingModules -join ', ')" -ForegroundColor Red
236
+ $PreflightOK = $false
237
+ } else {
238
+ Write-Host " [OK] Critical modules intact"
239
+ }
240
+
241
+ # (e) Bundled node_modules health check
242
+ $nmDir = Join-Path $STAGING_DIR "node_modules"
243
+ if (Test-Path $nmDir) {
244
+ $BundledOK = $true
245
+ foreach ($dep in @("ws", "silk-wasm")) {
246
+ if (-not (Test-Path (Join-Path $nmDir $dep))) {
247
+ Write-Host " [WARN] Bundled dependency missing: $dep" -ForegroundColor Yellow
248
+ $BundledOK = $false
249
+ }
250
+ }
251
+ if ($BundledOK) {
252
+ Write-Host " [OK] Core bundled dependencies intact"
253
+ }
254
+ }
255
+
256
+ # (f) Version sanity check
257
+ if ($StagingVersion) {
258
+ $StagingMajor = ($StagingVersion -split "\.")[0]
259
+ if ($StagingMajor -eq "0") {
260
+ Write-Host " [WARN] Major version is 0 ($StagingVersion), may not be a production release" -ForegroundColor Yellow
261
+ }
262
+ }
263
+
264
+ # Preflight result
265
+ if (-not $PreflightOK) {
266
+ Write-Host ""
267
+ Write-Host "[ABORT] Preflight checks failed, upgrade cancelled (old version unaffected)" -ForegroundColor Red
268
+ Remove-Item -Recurse -Force $STAGING_DIR -ErrorAction SilentlyContinue
269
+ exit 1
270
+ }
271
+ Write-Host " [OK] All preflight checks passed"
272
+
273
+ # [3/5] Replace plugin directory (in-place overwrite to avoid file-lock issues)
274
+ Write-Host ""
275
+ Write-Host "[3/5] Replacing plugin directory..."
276
+ if (-not (Test-Path $EXTENSIONS_DIR)) { New-Item -ItemType Directory -Path $EXTENSIONS_DIR -Force | Out-Null }
277
+ $TARGET_DIR = Join-Path $EXTENSIONS_DIR "openclaw-qqbot"
278
+
279
+ if (-not (Test-Path $TARGET_DIR)) {
280
+ # Fresh install: just move staging into place
281
+ Move-Item -Path $STAGING_DIR -Destination $TARGET_DIR
282
+ } else {
283
+ # In-place overwrite using robocopy /MIR (mirrors source to dest, works even with locked files)
284
+ Write-Host " Overwriting in-place with robocopy /MIR ..."
285
+ $roboArgs = @($STAGING_DIR, $TARGET_DIR, "/MIR", "/NFL", "/NDL", "/NJH", "/NJS", "/NP", "/R:3", "/W:2")
286
+ $roboResult = & robocopy @roboArgs 2>&1
287
+ $roboExit = $LASTEXITCODE
288
+ # robocopy exit codes: 0-7 = success (various levels of copy), 8+ = error
289
+ if ($roboExit -ge 8) {
290
+ Write-Host " robocopy failed (exit $roboExit), falling back to Copy-Item..." -ForegroundColor Yellow
291
+ # Fallback: recursive Copy-Item -Force (overwrites files even if target exists)
292
+ try {
293
+ Copy-Item -Path (Join-Path $STAGING_DIR "*") -Destination $TARGET_DIR -Recurse -Force -ErrorAction Stop
294
+ Write-Host " Copy-Item fallback succeeded"
295
+ } catch {
296
+ Write-Host " [ERROR] Copy-Item also failed: $($_.Exception.Message)" -ForegroundColor Red
297
+ exit 1
298
+ }
299
+ } else {
300
+ Write-Host " robocopy completed (exit $roboExit)"
301
+ }
302
+ # Clean up staging
303
+ Remove-Item -Recurse -Force $STAGING_DIR -ErrorAction SilentlyContinue
304
+ }
305
+
306
+ # Clean up leftover directories
307
+ foreach ($leftover in @("openclaw-qqbot.staging", ".qqbot-upgrade-staging", ".qqbot-upgrade-old", ".openclaw-qqbot-new")) {
308
+ $p = Join-Path $EXTENSIONS_DIR $leftover
309
+ if (Test-Path $p) { Remove-Item -Recurse -Force $p -ErrorAction SilentlyContinue }
310
+ }
311
+ $oldDir = Join-Path (Split-Path $EXTENSIONS_DIR -Parent) ".qqbot-upgrade-old"
312
+ if (Test-Path $oldDir) { Remove-Item -Recurse -Force $oldDir -ErrorAction SilentlyContinue }
313
+ foreach ($legacyName in @("qqbot", "openclaw-qq")) {
314
+ $p = Join-Path $EXTENSIONS_DIR $legacyName
315
+ if (Test-Path $p) { Remove-Item -Recurse -Force $p -ErrorAction SilentlyContinue }
316
+ }
317
+ Write-Host " Installed to: $TARGET_DIR"
318
+
319
+ # Execute postinstall script to create openclaw SDK symlink
320
+ # (upgrade-via-npm is pure file operation, npm install is not run, so postinstall won't trigger automatically)
321
+ $PostinstallScript = Join-Path $TARGET_DIR "scripts" "postinstall-link-sdk.js"
322
+ if (Test-Path $PostinstallScript) {
323
+ Write-Host " Running postinstall: creating openclaw SDK symlink..."
324
+ try {
325
+ Push-Location $TARGET_DIR
326
+ $postOutput = & node $PostinstallScript 2>&1
327
+ Pop-Location
328
+ if ($postOutput) { Write-Host " $postOutput" }
329
+ } catch {
330
+ Write-Host " [WARN] postinstall script failed (non-fatal)" -ForegroundColor Yellow
331
+ try { Pop-Location } catch {}
332
+ }
333
+ # Verify symlink creation
334
+ $symlinkPath = Join-Path $TARGET_DIR "node_modules" "openclaw"
335
+ if (Test-Path $symlinkPath) {
336
+ Write-Host " [OK] openclaw SDK symlink ready"
337
+ } else {
338
+ Write-Host " [WARN] openclaw SDK symlink not created, attempting manual fallback..." -ForegroundColor Yellow
339
+ $cliDataDir = Split-Path $EXTENSIONS_DIR -Parent
340
+ $cliName = (Split-Path $cliDataDir -Leaf) -replace '^\.',''
341
+ try {
342
+ $globalRoot = (& npm root -g 2>$null).Trim()
343
+ $globalPkg = Join-Path $globalRoot $cliName
344
+ if ($globalRoot -and (Test-Path $globalPkg)) {
345
+ $nmDir = Join-Path $TARGET_DIR "node_modules"
346
+ if (-not (Test-Path $nmDir)) { New-Item -ItemType Directory -Path $nmDir -Force | Out-Null }
347
+ New-Item -ItemType Junction -Path $symlinkPath -Target $globalPkg -Force | Out-Null
348
+ Write-Host " [OK] Manual symlink created: -> $globalPkg"
349
+ } else {
350
+ Write-Host " [ERROR] Cannot locate global $cliName installation (npm root -g: $globalRoot)" -ForegroundColor Red
351
+ }
352
+ } catch {
353
+ Write-Host " [ERROR] Manual symlink creation also failed: $($_.Exception.Message)" -ForegroundColor Red
354
+ }
355
+ }
356
+ } else {
357
+ Write-Host " [WARN] postinstall script not found, skipping symlink creation" -ForegroundColor Yellow
358
+ }
359
+
360
+ # [4/5] Verify installation
361
+ Write-Host ""
362
+ Write-Host "[4/5] Verifying installation..."
363
+ $NEW_VERSION = "unknown"
364
+ try {
365
+ $newPkgPath = Join-Path $TARGET_DIR "package.json"
366
+ if (Test-Path $newPkgPath) {
367
+ $newPkg = Get-Content $newPkgPath -Raw | ConvertFrom-Json
368
+ if ($newPkg.version) { $NEW_VERSION = $newPkg.version }
369
+ }
370
+ } catch {}
371
+
372
+ Write-Host "QQBOT_NEW_VERSION=$NEW_VERSION"
373
+
374
+ if ($NEW_VERSION -ne "unknown") {
375
+ Write-Host "QQBOT_REPORT=QQBot upgrade complete: v${NEW_VERSION}"
376
+ } else {
377
+ Write-Host "QQBOT_REPORT=[WARN] QQBot upgrade status unknown, cannot confirm new version"
378
+ }
379
+
380
+ Write-Host ""
381
+ Write-Host "==========================================="
382
+ Write-Host " File installation complete"
383
+ Write-Host "==========================================="
384
+
385
+ # --NoRestart mode
386
+ if ($NoRestart) {
387
+ Write-Host ""
388
+ Write-Host "[Skip restart] -NoRestart specified, exiting for caller to trigger gateway restart"
389
+ exit 0
390
+ }
391
+
392
+ # [配置] Configure appid/secret
393
+ if ($AppId -and $Secret) {
394
+ Write-Host ""
395
+ Write-Host "[Config] Writing qqbot channel config..."
396
+ $DESIRED_TOKEN = "${AppId}:${Secret}"
397
+
398
+ try {
399
+ & $CMD channels add --channel qqbot --token $DESIRED_TOKEN 2>&1 | Out-Null
400
+ if ($LASTEXITCODE -eq 0) {
401
+ Write-Host " Channel config saved"
402
+ } else { throw "channels add failed" }
403
+ } catch {
404
+ Write-Host " [WARN] $CMD channels add failed, trying direct config edit..." -ForegroundColor Yellow
405
+ $CONFIG_FILE = Join-Path (Join-Path $HOME_DIR ".$CMD") "$CMD.json"
406
+ if (Test-Path $CONFIG_FILE) {
407
+ try {
408
+ $cfg = Get-Content $CONFIG_FILE -Raw | ConvertFrom-Json
409
+ if (-not $cfg.channels) { $cfg | Add-Member -NotePropertyName channels -NotePropertyValue @{} }
410
+ if (-not $cfg.channels.qqbot) { $cfg.channels | Add-Member -NotePropertyName qqbot -NotePropertyValue @{} }
411
+ $cfg.channels.qqbot | Add-Member -NotePropertyName appId -NotePropertyValue $AppId -Force
412
+ $cfg.channels.qqbot | Add-Member -NotePropertyName clientSecret -NotePropertyValue $Secret -Force
413
+ $cfg | ConvertTo-Json -Depth 10 | Set-Content $CONFIG_FILE -Encoding UTF8
414
+ Write-Host " Channel config saved (direct file edit)"
415
+ } catch {
416
+ Write-Host " [ERROR] Config write failed, please configure manually:" -ForegroundColor Red
417
+ Write-Host " $CMD channels add --channel qqbot --token `"${AppId}:${Secret}`""
418
+ }
419
+ }
420
+ }
421
+ } elseif ($AppId -or $Secret) {
422
+ Write-Host ""
423
+ Write-Host "[WARN] -AppId and -Secret must be provided together" -ForegroundColor Yellow
424
+ }
425
+
426
+ # [5/5] Restart gateway
427
+ Write-Host ""
428
+
429
+ # Manual upgrade: write startup-marker before restart to prevent bot from sending duplicate notification
430
+ if ($NEW_VERSION -and $NEW_VERSION -ne "unknown") {
431
+ $MarkerDir = Join-Path $HOME_DIR ".openclaw" "qqbot" "data"
432
+ if (-not (Test-Path $MarkerDir)) { New-Item -ItemType Directory -Path $MarkerDir -Force | Out-Null }
433
+ $MarkerFile = Join-Path $MarkerDir "startup-marker.json"
434
+ $Now = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
435
+ @{ version = $NEW_VERSION; startedAt = $Now; greetedAt = $Now } | ConvertTo-Json -Compress | Set-Content $MarkerFile -Encoding UTF8
436
+ }
437
+
438
+ Write-Host "[Restart] Restarting gateway..."
439
+ try {
440
+ & $CMD gateway restart 2>&1 | Out-Null
441
+ if ($LASTEXITCODE -eq 0) {
442
+ Write-Host " Gateway restarted"
443
+ # Print the same upgrade greeting as bot notification (no need to push via bot in manual upgrade)
444
+ if ($NEW_VERSION -and $NEW_VERSION -ne "unknown") {
445
+ Write-Host ""
446
+ Write-Host "🎉 QQBot 插件已更新至 v${NEW_VERSION},在线等候你的吩咐。"
447
+ }
448
+ } else { throw "restart failed" }
449
+ } catch {
450
+ Write-Host " [WARN] Gateway restart failed, please run manually: $CMD gateway restart" -ForegroundColor Yellow
451
+ }