cclawd 1.0.0 → 1.0.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.
Files changed (111) hide show
  1. package/dist/{active-listener-DYmI7imH.js → active-listener-BLd27Pxd.js} +2 -2
  2. package/dist/{api-key-rotation-DLU4jvSu.js → api-key-rotation-Dg3JlNDQ.js} +1 -1
  3. package/dist/{audio-preflight-C9TMbRb4.js → audio-preflight-eV5m9mMp.js} +15 -15
  4. package/dist/{audio-transcription-runner-Q5zG_hYd.js → audio-transcription-runner-CQU4Eg1M.js} +10 -10
  5. package/dist/{audit-membership-runtime-DhxSwFnF.js → audit-membership-runtime-hXUuer4x.js} +6 -6
  6. package/dist/build-info.json +3 -3
  7. package/dist/bundled/boot-md/handler.js +35 -35
  8. package/dist/bundled/bootstrap-extra-files/handler.js +5 -5
  9. package/dist/bundled/command-logger/handler.js +2 -2
  10. package/dist/bundled/session-memory/handler.js +35 -35
  11. package/dist/{channel-activity-Bx08UTAg.js → channel-activity-dT3cYb0e.js} +2 -2
  12. package/dist/{commands-registry-llLVCTH9.js → commands-registry-CyLMCPuP.js} +2 -2
  13. package/dist/compact.runtime-DGRl4st4.js +39 -0
  14. package/dist/{deliver-DJf2ZBpe.js → deliver-B6eTtXSk.js} +19 -19
  15. package/dist/deliver-runtime-CLDpY6AW.js +19 -0
  16. package/dist/deps-send-discord.runtime-CxADlame.js +19 -0
  17. package/dist/deps-send-imessage.runtime-Wi79xm6H.js +18 -0
  18. package/dist/deps-send-signal.runtime-BDtzvsnR.js +17 -0
  19. package/dist/deps-send-slack.runtime-CgX24hgT.js +17 -0
  20. package/dist/deps-send-telegram.runtime-CEWc7ePn.js +20 -0
  21. package/dist/deps-send-whatsapp.runtime-B1KJ7YOp.js +43 -0
  22. package/dist/{diagnostic-BCCMF3O_.js → diagnostic-BZmAxdu9.js} +2 -2
  23. package/dist/{env-aYXLHjfZ.js → env-lw2hsIUY.js} +1 -1
  24. package/dist/{fetch-bvgIiupu.js → fetch-C0iyt-Iz.js} +3 -3
  25. package/dist/{fetch-DCTUdr1U.js → fetch-CMLoICyN.js} +5 -5
  26. package/dist/{fetch-guard-CqpEmMQ2.js → fetch-guard-DCj3k042.js} +2 -2
  27. package/dist/{frontmatter-DjZuS525.js → frontmatter-C_obXuTp.js} +3 -3
  28. package/dist/{github-copilot-token-CQmATy5E.js → github-copilot-token-8N63GdbE.js} +7 -7
  29. package/dist/{image-Q8E1-lZn.js → image-Bt49ybRv.js} +4 -4
  30. package/dist/image-runtime-Cilhq73U.js +12 -0
  31. package/dist/{ir-CzM3SxId.js → ir-CVtBjUiL.js} +6 -6
  32. package/dist/llm-slug-generator.js +35 -35
  33. package/dist/{logger-ChbX1G7s.js → logger-CbUVl62f.js} +7 -7
  34. package/dist/{login-B0mtU11X.js → login-D0fUoX-p.js} +4 -4
  35. package/dist/{login-qr-DY_i60f5.js → login-qr-ClBxstxZ.js} +10 -10
  36. package/dist/{manager-FAQPC0uO.js → manager-DSfEj66R.js} +12 -12
  37. package/dist/manager-runtime-BrZlGJsj.js +15 -0
  38. package/dist/{model-selection-wf3OY5DX.js → model-selection-CMEj8bpy.js} +130 -130
  39. package/dist/{outbound-Bw0dOVS7.js → outbound-BxIJyMzV.js} +6 -6
  40. package/dist/{outbound-attachment-1R6r9Pg_.js → outbound-attachment-CVJwpypG.js} +2 -2
  41. package/dist/{paths-C0HLtPu0.js → paths-CehYKFsO.js} +7 -7
  42. package/dist/{paths-hfkBoC7i.js → paths-DkxwiA8g.js} +5 -5
  43. package/dist/{pi-embedded-BAHaY-Oh.js → pi-embedded-CHNPEUAv.js} +159 -159
  44. package/dist/{pi-model-discovery-ItS07aJB.js → pi-model-discovery-D-r5y7kV.js} +7 -7
  45. package/dist/pi-model-discovery-runtime-DZQXYmdu.js +12 -0
  46. package/dist/{pi-tools.before-tool-call.runtime-D_mthvtC.js → pi-tools.before-tool-call.runtime-DagGpfw0.js} +10 -10
  47. package/dist/{proxy-fetch-c1ZUFFcO.js → proxy-fetch-BOh1PLOW.js} +1 -1
  48. package/dist/{pw-ai-Ok6KGelf.js → pw-ai-CoIUdns_.js} +9 -9
  49. package/dist/{qmd-manager-DhfEz4Ar.js → qmd-manager-DEscZz5_.js} +6 -6
  50. package/dist/{query-expansion-GqNV2iIE.js → query-expansion-BErUY8P2.js} +4 -4
  51. package/dist/runtime-whatsapp-login.runtime-ChqE9BkX.js +13 -0
  52. package/dist/runtime-whatsapp-outbound.runtime-yiy6jzKk.js +17 -0
  53. package/dist/{send-DPflcjM5.js → send-4rRrSKp9.js} +6 -6
  54. package/dist/{send-CEg4P96c.js → send-BKO1-P1t.js} +5 -5
  55. package/dist/{send-CS0ocZHl.js → send-EDBPXjTT.js} +3 -3
  56. package/dist/{send-6R8b9zsj.js → send-K2mAG7KC.js} +5 -5
  57. package/dist/{send-DwAoiT2p.js → send-V1MRV7QF.js} +25 -25
  58. package/dist/{session-BoIID5UR.js → session-CuVCho2m.js} +7 -7
  59. package/dist/{skill-commands-DhdiziMs.js → skill-commands-B55LOaMB.js} +9 -9
  60. package/dist/slash-commands.runtime-BchS0VkW.js +12 -0
  61. package/dist/slash-dispatch.runtime-BIKRY3fr.js +39 -0
  62. package/dist/slash-skill-commands.runtime-BP4jBHU9.js +13 -0
  63. package/dist/subagent-registry-runtime-DjEYzSyM.js +39 -0
  64. package/dist/{subsystem-C8z6w6xC.js → subsystem-DfXy5gUB.js} +14 -14
  65. package/dist/{tables-DQusRhkD.js → tables-BAGqh2XD.js} +1 -1
  66. package/dist/{target-errors-CfavnC9U.js → target-errors-CeBF8Pws.js} +1 -1
  67. package/dist/{tokens-BWDIKewp.js → tokens-6ul2IrzG.js} +1 -1
  68. package/dist/{web-CrcrTQ2c.js → web-BRSmQdtm.js} +39 -39
  69. package/dist/{whatsapp-actions-B0u0ZAme.js → whatsapp-actions-Dxb2K2Xh.js} +15 -15
  70. package/dist/{workspace-CWDYHR27.js → workspace-DGIcKCCW.js} +20 -20
  71. package/extensions/mfa-auth/README.md +33 -38
  72. package/extensions/mfa-auth/index.ts +97 -92
  73. package/extensions/mfa-auth/node_modules/.bin/qrcode-terminal +1 -6
  74. package/extensions/mfa-auth/node_modules/.bin/qrcode-terminal.cmd +17 -0
  75. package/extensions/mfa-auth/node_modules/.bin/qrcode-terminal.ps1 +0 -13
  76. package/extensions/mfa-auth/node_modules/.bin/tsx +1 -6
  77. package/extensions/mfa-auth/node_modules/.bin/tsx.cmd +17 -0
  78. package/extensions/mfa-auth/node_modules/.bin/tsx.ps1 +0 -13
  79. package/extensions/mfa-auth/node_modules/.package-lock.json +103 -0
  80. package/extensions/mfa-auth/package-lock.json +115 -0
  81. package/extensions/mfa-auth/package.json +1 -1
  82. package/extensions/mfa-auth/src/auth-manager.ts +4 -2
  83. package/extensions/mfa-auth/src/config.ts +1 -4
  84. package/extensions/mfa-auth/src/dabby-client.test.ts +68 -147
  85. package/extensions/mfa-auth/src/dabby-client.ts +70 -89
  86. package/extensions/mfa-auth/src/feishu-support/index.ts +2 -2
  87. package/extensions/mfa-auth/src/notification-service.ts +19 -14
  88. package/extensions/mfa-auth/src/providers/base.ts +0 -1
  89. package/extensions/mfa-auth/src/providers/qr-code.ts +3 -506
  90. package/extensions/mfa-auth/src/server.ts +3 -223
  91. package/extensions/mfa-auth/src/types.ts +13 -36
  92. package/package.json +458 -460
  93. package/dist/compact.runtime-BEn3giMt.js +0 -39
  94. package/dist/deliver-runtime-DkQ3XzGv.js +0 -19
  95. package/dist/deps-send-discord.runtime-BLpqSj6s.js +0 -19
  96. package/dist/deps-send-imessage.runtime-BFzyYqvR.js +0 -18
  97. package/dist/deps-send-signal.runtime-DT0TYCy1.js +0 -17
  98. package/dist/deps-send-slack.runtime-BhaGFfMX.js +0 -17
  99. package/dist/deps-send-telegram.runtime-B6Cic9NX.js +0 -20
  100. package/dist/deps-send-whatsapp.runtime-WtEhIq2S.js +0 -43
  101. package/dist/image-runtime-B1LFYfQ2.js +0 -12
  102. package/dist/manager-runtime-Da7ME9vS.js +0 -15
  103. package/dist/pi-model-discovery-runtime-DjM7Z1fx.js +0 -12
  104. package/dist/runtime-whatsapp-login.runtime-D4BRhQkK.js +0 -13
  105. package/dist/runtime-whatsapp-outbound.runtime-DJPpS6g-.js +0 -17
  106. package/dist/slash-commands.runtime-Cu1lTjV9.js +0 -12
  107. package/dist/slash-dispatch.runtime-DRVJEF4l.js +0 -39
  108. package/dist/slash-skill-commands.runtime-C373PJjv.js +0 -13
  109. package/dist/subagent-registry-runtime-D7hWBo1G.js +0 -39
  110. package/extensions/mfa-auth/node_modules/.bin/qrcode-terminal.CMD +0 -12
  111. package/extensions/mfa-auth/node_modules/.bin/tsx.CMD +0 -12
@@ -1,7 +1,6 @@
1
1
  import { authManager } from "../auth-manager.js";
2
2
  import { config } from "../config.js";
3
3
  import { dabbyClient } from "../dabby-client.js";
4
- import { renderQrPngBase64 } from "../qr.js";
5
4
  import type { AuthSession, AuthResult } from "../types.js";
6
5
  import { BaseAuthProvider } from "./base.js";
7
6
 
@@ -12,12 +11,12 @@ export class QrCodeAuthProvider extends BaseAuthProvider {
12
11
 
13
12
  async initialize(session: AuthSession): Promise<void> {
14
13
  try {
15
- const tokenInfo = await dabbyClient.getQrCode();
14
+ const tokenInfo = await dabbyClient.getVerifyCode();
16
15
 
17
16
  authManager.updateAuthStatus(session.sessionId, "pending");
18
17
  session.certToken = tokenInfo.certToken;
19
- session.qrcodeContent = tokenInfo.qrcodeContent;
20
- session.expireTimeMs = tokenInfo.expireTimeMs;
18
+ session.qrCodeUrl = tokenInfo.qrCodeUrl;
19
+ session.expireTimeMs = Date.now() + 5 * 60 * 1000;
21
20
  session.authStatus = "pending";
22
21
 
23
22
  console.log(`[mfa-auth] QR code initialized for session ${session.sessionId}`);
@@ -61,508 +60,6 @@ export class QrCodeAuthProvider extends BaseAuthProvider {
61
60
  return { success: false, error: String(error), status: "failed" };
62
61
  }
63
62
  }
64
-
65
- async generateAuthPage(session: AuthSession, authUrl: string): Promise<string> {
66
- const remainingTime = Math.max(
67
- 0,
68
- Math.ceil((config.timeout - (Date.now() - session.timestamp)) / 1000),
69
- );
70
- const triggerType = session.originalContext.triggerType || "sensitive_operation";
71
- const commandPreview =
72
- session.originalContext.commandBody.length > 100
73
- ? session.originalContext.commandBody.substring(0, 100) + "..."
74
- : session.originalContext.commandBody;
75
-
76
- const qrCode = session.qrcodeContent ? await renderQrPngBase64(session.qrcodeContent) : "";
77
- const isReauth = session.originalContext.commandBody.trim() === "/reauth";
78
-
79
- return this.renderHtml(
80
- session.sessionId,
81
- commandPreview,
82
- qrCode,
83
- remainingTime,
84
- triggerType,
85
- isReauth,
86
- authUrl,
87
- session.qrcodeContent || "",
88
- );
89
- }
90
-
91
- private renderHtml(
92
- sessionId: string,
93
- commandPreview: string,
94
- qrCode: string,
95
- remainingTime: number,
96
- triggerType: "first_message" | "sensitive_operation" = "sensitive_operation",
97
- isReauth: boolean = false,
98
- authUrl: string,
99
- qrCodeContent: string,
100
- ): string {
101
- const escapedPreview = this.escapeHtml(commandPreview);
102
- const isFirstMessageAuth = triggerType === "first_message";
103
- const pageTitle = isFirstMessageAuth ? (isReauth ? "重新认证" : "首次认证") : "二次认证";
104
- const pageTitleWithIcon = isFirstMessageAuth
105
- ? isReauth
106
- ? "🔐 重新认证"
107
- : "🔐 首次认证"
108
- : "🔐 二次认证";
109
-
110
- return `
111
- <!DOCTYPE html>
112
- <html>
113
- <head>
114
- <meta charset="utf-8">
115
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
116
- <title>${pageTitle}</title>
117
- <style>
118
- body {
119
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
120
- display: flex;
121
- align-items: center;
122
- justify-content: center;
123
- min-height: 100vh;
124
- margin: 0;
125
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
126
- }
127
- .container {
128
- background: white;
129
- padding: 40px;
130
- border-radius: 12px;
131
- box-shadow: 0 10px 40px rgba(0,0,0,0.2);
132
- max-width: 400px;
133
- width: 90%;
134
- }
135
- h1 {
136
- color: #333;
137
- margin-top: 0;
138
- font-size: 24px;
139
- text-align: center;
140
- }
141
- .info {
142
- background: #f7fafc;
143
- padding: 15px;
144
- border-radius: 6px;
145
- margin: 20px 0;
146
- font-size: 14px;
147
- color: #4a5568;
148
- }
149
- .info strong {
150
- color: #2d3748;
151
- }
152
- .timer {
153
- text-align: center;
154
- color: #e53e3e;
155
- font-weight: 600;
156
- margin: 10px 0;
157
- }
158
- .status {
159
- text-align: center;
160
- padding: 10px;
161
- border-radius: 6px;
162
- margin: 10px 0;
163
- font-weight: 600;
164
- display: none;
165
- }
166
- .status.error {
167
- background: #fed7d7;
168
- color: #742a2a;
169
- display: block;
170
- }
171
- .result {
172
- text-align: center;
173
- padding: 15px;
174
- border-radius: 6px;
175
- margin-top: 20px;
176
- font-weight: 600;
177
- display: none;
178
- white-space: pre-line;
179
- }
180
- .result.success {
181
- background: transparent;
182
- color: #111827;
183
- padding: 0;
184
- white-space: normal;
185
- }
186
- .result.error {
187
- background: #fed7d7;
188
- color: #742a2a;
189
- }
190
- .qr-section {
191
- text-align: center;
192
- margin: 20px 0;
193
- padding: 15px;
194
- background: #f7fafc;
195
- border-radius: 8px;
196
- border: 1px solid #e2e8f0;
197
- }
198
- .qr-section h3 {
199
- margin: 0 0 10px 0;
200
- font-size: 14px;
201
- color: #4a5568;
202
- }
203
- .qr-image {
204
- display: inline-block;
205
- padding: 10px;
206
- background: white;
207
- border-radius: 4px;
208
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
209
- }
210
- .qr-actions {
211
- margin-top: 15px;
212
- }
213
- .refresh-btn {
214
- background: white;
215
- border: 1px solid #dcdfe6;
216
- color: #606266;
217
- padding: 8px 15px;
218
- border-radius: 4px;
219
- cursor: pointer;
220
- font-size: 13px;
221
- transition: all 0.2s;
222
- display: inline-flex;
223
- align-items: center;
224
- gap: 5px;
225
- }
226
- .refresh-btn:hover {
227
- color: #409eff;
228
- border-color: #c6e2ff;
229
- background-color: #ecf5ff;
230
- }
231
- .refresh-btn:disabled {
232
- color: #c0c4cc;
233
- cursor: not-allowed;
234
- border-color: #ebeef5;
235
- background-color: #fff;
236
- }
237
- .qr-link {
238
- margin-top: 15px;
239
- text-align: center;
240
- padding: 10px;
241
- background: #f7fafc;
242
- border-radius: 6px;
243
- }
244
- .qr-link-label {
245
- font-size: 12px;
246
- color: #718096;
247
- margin-bottom: 5px;
248
- }
249
- .qr-link-url {
250
- color: #3b82f6;
251
- text-decoration: none;
252
- word-break: break-all;
253
- font-size: 13px;
254
- }
255
- .qr-link-url:hover {
256
- text-decoration: underline;
257
- color: #2563eb;
258
- }
259
- body.success-mode {
260
- background: #ffffff;
261
- }
262
- .container.success-mode {
263
- max-width: 520px;
264
- width: 100%;
265
- box-shadow: none;
266
- border-radius: 0;
267
- padding: 70px 30px;
268
- }
269
- .success-view {
270
- text-align: center;
271
- }
272
- .success-icon {
273
- width: 110px;
274
- height: 110px;
275
- border-radius: 9999px;
276
- background: #67c23a;
277
- display: inline-flex;
278
- align-items: center;
279
- justify-content: center;
280
- margin: 0 auto 22px auto;
281
- }
282
- .success-icon::before {
283
- content: "✓";
284
- color: #ffffff;
285
- font-size: 64px;
286
- line-height: 1;
287
- font-weight: 700;
288
- transform: translateY(-2px);
289
- }
290
- .success-title {
291
- margin: 0 0 14px 0;
292
- font-size: 34px;
293
- color: #111827;
294
- letter-spacing: 1px;
295
- }
296
- .success-subtitle {
297
- margin: 0;
298
- font-size: 18px;
299
- color: #6b7280;
300
- line-height: 1.7;
301
- }
302
- .loading {
303
- display: inline-block;
304
- width: 20px;
305
- height: 20px;
306
- border: 3px solid #f3f3f3;
307
- border-top: 3px solid #3498db;
308
- border-radius: 50%;
309
- animation: spin 1s linear infinite;
310
- }
311
- @keyframes spin {
312
- 0% { transform: rotate(0deg); }
313
- 100% { transform: rotate(360deg); }
314
- }
315
- </style>
316
- </head>
317
- <body>
318
- <div class="container">
319
- <h1>${pageTitleWithIcon}</h1>
320
- <div class="info">
321
- <p>待验证操作:</p>
322
- <strong>${escapedPreview}</strong>
323
- </div>
324
- <div class="qr-section">
325
- <h3>📱 请打开【微信或数字身份助手APP】扫码</h3>
326
- <div class="qr-image">
327
- ${qrCode ? `<img id="qr-img" src="data:image/png;base64,${qrCode}" alt="认证二维码" width="200" height="200">` : '<p class="loading"></p><p>正在生成二维码...</p>'}
328
- </div>
329
- <div class="qr-actions">
330
- <button id="refresh-btn" class="refresh-btn" onclick="refreshQrCode()">
331
- <span class="refresh-icon">🔄</span> 刷新二维码
332
- </button>
333
- </div>
334
- <div class="qr-link">
335
- <div class="qr-link-label">🔗 二维码链接:</div>
336
- <a href="${qrCodeContent}" class="qr-link-url" id="qr-link-url" target="_blank">${qrCodeContent}</a>
337
- </div>
338
- </div>
339
- <div class="timer">⏱️ 有效期: <span id="timer">${Math.floor(remainingTime / 60)}:${String(remainingTime % 60).padStart(2, "0")}</span></div>
340
- <div id="status" class="status"></div>
341
- <div id="result" class="result"></div>
342
- </div>
343
- <script>
344
- function escapeHtml(text) {
345
- const div = document.createElement('div');
346
- div.textContent = text;
347
- return div.innerHTML;
348
- }
349
-
350
- const sessionId = "${sessionId}";
351
- const triggerType = "${triggerType}";
352
- const isFirstMessageAuth = triggerType === "first_message";
353
- const isReauth = ${isReauth};
354
- let timeLeft = ${remainingTime};
355
- let pollInterval;
356
- let timerInterval;
357
- let isPolling = true;
358
-
359
- function updateTimer() {
360
- const timerEl = document.getElementById('timer');
361
- if (!timerEl) return;
362
-
363
- const minutes = Math.floor(timeLeft / 60);
364
- const seconds = timeLeft % 60;
365
- timerEl.textContent = minutes + ':' + String(seconds).padStart(2, '0');
366
-
367
- if (timeLeft <= 0) {
368
- clearInterval(pollInterval);
369
- clearInterval(timerInterval);
370
- isPolling = false;
371
- showExpired();
372
- }
373
- timeLeft--;
374
- }
375
-
376
- function showSuccess() {
377
- clearInterval(pollInterval);
378
- clearInterval(timerInterval);
379
- isPolling = false;
380
-
381
- const result = document.getElementById('result');
382
- // Ensure result is visible immediately
383
- if (result) {
384
- result.style.display = 'block';
385
- result.classList.add('success');
386
- result.classList.remove('error');
387
- }
388
-
389
- const qrSection = document.querySelector('.qr-section');
390
- const timerDiv = document.querySelector('.timer');
391
- const infoEl = document.querySelector('.info');
392
- const headingEl = document.querySelector('h1');
393
- const containerEl = document.querySelector('.container');
394
- const operationEl = document.querySelector('.info strong');
395
- const statusEl = document.getElementById('status');
396
- const refreshBtn = document.getElementById('refresh-btn');
397
-
398
- if (refreshBtn) refreshBtn.style.display = 'none';
399
-
400
- const operationName = operationEl ? operationEl.textContent.trim() : '';
401
- const operationNameTag = operationName ? '【' + escapeHtml(operationName) + '】' : '';
402
-
403
- let successMessage = '';
404
- if (isFirstMessageAuth) {
405
- successMessage = isReauth
406
- ? '✅ 认证成功!请回到聊天窗口,重新发送消息以继续对话。'
407
- : '✅ 认证成功!请回到聊天窗口,重新发送消息以继续对话。';
408
- } else {
409
- successMessage = '✅ 认证成功!<br><br>请回到聊天窗口,重新发送之前的命令' + operationNameTag + '即可执行。';
410
- }
411
-
412
- if (result) {
413
- result.innerHTML =
414
- '<div class="success-view">' +
415
- '<div class="success-icon"></div>' +
416
- '<h2 class="success-title">扫码认证成功</h2>' +
417
- '<p class="success-subtitle">' + successMessage + '</p>' +
418
- '</div>';
419
- }
420
-
421
- if (qrSection) qrSection.style.display = 'none';
422
- if (timerDiv) timerDiv.style.display = 'none';
423
- if (infoEl) infoEl.style.display = 'none';
424
- if (headingEl) headingEl.style.display = 'none';
425
- if (statusEl) statusEl.style.display = 'none';
426
- if (containerEl) containerEl.classList.add('success-mode');
427
- document.body.classList.add('success-mode');
428
- }
429
-
430
- function showError(message) {
431
- clearInterval(pollInterval);
432
- clearInterval(timerInterval);
433
- isPolling = false;
434
-
435
- const result = document.getElementById('result');
436
- result.textContent = '❌ ' + message;
437
- result.style.display = 'block';
438
- result.classList.add('error');
439
- result.classList.remove('success');
440
- }
441
-
442
- function showExpired() {
443
- clearInterval(pollInterval);
444
- clearInterval(timerInterval);
445
- isPolling = false;
446
-
447
- const result = document.getElementById('result');
448
- result.innerHTML = '⚠️ 二维码已过期<br><button onclick="refreshQrCode()" class="refresh-btn" style="margin-top:10px">🔄 点击刷新</button>';
449
- result.style.display = 'block';
450
- result.classList.add('error');
451
- result.classList.remove('success');
452
-
453
- const timerEl = document.getElementById('timer');
454
- if(timerEl) timerEl.textContent = "0:00";
455
- }
456
-
457
- async function refreshQrCode() {
458
- const btn = document.getElementById('refresh-btn');
459
- const img = document.getElementById('qr-img');
460
- const result = document.getElementById('result');
461
- const statusEl = document.getElementById('status');
462
-
463
- if (btn) {
464
- btn.disabled = true;
465
- btn.innerHTML = '<span class="loading" style="width:14px;height:14px;border-width:2px;margin-right:5px"></span> 刷新中...';
466
- }
467
-
468
- try {
469
- const response = await fetch('/mfa-auth/refresh', {
470
- method: 'POST',
471
- headers: { 'Content-Type': 'application/json' },
472
- body: JSON.stringify({ sessionId })
473
- });
474
-
475
- const data = await response.json();
476
-
477
- if (data.success) {
478
- if (img) img.src = 'data:image/png;base64,' + data.qrcodeBase64;
479
-
480
- // 更新二维码链接
481
- const qrLink = document.getElementById('qr-link-url');
482
- if (qrLink && data.qrcodeContent) {
483
- qrLink.href = data.qrcodeContent;
484
- qrLink.textContent = data.qrcodeContent;
485
- }
486
-
487
- timeLeft = data.remainingTime;
488
- isPolling = true;
489
-
490
- // Hide error/result
491
- result.style.display = 'none';
492
- if (statusEl) statusEl.style.display = 'none';
493
-
494
- // Reset timer interval if needed
495
- // It runs every 1s, so just updating timeLeft is enough
496
-
497
- // Restart polling if stopped
498
- clearInterval(pollInterval);
499
- clearInterval(timerInterval);
500
- pollInterval = setInterval(pollAuthStatus, 2000);
501
- timerInterval = setInterval(updateTimer, 1000);
502
-
503
- // Reset button
504
- if (btn) {
505
- btn.disabled = false;
506
- btn.innerHTML = '<span class="refresh-icon">🔄</span> 刷新二维码';
507
- }
508
- } else {
509
- throw new Error(data.error || '刷新失败');
510
- }
511
- } catch (error) {
512
- alert('刷新失败: ' + error.message);
513
- if (btn) {
514
- btn.disabled = false;
515
- btn.innerHTML = '<span class="refresh-icon">🔄</span> 重试刷新';
516
- }
517
- }
518
- }
519
-
520
- async function pollAuthStatus() {
521
- if (!isPolling) return;
522
-
523
- try {
524
- console.log('[mfa-auth] Polling auth status for session:', sessionId);
525
- const response = await fetch('/mfa-auth/verify', {
526
- method: 'POST',
527
- headers: { 'Content-Type': 'application/json' },
528
- body: JSON.stringify({ sessionId })
529
- });
530
- const data = await response.json();
531
- console.log('[mfa-auth] Poll response:', data);
532
-
533
- if (data.success) {
534
- clearInterval(pollInterval);
535
- isPolling = false;
536
- showSuccess();
537
- } else if (data.status === 'failed') {
538
- showError(data.error || '认证失败,请重试');
539
- } else if (data.status === 'expired') {
540
- showExpired();
541
- }
542
- } catch (error) {
543
- console.error('Polling error:', error);
544
- }
545
- }
546
-
547
- timerInterval = setInterval(updateTimer, 1000);
548
- pollInterval = setInterval(pollAuthStatus, 2000);
549
- pollAuthStatus();
550
- </script>
551
- </body>
552
- </html>
553
- `;
554
- }
555
-
556
- private escapeHtml(text: string): string {
557
- const map: Record<string, string> = {
558
- "&": "&amp;",
559
- "<": "&lt;",
560
- ">": "&gt;",
561
- '"': "&quot;",
562
- "'": "&#39;",
563
- };
564
- return text.replace(/[&<>"']/g, (c) => map[c]);
565
- }
566
63
  }
567
64
 
568
65
  class QrCodeAuthProviderFactory {