@weotro/dx 0.1.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 +755 -0
- package/bin/dx-with-version-env.js +8 -0
- package/bin/dx.js +187 -0
- package/lib/artifact-deploy/artifact-builder.js +144 -0
- package/lib/artifact-deploy/config.js +180 -0
- package/lib/artifact-deploy/remote-script.js +301 -0
- package/lib/artifact-deploy/remote-transport.js +86 -0
- package/lib/artifact-deploy.js +70 -0
- package/lib/backend-artifact-deploy/artifact-builder.js +267 -0
- package/lib/backend-artifact-deploy/config.js +218 -0
- package/lib/backend-artifact-deploy/path-utils.js +18 -0
- package/lib/backend-artifact-deploy/remote-phases.js +14 -0
- package/lib/backend-artifact-deploy/remote-result.js +44 -0
- package/lib/backend-artifact-deploy/remote-script.js +507 -0
- package/lib/backend-artifact-deploy/remote-transport.js +123 -0
- package/lib/backend-artifact-deploy/rollback.js +5 -0
- package/lib/backend-artifact-deploy/runtime-package.js +46 -0
- package/lib/backend-artifact-deploy.js +91 -0
- package/lib/backend-package.js +674 -0
- package/lib/cli/args.js +38 -0
- package/lib/cli/command-result.js +1 -0
- package/lib/cli/commands/contracts.js +60 -0
- package/lib/cli/commands/core.js +533 -0
- package/lib/cli/commands/db.js +231 -0
- package/lib/cli/commands/deploy.js +175 -0
- package/lib/cli/commands/env.js +120 -0
- package/lib/cli/commands/export.js +39 -0
- package/lib/cli/commands/package.js +22 -0
- package/lib/cli/commands/release.js +55 -0
- package/lib/cli/commands/stack.js +427 -0
- package/lib/cli/commands/start.js +58 -0
- package/lib/cli/commands/worktree.js +145 -0
- package/lib/cli/dx-cli.js +1072 -0
- package/lib/cli/flags.js +123 -0
- package/lib/cli/help-model.js +222 -0
- package/lib/cli/help-renderer.js +137 -0
- package/lib/cli/help-schema.js +552 -0
- package/lib/cli/help.js +141 -0
- package/lib/cli/index.js +4 -0
- package/lib/cli/nx-command.js +13 -0
- package/lib/codex-initial.js +271 -0
- package/lib/confirm.js +213 -0
- package/lib/env-policy.js +134 -0
- package/lib/env-profile.js +435 -0
- package/lib/env.js +261 -0
- package/lib/exec.js +692 -0
- package/lib/logger.js +239 -0
- package/lib/nx-ignore.js +45 -0
- package/lib/run-with-version-env.js +163 -0
- package/lib/sdk-build.js +424 -0
- package/lib/start-dev.js +401 -0
- package/lib/telegram-webhook.js +431 -0
- package/lib/validate-env.js +317 -0
- package/lib/vercel-deploy.js +549 -0
- package/lib/version.js +14 -0
- package/lib/worktree.js +1052 -0
- package/package.json +45 -0
- package/skills/create-issue/SKILL.md +90 -0
- package/skills/delivering-design-handoff/SKILL.md +290 -0
- package/skills/doctor/SKILL.md +76 -0
- package/skills/gh-dependabot-cleanup/SKILL.md +54 -0
- package/skills/gh-dependabot-cleanup/agents/openai.yaml +7 -0
- package/skills/git-release/SKILL.md +194 -0
- package/skills/git-release/agents/openai.yaml +7 -0
- package/skills/online-debug-guard/SKILL.md +111 -0
- package/skills/ship-issue-pr/SKILL.md +676 -0
- package/skills/stagewise-ui-debugging/SKILL.md +48 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process'
|
|
2
|
+
import { logger } from './logger.js'
|
|
3
|
+
import { envManager } from './env.js'
|
|
4
|
+
|
|
5
|
+
function normalizeWebhookPath(raw) {
|
|
6
|
+
const s = String(raw || '').trim()
|
|
7
|
+
if (!s) return '/api/webhook'
|
|
8
|
+
if (s.startsWith('/')) return s
|
|
9
|
+
return `/${s}`
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeDeployUrl(raw) {
|
|
13
|
+
const s = String(raw || '')
|
|
14
|
+
const m = s.match(/(https?:\/\/)?([a-z0-9-]+\.vercel\.app)\b/i)
|
|
15
|
+
if (!m) return null
|
|
16
|
+
return `https://${m[2]}`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function parseDeployUrlFromDeployOutput(output) {
|
|
20
|
+
const lines = String(output || '')
|
|
21
|
+
.split(/\r?\n/)
|
|
22
|
+
.map(line => String(line || '').trim())
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
|
|
25
|
+
const pickUrl = line => {
|
|
26
|
+
const m = line.match(/(https?:\/\/)?([a-z0-9-]+\.vercel\.app)\b/i)
|
|
27
|
+
return m ? `https://${m[2]}` : null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
if (!/^production\s*:/i.test(line)) continue
|
|
32
|
+
const url = pickUrl(line)
|
|
33
|
+
if (url) return url
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
if (!/^preview\s*:/i.test(line)) continue
|
|
38
|
+
const url = pickUrl(line)
|
|
39
|
+
if (url) return url
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
if (/to deploy to production/i.test(line)) continue
|
|
44
|
+
const url = pickUrl(line)
|
|
45
|
+
if (url) return url
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function parseDeployUrlFromVercelListOutput(output, projectNameHint) {
|
|
52
|
+
const lines = String(output || '')
|
|
53
|
+
.split(/\r?\n/)
|
|
54
|
+
.map(l => l.trim())
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
|
|
57
|
+
const isReady = line => /\b(Ready|READY)\b/.test(line)
|
|
58
|
+
const hasUrl = line => /\b[a-z0-9-]+\.vercel\.app\b/i.test(line)
|
|
59
|
+
const pickUrl = line => {
|
|
60
|
+
const m = line.match(/(https?:\/\/)?([a-z0-9-]+\.vercel\.app)\b/i)
|
|
61
|
+
return m ? `https://${m[2]}` : null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const hint = projectNameHint ? String(projectNameHint) : ''
|
|
65
|
+
if (hint) {
|
|
66
|
+
for (const line of lines) {
|
|
67
|
+
if (!line.includes(hint)) continue
|
|
68
|
+
if (!isReady(line)) continue
|
|
69
|
+
if (!hasUrl(line)) continue
|
|
70
|
+
const url = pickUrl(line)
|
|
71
|
+
if (url) return url
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
if (!isReady(line)) continue
|
|
77
|
+
if (!hasUrl(line)) continue
|
|
78
|
+
const url = pickUrl(line)
|
|
79
|
+
if (url) return url
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 处理 Telegram Bot 部署后的 Webhook 配置
|
|
87
|
+
*/
|
|
88
|
+
export async function handleTelegramBotDeploy(environment, projectId, orgId, token, options = {}) {
|
|
89
|
+
logger.step('配置 Telegram Webhook...')
|
|
90
|
+
|
|
91
|
+
const {
|
|
92
|
+
deployOutput,
|
|
93
|
+
projectNameHint,
|
|
94
|
+
webhookPath: webhookPathOverride,
|
|
95
|
+
dryRun: dryRunOverride,
|
|
96
|
+
strict: strictOverride,
|
|
97
|
+
} = options || {}
|
|
98
|
+
|
|
99
|
+
const strictDefault = true
|
|
100
|
+
|
|
101
|
+
const strictEnv = process.env.DX_TELEGRAM_WEBHOOK_STRICT != null
|
|
102
|
+
? !['0', 'false', 'no'].includes(String(process.env.DX_TELEGRAM_WEBHOOK_STRICT).toLowerCase())
|
|
103
|
+
: undefined
|
|
104
|
+
|
|
105
|
+
const strict = strictOverride ?? strictEnv ?? strictDefault
|
|
106
|
+
|
|
107
|
+
const dryRunEnv = ['1', 'true', 'yes'].includes(String(process.env.DX_TELEGRAM_WEBHOOK_DRY_RUN || '').toLowerCase())
|
|
108
|
+
const dryRun = dryRunOverride ?? (dryRunEnv ? true : false)
|
|
109
|
+
|
|
110
|
+
const webhookPath = normalizeWebhookPath(webhookPathOverride ?? process.env.DX_TELEGRAM_WEBHOOK_PATH ?? '/api/webhook')
|
|
111
|
+
|
|
112
|
+
// 1. 验证必需环境变量
|
|
113
|
+
const botToken = process.env.TELEGRAM_BOT_TOKEN
|
|
114
|
+
const webhookSecret = process.env.TELEGRAM_BOT_WEBHOOK_SECRET
|
|
115
|
+
|
|
116
|
+
const missingVars = []
|
|
117
|
+
if (!botToken || envManager.isPlaceholderEnvValue(botToken)) {
|
|
118
|
+
missingVars.push('TELEGRAM_BOT_TOKEN')
|
|
119
|
+
}
|
|
120
|
+
if (!webhookSecret || envManager.isPlaceholderEnvValue(webhookSecret)) {
|
|
121
|
+
missingVars.push('TELEGRAM_BOT_WEBHOOK_SECRET')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (missingVars.length > 0) {
|
|
125
|
+
logger.error('缺少以下 Telegram Bot 环境变量:')
|
|
126
|
+
missingVars.forEach(v => {
|
|
127
|
+
logger.error(` - ${v}`)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const message = 'Telegram Webhook 配置失败:缺少必需环境变量'
|
|
131
|
+
if (strict) {
|
|
132
|
+
return {
|
|
133
|
+
status: 'failed',
|
|
134
|
+
reason: 'missing_env_vars',
|
|
135
|
+
strict,
|
|
136
|
+
message,
|
|
137
|
+
missingVars,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
logger.warn('跳过 Webhook 配置,请手动设置')
|
|
142
|
+
return {
|
|
143
|
+
status: 'warning',
|
|
144
|
+
reason: 'missing_env_vars',
|
|
145
|
+
strict,
|
|
146
|
+
message,
|
|
147
|
+
missingVars,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// 2. 获取 Vercel 部署 URL
|
|
153
|
+
const deploymentUrl = await getLatestDeploymentUrl({
|
|
154
|
+
projectId,
|
|
155
|
+
orgId,
|
|
156
|
+
token,
|
|
157
|
+
environment,
|
|
158
|
+
deployOutput,
|
|
159
|
+
projectNameHint,
|
|
160
|
+
})
|
|
161
|
+
if (!deploymentUrl) {
|
|
162
|
+
const message = '无法获取 Vercel 部署 URL'
|
|
163
|
+
if (strict) {
|
|
164
|
+
return {
|
|
165
|
+
status: 'failed',
|
|
166
|
+
reason: 'missing_deploy_url',
|
|
167
|
+
strict,
|
|
168
|
+
message,
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
logger.error('无法获取 Vercel 部署 URL,跳过 Webhook 配置')
|
|
172
|
+
return {
|
|
173
|
+
status: 'warning',
|
|
174
|
+
reason: 'missing_deploy_url',
|
|
175
|
+
strict,
|
|
176
|
+
message,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const webhookUrl = `${deploymentUrl}${webhookPath}`
|
|
181
|
+
logger.info(`Webhook URL: ${webhookUrl}`)
|
|
182
|
+
|
|
183
|
+
if (dryRun) {
|
|
184
|
+
logger.warn('DX_TELEGRAM_WEBHOOK_DRY_RUN=1,已跳过 setWebhook/getWebhookInfo 调用')
|
|
185
|
+
return {
|
|
186
|
+
status: 'warning',
|
|
187
|
+
reason: 'dry_run',
|
|
188
|
+
strict,
|
|
189
|
+
message: 'DX_TELEGRAM_WEBHOOK_DRY_RUN=1',
|
|
190
|
+
webhookUrl,
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 3. 调用 Telegram API 设置 Webhook
|
|
195
|
+
const telegramApiUrl = `https://api.telegram.org/bot${botToken}/setWebhook`
|
|
196
|
+
const payload = JSON.stringify({
|
|
197
|
+
url: webhookUrl,
|
|
198
|
+
secret_token: webhookSecret,
|
|
199
|
+
drop_pending_updates: false,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const curlCmd = [
|
|
203
|
+
'curl',
|
|
204
|
+
'-X POST',
|
|
205
|
+
`"${telegramApiUrl}"`,
|
|
206
|
+
'-H "Content-Type: application/json"',
|
|
207
|
+
`-d '${payload}'`,
|
|
208
|
+
'--silent',
|
|
209
|
+
].join(' ')
|
|
210
|
+
|
|
211
|
+
const response = execSync(curlCmd, { encoding: 'utf8' })
|
|
212
|
+
const result = JSON.parse(response)
|
|
213
|
+
|
|
214
|
+
if (result.ok) {
|
|
215
|
+
logger.success('Telegram Webhook 设置成功')
|
|
216
|
+
logger.info(`Webhook URL: ${webhookUrl}`)
|
|
217
|
+
|
|
218
|
+
// 4. 验证 Webhook 状态
|
|
219
|
+
const verifyResult = await verifyWebhook(botToken, webhookUrl, { strict })
|
|
220
|
+
if (verifyResult.status === 'success') {
|
|
221
|
+
return {
|
|
222
|
+
status: 'success',
|
|
223
|
+
reason: 'verified',
|
|
224
|
+
strict,
|
|
225
|
+
webhookUrl,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
status: strict ? 'failed' : 'warning',
|
|
231
|
+
reason: 'verify_failed',
|
|
232
|
+
strict,
|
|
233
|
+
message: verifyResult.message,
|
|
234
|
+
webhookUrl,
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const desc = result.description || '未知错误'
|
|
239
|
+
const message = `Telegram Webhook 设置失败: ${desc}`
|
|
240
|
+
if (strict) {
|
|
241
|
+
return {
|
|
242
|
+
status: 'failed',
|
|
243
|
+
reason: 'set_webhook_failed',
|
|
244
|
+
strict,
|
|
245
|
+
message,
|
|
246
|
+
webhookUrl,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
logger.error(message)
|
|
251
|
+
logger.info('请手动执行以下命令(不要把明文 token/secret 写进日志):')
|
|
252
|
+
const manualPayload = JSON.stringify({
|
|
253
|
+
url: webhookUrl,
|
|
254
|
+
secret_token: '<YOUR_WEBHOOK_SECRET>',
|
|
255
|
+
drop_pending_updates: false,
|
|
256
|
+
})
|
|
257
|
+
logger.info(
|
|
258
|
+
`curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" -H "Content-Type: application/json" -d '${manualPayload}' --silent`,
|
|
259
|
+
)
|
|
260
|
+
return {
|
|
261
|
+
status: 'warning',
|
|
262
|
+
reason: 'set_webhook_failed',
|
|
263
|
+
strict,
|
|
264
|
+
message,
|
|
265
|
+
webhookUrl,
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
const message = error?.message || String(error)
|
|
271
|
+
logger.error(`Webhook 配置失败: ${message}`)
|
|
272
|
+
|
|
273
|
+
if (!strict) {
|
|
274
|
+
logger.warn('请手动设置 Webhook(参考 apps/telegram-bot/README.md)')
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
status: strict ? 'failed' : 'warning',
|
|
278
|
+
reason: 'runtime_error',
|
|
279
|
+
strict,
|
|
280
|
+
message,
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 获取最新部署的 URL
|
|
287
|
+
*/
|
|
288
|
+
async function getLatestDeploymentUrl({
|
|
289
|
+
projectId,
|
|
290
|
+
orgId,
|
|
291
|
+
token,
|
|
292
|
+
environment,
|
|
293
|
+
deployOutput,
|
|
294
|
+
projectNameHint,
|
|
295
|
+
}) {
|
|
296
|
+
const fromDeploy = parseDeployUrlFromDeployOutput(deployOutput)
|
|
297
|
+
if (fromDeploy) return fromDeploy
|
|
298
|
+
|
|
299
|
+
const fromApi = await getDeploymentUrlFromVercelApi({ projectId, orgId, token, environment })
|
|
300
|
+
if (fromApi) return fromApi
|
|
301
|
+
|
|
302
|
+
const fromList = await getDeploymentUrlFromVercelList({ orgId, token, projectNameHint })
|
|
303
|
+
if (fromList) return fromList
|
|
304
|
+
|
|
305
|
+
return null
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function pickDeploymentUrlFromVercelApiResponse(json) {
|
|
309
|
+
const deployments = json?.deployments
|
|
310
|
+
if (!Array.isArray(deployments)) return null
|
|
311
|
+
|
|
312
|
+
for (const d of deployments) {
|
|
313
|
+
const url = d?.url
|
|
314
|
+
if (!url) continue
|
|
315
|
+
const state = d?.state || d?.readyState
|
|
316
|
+
if (state && String(state).toUpperCase() !== 'READY') continue
|
|
317
|
+
const normalized = normalizeDeployUrl(url)
|
|
318
|
+
if (normalized) return normalized
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return null
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function getDeploymentUrlFromVercelApi({ projectId, orgId, token, environment }) {
|
|
325
|
+
try {
|
|
326
|
+
const qs = new URLSearchParams({
|
|
327
|
+
projectId: String(projectId),
|
|
328
|
+
state: 'READY',
|
|
329
|
+
limit: '10',
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
// dx 的 deploy 实现里:staging/production 都会传 --prod,因此对应 Vercel 的 production target。
|
|
333
|
+
// development 环境若需要兜底查询,则不强制 target(避免与 Vercel CLI/REST 字段差异耦合)。
|
|
334
|
+
if (environment !== 'development') {
|
|
335
|
+
qs.set('target', 'production')
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (orgId) {
|
|
339
|
+
const scope = String(orgId)
|
|
340
|
+
if (scope.startsWith('team_')) qs.set('teamId', scope)
|
|
341
|
+
else qs.set('slug', scope)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const url = `https://api.vercel.com/v6/deployments?${qs.toString()}`
|
|
345
|
+
const res = await fetch(url, {
|
|
346
|
+
method: 'GET',
|
|
347
|
+
headers: {
|
|
348
|
+
Authorization: `Bearer ${token}`,
|
|
349
|
+
},
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
if (!res.ok) {
|
|
353
|
+
logger.warn(`Vercel API 获取部署列表失败: HTTP ${res.status}`)
|
|
354
|
+
return null
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const json = await res.json()
|
|
358
|
+
return pickDeploymentUrlFromVercelApiResponse(json)
|
|
359
|
+
} catch (error) {
|
|
360
|
+
logger.warn(`Vercel API 获取部署列表失败: ${error?.message || String(error)}`)
|
|
361
|
+
return null
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async function getDeploymentUrlFromVercelList({ orgId, token, projectNameHint }) {
|
|
366
|
+
try {
|
|
367
|
+
const cmd = ['vercel', 'list', orgId ? `--scope=${orgId}` : '']
|
|
368
|
+
.filter(Boolean)
|
|
369
|
+
.join(' ')
|
|
370
|
+
|
|
371
|
+
const output = execSync(cmd, {
|
|
372
|
+
encoding: 'utf8',
|
|
373
|
+
env: {
|
|
374
|
+
...process.env,
|
|
375
|
+
VERCEL_TOKEN: token,
|
|
376
|
+
},
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
return parseDeployUrlFromVercelListOutput(output, projectNameHint)
|
|
380
|
+
} catch (error) {
|
|
381
|
+
logger.warn(`vercel list 获取部署 URL 失败: ${error?.message || String(error)}`)
|
|
382
|
+
return null
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* 验证 Webhook 配置
|
|
388
|
+
*/
|
|
389
|
+
async function verifyWebhook(botToken, expectedWebhookUrl, options = {}) {
|
|
390
|
+
const { strict = false } = options || {}
|
|
391
|
+
try {
|
|
392
|
+
const cmd = `curl -s "https://api.telegram.org/bot${botToken}/getWebhookInfo"`
|
|
393
|
+
const response = execSync(cmd, { encoding: 'utf8' })
|
|
394
|
+
const result = JSON.parse(response)
|
|
395
|
+
|
|
396
|
+
if (result.ok && result.result) {
|
|
397
|
+
const info = result.result
|
|
398
|
+
logger.info('Webhook 状态:')
|
|
399
|
+
logger.info(` URL: ${info.url}`)
|
|
400
|
+
logger.info(` Pending Updates: ${info.pending_update_count}`)
|
|
401
|
+
if (info.last_error_message) {
|
|
402
|
+
logger.warn(` 最后错误: ${info.last_error_message}`)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (expectedWebhookUrl && info.url !== expectedWebhookUrl) {
|
|
406
|
+
const message = `Webhook 未生效:期望 ${expectedWebhookUrl},实际 ${info.url || '(empty)'}`
|
|
407
|
+
if (strict) {
|
|
408
|
+
return { status: 'failed', message }
|
|
409
|
+
}
|
|
410
|
+
logger.warn(message)
|
|
411
|
+
return { status: 'warning', message }
|
|
412
|
+
}
|
|
413
|
+
return { status: 'success' }
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
const desc = result?.description || '未知错误'
|
|
417
|
+
const message = `getWebhookInfo 失败: ${desc}`
|
|
418
|
+
if (strict) {
|
|
419
|
+
return { status: 'failed', message }
|
|
420
|
+
}
|
|
421
|
+
logger.warn(message)
|
|
422
|
+
return { status: 'warning', message }
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
const message = error?.message || String(error)
|
|
427
|
+
if (strict) return { status: 'failed', message }
|
|
428
|
+
logger.warn('无法验证 Webhook 状态')
|
|
429
|
+
return { status: 'warning', message: '无法验证 Webhook 状态' }
|
|
430
|
+
}
|
|
431
|
+
}
|