@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,156 @@
1
+ #!/bin/bash
2
+
3
+ # qqbot markdown 配置脚本
4
+ # 用于单独设置是否启用 markdown 消息格式
5
+ # 直接编辑 JSON 配置文件,避免框架验证拒绝未注册的 channel
6
+
7
+ set -e
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
+ cd "$SCRIPT_DIR"
11
+
12
+ # 自动检测 CLI 配置文件(兼容 openclaw / clawdbot / moltbot)
13
+ OPENCLAW_CONFIG=""
14
+ for app in openclaw clawdbot moltbot; do
15
+ cfg="$HOME/.$app/$app.json"
16
+ if [ -f "$cfg" ]; then
17
+ OPENCLAW_CONFIG="$cfg"
18
+ break
19
+ fi
20
+ done
21
+
22
+ if [ -z "$OPENCLAW_CONFIG" ]; then
23
+ echo "❌ 未找到 openclaw / clawdbot / moltbot 配置文件"
24
+ echo " 请先运行 openclaw onboard 初始化配置"
25
+ exit 1
26
+ fi
27
+
28
+ show_help() {
29
+ echo "用法: $0 [选项]"
30
+ echo ""
31
+ echo "选项:"
32
+ echo " enable, on, yes 启用 markdown 消息格式"
33
+ echo " disable, off, no 禁用 markdown 消息格式(使用纯文本)"
34
+ echo " status 显示当前 markdown 配置状态"
35
+ echo " -h, --help 显示帮助信息"
36
+ echo ""
37
+ echo "示例:"
38
+ echo " $0 enable 启用 markdown"
39
+ echo " $0 disable 禁用 markdown"
40
+ echo " $0 status 查看当前状态"
41
+ echo " $0 交互式选择"
42
+ echo ""
43
+ echo "⚠️ 注意: 启用 markdown 需要在 QQ 开放平台申请 markdown 消息权限"
44
+ echo " 如果没有权限,消息将无法正常发送!"
45
+ }
46
+
47
+ set_markdown_value() {
48
+ local value="$1"
49
+ node -e "
50
+ const fs = require('fs');
51
+ const cfg = JSON.parse(fs.readFileSync('$OPENCLAW_CONFIG', 'utf-8'));
52
+ if (!cfg.channels) cfg.channels = {};
53
+ if (!cfg.channels.qqbot) cfg.channels.qqbot = {};
54
+ cfg.channels.qqbot.markdownSupport = $value;
55
+ fs.writeFileSync('$OPENCLAW_CONFIG', JSON.stringify(cfg, null, 4) + '\n');
56
+ "
57
+ }
58
+
59
+ enable_markdown() {
60
+ echo "✅ 启用 markdown 消息格式..."
61
+ set_markdown_value true
62
+ echo ""
63
+ echo "markdown 已启用。"
64
+ echo "⚠️ 请确保您已在 QQ 开放平台申请了 markdown 消息权限。"
65
+ }
66
+
67
+ disable_markdown() {
68
+ echo "❌ 禁用 markdown 消息格式(使用纯文本)..."
69
+ set_markdown_value false
70
+ echo ""
71
+ echo "markdown 已禁用,将使用纯文本格式发送消息。"
72
+ }
73
+
74
+ show_status() {
75
+ echo "当前 markdown 配置状态:"
76
+ echo " 配置文件: $OPENCLAW_CONFIG"
77
+ echo ""
78
+ current=$(node -e "
79
+ const cfg = JSON.parse(require('fs').readFileSync('$OPENCLAW_CONFIG', 'utf-8'));
80
+ console.log(cfg.channels?.qqbot?.markdownSupport ?? '未设置');
81
+ " 2>/dev/null || echo "未设置")
82
+ if [ "$current" = "true" ]; then
83
+ echo " 状态: ✅ 已启用"
84
+ echo ""
85
+ echo " ⚠️ 请确保您已在 QQ 开放平台申请了 markdown 消息权限。"
86
+ elif [ "$current" = "false" ]; then
87
+ echo " 状态: ❌ 已禁用(纯文本模式)"
88
+ else
89
+ echo " 状态: 未设置(默认: 禁用)"
90
+ fi
91
+ }
92
+
93
+ interactive_select() {
94
+ echo "========================================="
95
+ echo " qqbot markdown 配置"
96
+ echo "========================================="
97
+ echo ""
98
+ show_status
99
+ echo ""
100
+ echo "-----------------------------------------"
101
+ echo ""
102
+ echo "是否启用 markdown 消息格式?"
103
+ echo ""
104
+ echo "⚠️ 注意: 启用 markdown 需要在 QQ 开放平台申请 markdown 消息权限"
105
+ echo " 如果没有权限,消息将无法正常发送!"
106
+ echo ""
107
+ echo " 1) 启用 markdown"
108
+ echo " 2) 禁用 markdown(纯文本)"
109
+ echo " 3) 取消"
110
+ echo ""
111
+ read -t 10 -p "请选择 [1-3] (默认: 2): " choice || choice="2"
112
+
113
+ case "$choice" in
114
+ 1)
115
+ echo ""
116
+ enable_markdown
117
+ ;;
118
+ 2|"")
119
+ echo ""
120
+ disable_markdown
121
+ ;;
122
+ 3)
123
+ echo "已取消。"
124
+ exit 0
125
+ ;;
126
+ *)
127
+ echo "无效选择,已取消。"
128
+ exit 1
129
+ ;;
130
+ esac
131
+ }
132
+
133
+ # 主逻辑
134
+ case "${1:-}" in
135
+ enable|on|yes)
136
+ enable_markdown
137
+ ;;
138
+ disable|off|no)
139
+ disable_markdown
140
+ ;;
141
+ status)
142
+ show_status
143
+ ;;
144
+ -h|--help)
145
+ show_help
146
+ ;;
147
+ "")
148
+ interactive_select
149
+ ;;
150
+ *)
151
+ echo "未知选项: $1"
152
+ echo ""
153
+ show_help
154
+ exit 1
155
+ ;;
156
+ esac
@@ -0,0 +1,116 @@
1
+ /**
2
+ * 测试 sendMedia 路径:语音、视频、文件
3
+ * 用法:npx tsx scripts/test-sendmedia.ts <openid>
4
+ */
5
+ import { sendMedia } from "../src/outbound.js";
6
+ import type { ResolvedQQBotAccount } from "../src/types.js";
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+
10
+ const LOG_FILE = "/tmp/test-sendmedia-output.log";
11
+
12
+ function log(msg: string) {
13
+ const line = msg + "\n";
14
+ process.stdout.write(line);
15
+ fs.appendFileSync(LOG_FILE, line);
16
+ }
17
+
18
+ function normalizeAppId(raw: unknown): string {
19
+ if (raw === null || raw === undefined) return "";
20
+ return String(raw).trim();
21
+ }
22
+
23
+ function detectConfigPath(): string | null {
24
+ const home = process.env.HOME || "/home/ubuntu";
25
+ for (const app of ["openclaw", "clawdbot", "moltbot"]) {
26
+ const p = path.join(home, `.${app}`, `${app}.json`);
27
+ if (fs.existsSync(p)) return p;
28
+ }
29
+ return null;
30
+ }
31
+
32
+ function loadAccount(): ResolvedQQBotAccount | null {
33
+ const configPath = detectConfigPath();
34
+ try {
35
+ if (!configPath || !fs.existsSync(configPath)) {
36
+ const appId = process.env.QQBOT_APP_ID;
37
+ const clientSecret = process.env.QQBOT_CLIENT_SECRET;
38
+ if (appId && clientSecret) {
39
+ return { accountId: "default", appId: normalizeAppId(appId), clientSecret, enabled: true, secretSource: "env", markdownSupport: true, config: {} };
40
+ }
41
+ return null;
42
+ }
43
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
44
+ const qqbot = config.channels?.qqbot;
45
+ if (!qqbot) return null;
46
+ return {
47
+ accountId: "default",
48
+ appId: normalizeAppId(qqbot.appId ?? process.env.QQBOT_APP_ID),
49
+ clientSecret: qqbot.clientSecret || process.env.QQBOT_CLIENT_SECRET,
50
+ enabled: qqbot.enabled ?? true,
51
+ secretSource: qqbot.clientSecret ? "config" as const : "env" as const,
52
+ markdownSupport: qqbot.markdownSupport ?? true,
53
+ config: qqbot,
54
+ };
55
+ } catch (err) {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ function sleep(ms: number) { return new Promise(r => setTimeout(r, ms)); }
61
+
62
+ async function main() {
63
+ // 清空日志
64
+ fs.writeFileSync(LOG_FILE, "");
65
+
66
+ const openid = process.argv[2];
67
+ if (!openid) { log("用法: npx tsx scripts/test-sendmedia.ts <openid>"); process.exit(1); }
68
+
69
+ const account = loadAccount();
70
+ if (!account) { log("无法加载账户配置"); process.exit(1); }
71
+
72
+ const to = `c2c:${openid}`;
73
+ log(`目标: ${to}\n`);
74
+
75
+ // ===== 1. 语音 =====
76
+ log("==================================================");
77
+ log("TEST 1: 语音 (本地 WAV 文件)");
78
+ log("==================================================");
79
+ const wavPath = "/tmp/test-voice.wav";
80
+ if (fs.existsSync(wavPath)) {
81
+ const r1 = await sendMedia({ to, text: "测试语音 sendMedia", mediaUrl: wavPath, account });
82
+ log("结果: " + JSON.stringify(r1, null, 2));
83
+ } else {
84
+ log("跳过: /tmp/test-voice.wav 不存在");
85
+ }
86
+
87
+ await sleep(2000);
88
+
89
+ // ===== 2. 视频 =====
90
+ log("\n==================================================");
91
+ log("TEST 2: 视频 (公网 MP4 URL)");
92
+ log("==================================================");
93
+ const videoUrl = "https://www.w3schools.com/html/mov_bbb.mp4";
94
+ const r2 = await sendMedia({ to, text: "测试视频 sendMedia", mediaUrl: videoUrl, account });
95
+ log("结果: " + JSON.stringify(r2, null, 2));
96
+
97
+ await sleep(2000);
98
+
99
+ // ===== 3. 文件 =====
100
+ log("\n==================================================");
101
+ log("TEST 3: 文件 (本地 TXT 文件)");
102
+ log("==================================================");
103
+ const txtPath = "/tmp/test-doc.txt";
104
+ if (fs.existsSync(txtPath)) {
105
+ const r3 = await sendMedia({ to, text: "测试文件 sendMedia", mediaUrl: txtPath, account });
106
+ log("结果: " + JSON.stringify(r3, null, 2));
107
+ } else {
108
+ log("跳过: /tmp/test-doc.txt 不存在");
109
+ }
110
+
111
+ log("\n==================================================");
112
+ log("全部测试完成");
113
+ log("==================================================");
114
+ }
115
+
116
+ main().catch(err => { log("脚本异常: " + err); process.exit(1); });