cclawd 1.0.7 → 1.0.8

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 (168) hide show
  1. package/dist/{active-listener-DYmI7imH.js → active-listener-Dkhmfuwx.js} +2 -2
  2. package/dist/{api-key-rotation-DLU4jvSu.js → api-key-rotation-BOfI3cG3.js} +1 -1
  3. package/dist/{audio-preflight-C9TMbRb4.js → audio-preflight-CtkZ5SAs.js} +15 -15
  4. package/dist/{audio-transcription-runner-Q5zG_hYd.js → audio-transcription-runner-CbPqoiHX.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-DWAER4wd.js} +2 -2
  12. package/dist/{commands-registry-llLVCTH9.js → commands-registry-BUyiA7nE.js} +2 -2
  13. package/dist/compact.runtime-DpcZpcTl.js +39 -0
  14. package/dist/{deliver-DJf2ZBpe.js → deliver-aHOaRbkt.js} +19 -19
  15. package/dist/deliver-runtime-D4bCsr6d.js +19 -0
  16. package/dist/deps-send-discord.runtime-BziKU-pE.js +19 -0
  17. package/dist/deps-send-imessage.runtime-CFRnDTqp.js +18 -0
  18. package/dist/deps-send-signal.runtime-BuOtABJm.js +17 -0
  19. package/dist/deps-send-slack.runtime-BOLqvMxW.js +17 -0
  20. package/dist/deps-send-telegram.runtime-DeEoFLv5.js +20 -0
  21. package/dist/deps-send-whatsapp.runtime-CG1uXYLY.js +43 -0
  22. package/dist/{diagnostic-BCCMF3O_.js → diagnostic-BdcXX9iJ.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-D9NUULbj.js} +5 -5
  26. package/dist/{fetch-guard-CqpEmMQ2.js → fetch-guard-B5ZMnGaN.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-4x07m4Jl.js} +4 -4
  30. package/dist/image-runtime-smkMrIol.js +12 -0
  31. package/dist/{ir-CzM3SxId.js → ir-CsgNUpOU.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-p_O59TVQ.js} +4 -4
  35. package/dist/{login-qr-DY_i60f5.js → login-qr-BCJpDsAy.js} +10 -10
  36. package/dist/{manager-FAQPC0uO.js → manager-CwYv8O3T.js} +12 -12
  37. package/dist/manager-runtime-D_jEoBr9.js +15 -0
  38. package/dist/{model-selection-wf3OY5DX.js → model-selection-Cv2Puf5z.js} +122 -122
  39. package/dist/{outbound-Bw0dOVS7.js → outbound-Chpiwybe.js} +6 -6
  40. package/dist/{outbound-attachment-1R6r9Pg_.js → outbound-attachment-BnAVJDLe.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-CJVNBk0y.js} +150 -150
  44. package/dist/pi-model-discovery-7IzK0Uc3.js +131 -0
  45. package/dist/pi-model-discovery-runtime-DABef3qy.js +12 -0
  46. package/dist/{pi-tools.before-tool-call.runtime-D_mthvtC.js → pi-tools.before-tool-call.runtime-BP2UvGJb.js} +10 -10
  47. package/dist/plugin-sdk/active-listener-CN-tMEvN.js +35 -0
  48. package/dist/plugin-sdk/api-key-rotation-CimGYMBc.js +176 -0
  49. package/dist/plugin-sdk/audio-preflight-C-xXBoE2.js +51 -0
  50. package/dist/plugin-sdk/audio-transcription-runner-CTIHpebA.js +2173 -0
  51. package/dist/plugin-sdk/audit-membership-runtime-BFatB2LJ.js +58 -0
  52. package/dist/plugin-sdk/channel-activity-DO0FEzyj.js +95 -0
  53. package/dist/plugin-sdk/channel-web-Da-__nUF.js +2238 -0
  54. package/dist/plugin-sdk/commands-registry-6no2NNrY.js +1118 -0
  55. package/dist/plugin-sdk/compact.runtime-CCoclu5e.js +35 -0
  56. package/dist/plugin-sdk/compat.js +35 -35
  57. package/dist/plugin-sdk/config-B9ODwgpz.js +37426 -0
  58. package/dist/plugin-sdk/deliver-B1fFpKjV.js +1757 -0
  59. package/dist/plugin-sdk/deliver-runtime-DB-VRMe1.js +15 -0
  60. package/dist/plugin-sdk/deps-send-discord.runtime-DklqycYG.js +15 -0
  61. package/dist/plugin-sdk/deps-send-imessage.runtime-Chs8zeon.js +14 -0
  62. package/dist/plugin-sdk/deps-send-signal.runtime-clW9aSJP.js +13 -0
  63. package/dist/plugin-sdk/deps-send-slack.runtime-BUx0LYY1.js +13 -0
  64. package/dist/plugin-sdk/deps-send-telegram.runtime-LECSHgMG.js +16 -0
  65. package/dist/plugin-sdk/deps-send-whatsapp.runtime-D2d65fw0.js +40 -0
  66. package/dist/plugin-sdk/diagnostic-CxIvS-C2.js +315 -0
  67. package/dist/plugin-sdk/dispatch-BqlR4dPx.js +105863 -0
  68. package/dist/plugin-sdk/env-b9k1PHMI.js +34 -0
  69. package/dist/plugin-sdk/fetch-PoxzAANT.js +326 -0
  70. package/dist/plugin-sdk/fetch-guard-4UVSZ0uS.js +164 -0
  71. package/dist/plugin-sdk/image-Ch6M4tnJ.js +2420 -0
  72. package/dist/plugin-sdk/image-runtime-CSh2o5wY.js +8 -0
  73. package/dist/plugin-sdk/ir-CugsqGH8.js +1312 -0
  74. package/dist/plugin-sdk/local-roots-adnEg9zb.js +217 -0
  75. package/dist/plugin-sdk/logger-D6zRubj0.js +1164 -0
  76. package/dist/plugin-sdk/login-CYvkQ0At.js +54 -0
  77. package/dist/plugin-sdk/login-qr-ll4NtaT5.js +316 -0
  78. package/dist/plugin-sdk/manager-CHy8IclH.js +3959 -0
  79. package/dist/plugin-sdk/manager-runtime-C70EkEr7.js +11 -0
  80. package/dist/plugin-sdk/outbound-Wzs2iN7X.js +216 -0
  81. package/dist/plugin-sdk/outbound-attachment-khXJwucX.js +17 -0
  82. package/dist/plugin-sdk/paths-BtVqCdw4.js +3063 -0
  83. package/dist/{pi-model-discovery-ItS07aJB.js → plugin-sdk/pi-model-discovery-Dh4ziodY.js} +2 -2
  84. package/dist/plugin-sdk/pi-model-discovery-runtime-b83Xe-HT.js +8 -0
  85. package/dist/plugin-sdk/pi-tools.before-tool-call.runtime-C1z5CDBF.js +349 -0
  86. package/dist/{proxy-fetch-c1ZUFFcO.js → plugin-sdk/proxy-fetch-CJEmoBxi.js} +1 -1
  87. package/dist/plugin-sdk/pw-ai-Dj3Cvlzl.js +1990 -0
  88. package/dist/plugin-sdk/qmd-manager-egHUAseQ.js +1581 -0
  89. package/dist/plugin-sdk/resolve-outbound-target-BiICvIKs.js +38 -0
  90. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-DNApufzW.js +9 -0
  91. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-CBmtfIQ8.js +13 -0
  92. package/dist/plugin-sdk/send-CScblaI4.js +532 -0
  93. package/dist/plugin-sdk/send-CeHhnld6.js +407 -0
  94. package/dist/plugin-sdk/send-DP_c8JfR.js +3277 -0
  95. package/dist/plugin-sdk/send-Dc5fI6e8.js +495 -0
  96. package/dist/plugin-sdk/send-l-77_s1_.js +2507 -0
  97. package/dist/plugin-sdk/session-CkOKZaqa.js +166 -0
  98. package/dist/plugin-sdk/signal.js +2 -2
  99. package/dist/plugin-sdk/skill-commands-BohYCgkq.js +336 -0
  100. package/dist/plugin-sdk/slash-commands.runtime-DpLfVTM6.js +8 -0
  101. package/dist/plugin-sdk/slash-dispatch.runtime-CASMHwpm.js +35 -0
  102. package/dist/plugin-sdk/slash-skill-commands.runtime-D7rrJEci.js +9 -0
  103. package/dist/plugin-sdk/sqlite-CJE3X7Mv.js +1005 -0
  104. package/dist/plugin-sdk/subagent-registry-runtime-B1oo5bih.js +35 -0
  105. package/dist/plugin-sdk/tables-D5VgpTmm.js +53 -0
  106. package/dist/plugin-sdk/target-errors-C6zZ_OpA.js +191 -0
  107. package/dist/{tokens-BWDIKewp.js → plugin-sdk/tokens-DUnJnpMS.js} +1 -1
  108. package/dist/plugin-sdk/web-TfUM1nSi.js +39 -0
  109. package/dist/plugin-sdk/whatsapp-actions-DuWJ0j1r.js +71 -0
  110. package/dist/proxy-fetch-BOh1PLOW.js +54 -0
  111. package/dist/{pw-ai-Ok6KGelf.js → pw-ai-DwH5GpEO.js} +9 -9
  112. package/dist/{qmd-manager-DhfEz4Ar.js → qmd-manager-DEscZz5_.js} +6 -6
  113. package/dist/{query-expansion-GqNV2iIE.js → query-expansion-BErUY8P2.js} +4 -4
  114. package/dist/runtime-whatsapp-login.runtime-BI3U306v.js +13 -0
  115. package/dist/runtime-whatsapp-outbound.runtime-Bsc2uD09.js +17 -0
  116. package/dist/{send-DwAoiT2p.js → send-BDnOgWIp.js} +25 -25
  117. package/dist/{send-CS0ocZHl.js → send-C-Q_WPMf.js} +3 -3
  118. package/dist/{send-6R8b9zsj.js → send-DUibfNQD.js} +5 -5
  119. package/dist/{send-DPflcjM5.js → send-DtBvCnPQ.js} +6 -6
  120. package/dist/{send-CEg4P96c.js → send-ORtn50qg.js} +5 -5
  121. package/dist/{session-BoIID5UR.js → session-B7imi6T5.js} +7 -7
  122. package/dist/{skill-commands-DhdiziMs.js → skill-commands-B9brPuiL.js} +9 -9
  123. package/dist/slash-commands.runtime-Cf6ygfBp.js +12 -0
  124. package/dist/slash-dispatch.runtime-CsmvhO5K.js +39 -0
  125. package/dist/slash-skill-commands.runtime-CX7stIEP.js +13 -0
  126. package/dist/subagent-registry-runtime-B_S1nf7y.js +39 -0
  127. package/dist/{subsystem-C8z6w6xC.js → subsystem-DfXy5gUB.js} +14 -14
  128. package/dist/{tables-DQusRhkD.js → tables-CjQqTOdD.js} +1 -1
  129. package/dist/{target-errors-CfavnC9U.js → target-errors-BZE1mc-W.js} +1 -1
  130. package/dist/tokens-6ul2IrzG.js +50 -0
  131. package/dist/{web-CrcrTQ2c.js → web-Cd8yK1Zq.js} +39 -39
  132. package/dist/{whatsapp-actions-B0u0ZAme.js → whatsapp-actions-CYEzUMBI.js} +15 -15
  133. package/dist/{workspace-CWDYHR27.js → workspace-DGIcKCCW.js} +20 -20
  134. package/extensions/cclawd-mfa-auth/README.md +118 -0
  135. package/extensions/cclawd-mfa-auth/index.ts +382 -0
  136. package/extensions/cclawd-mfa-auth/openclaw.plugin.json +34 -0
  137. package/extensions/cclawd-mfa-auth/package.json +27 -0
  138. package/extensions/cclawd-mfa-auth/src/auth-manager.ts +536 -0
  139. package/extensions/cclawd-mfa-auth/src/config.ts +82 -0
  140. package/extensions/cclawd-mfa-auth/src/dabby-client.ts +200 -0
  141. package/extensions/cclawd-mfa-auth/src/notification-service.ts +417 -0
  142. package/extensions/cclawd-mfa-auth/src/polling-manager.ts +232 -0
  143. package/extensions/cclawd-mfa-auth/src/providers/base.ts +25 -0
  144. package/extensions/cclawd-mfa-auth/src/providers/qr-code.ts +98 -0
  145. package/extensions/cclawd-mfa-auth/src/sensitive-detector.ts +159 -0
  146. package/extensions/cclawd-mfa-auth/src/session-resolver.ts +159 -0
  147. package/extensions/cclawd-mfa-auth/src/types.ts +156 -0
  148. package/extensions/mfa-auth/index.ts +675 -1028
  149. package/extensions/mfa-auth/openclaw.plugin.json +1 -1
  150. package/extensions/mfa-auth/src/notification-service.ts +2 -8
  151. package/package.json +453 -453
  152. package/dist/compact.runtime-BEn3giMt.js +0 -39
  153. package/dist/deliver-runtime-DkQ3XzGv.js +0 -19
  154. package/dist/deps-send-discord.runtime-BLpqSj6s.js +0 -19
  155. package/dist/deps-send-imessage.runtime-BFzyYqvR.js +0 -18
  156. package/dist/deps-send-signal.runtime-DT0TYCy1.js +0 -17
  157. package/dist/deps-send-slack.runtime-BhaGFfMX.js +0 -17
  158. package/dist/deps-send-telegram.runtime-B6Cic9NX.js +0 -20
  159. package/dist/deps-send-whatsapp.runtime-WtEhIq2S.js +0 -43
  160. package/dist/image-runtime-B1LFYfQ2.js +0 -12
  161. package/dist/manager-runtime-Da7ME9vS.js +0 -15
  162. package/dist/pi-model-discovery-runtime-DjM7Z1fx.js +0 -12
  163. package/dist/runtime-whatsapp-login.runtime-D4BRhQkK.js +0 -13
  164. package/dist/runtime-whatsapp-outbound.runtime-DJPpS6g-.js +0 -17
  165. package/dist/slash-commands.runtime-Cu1lTjV9.js +0 -12
  166. package/dist/slash-dispatch.runtime-DRVJEF4l.js +0 -39
  167. package/dist/slash-skill-commands.runtime-C373PJjv.js +0 -13
  168. package/dist/subagent-registry-runtime-D7hWBo1G.js +0 -39
@@ -0,0 +1,34 @@
1
+ {
2
+ "id": "cclawd-mfa-auth",
3
+ "name": "Cclawd MFA Auth",
4
+ "version": "1.0.0",
5
+ "description": "MFA authentication plugin for Cclawd with multi-channel support",
6
+ "main": "index.ts",
7
+ "hooks": ["before_tool_call"],
8
+ "author": "",
9
+ "license": "MIT",
10
+ "keywords": [
11
+ "mfa",
12
+ "authentication",
13
+ "security"
14
+ ],
15
+ "config": {
16
+ "env": [
17
+ "MFA_AUTH_API_KEY",
18
+ "DABBY_API_BASE_URL",
19
+ "MFA_VERIFICATION_DURATION",
20
+ "MFA_FIRST_MESSAGE_AUTH_DURATION",
21
+ "MFA_REQUIRE_AUTH_ON_FIRST_MESSAGE",
22
+ "MFA_REQUIRE_AUTH_ON_SENSITIVE_OPERATION",
23
+ "MFA_SENSITIVE_KEYWORDS",
24
+ "MFA_GATEWAY_HOST",
25
+ "MFA_ENABLE_AUTH_NOTIFICATION",
26
+ "MFA_AUTH_STATE_DIR"
27
+ ]
28
+ },
29
+ "configSchema": {
30
+ "type": "object",
31
+ "additionalProperties": false,
32
+ "properties": {}
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "cclawd-mfa-auth",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "MFA authentication plugin for Cclawd with multi-channel support",
6
+ "main": "index.ts",
7
+ "scripts": {
8
+ "dev": "tsx watch index.ts",
9
+ "build": "tsc"
10
+ },
11
+ "dependencies": {
12
+ "@larksuiteoapi/node-sdk": "^1.59.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^20.0.0",
16
+ "tsx": "^4.21.0",
17
+ "typescript": "^5.0.0"
18
+ },
19
+ "keywords": [
20
+ "openclaw",
21
+ "mfa",
22
+ "authentication",
23
+ "plugin"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT"
27
+ }
@@ -0,0 +1,536 @@
1
+ /**
2
+ * Cclawd MFA Auth Plugin - Authentication Session Manager
3
+ */
4
+
5
+ import crypto from 'node:crypto';
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { config } from './config.js';
9
+ import type {
10
+ AuthSession,
11
+ AuthMethodProvider,
12
+ AuthResult,
13
+ PendingAuthContext,
14
+ NotificationInfo,
15
+ } from './types.js';
16
+
17
+ /**
18
+ * 首次消息认证记录
19
+ */
20
+ interface FirstMessageAuthRecord {
21
+ verifiedAt: number;
22
+ }
23
+
24
+ /**
25
+ * 认证管理器
26
+ */
27
+ export class AuthManager {
28
+ // 会话存储
29
+ private sessions = new Map<string, AuthSession>();
30
+
31
+ // 敏感操作认证状态
32
+ public verifiedForSensitiveOps = new Map<string, number>();
33
+
34
+ // 首次消息认证状态
35
+ private verifiedForFirstMessage = new Map<string, number>();
36
+
37
+ // 待发送的通知
38
+ private pendingNotifications = new Map<string, NotificationInfo>();
39
+
40
+ // 认证提供者注册表
41
+ private providers = new Map<string, AuthMethodProvider>();
42
+
43
+ // 待执行的操作
44
+ private pendingExecutions = new Map<string, { sessionId: string; timestamp: number }>();
45
+
46
+ // 配置
47
+ private config = config;
48
+
49
+ // 持久化目录
50
+ private persistDir: string;
51
+
52
+ // 初始化状态
53
+ private initialized = false;
54
+ private initPromise: Promise<void> | null = null;
55
+
56
+ constructor() {
57
+ this.persistDir = this.resolvePersistDir();
58
+ // 定期清理过期数据
59
+ setInterval(() => this.cleanup(), 30000);
60
+ }
61
+
62
+ /**
63
+ * 异步初始化
64
+ */
65
+ private async initialize(): Promise<void> {
66
+ if (this.initialized) return;
67
+ if (this.initPromise) return this.initPromise;
68
+
69
+ this.initPromise = (async () => {
70
+ try {
71
+ this.loadPersistedFirstMessageAuth();
72
+ this.initialized = true;
73
+ console.log('[cclawd-mfa-auth] AuthManager initialized successfully');
74
+ } catch (error) {
75
+ console.error('[cclawd-mfa-auth] Failed to initialize AuthManager:', error);
76
+ }
77
+ })();
78
+
79
+ return this.initPromise;
80
+ }
81
+
82
+ /**
83
+ * 解析持久化目录路径
84
+ */
85
+ private resolvePersistDir(): string {
86
+ const dir = this.config.persistAuthStateDir || '~/.openclaw/mfa-auth/';
87
+ if (dir.startsWith('~/')) {
88
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
89
+ return dir.replace('~', homeDir);
90
+ }
91
+ return dir;
92
+ }
93
+
94
+ /**
95
+ * 获取持久化文件路径
96
+ */
97
+ private getPersistFilePath(): string {
98
+ return `${this.persistDir}/first-message-auth.json`;
99
+ }
100
+
101
+ /**
102
+ * 注册认证提供者
103
+ */
104
+ registerProvider(provider: AuthMethodProvider): void {
105
+ this.providers.set(provider.methodType, provider);
106
+ console.log(`[cclawd-mfa-auth] Registered auth provider: ${provider.methodType}`);
107
+ }
108
+
109
+ /**
110
+ * 加载持久化的首次消息认证状态
111
+ */
112
+ loadPersistedFirstMessageAuth(): void {
113
+ const filePath = this.getPersistFilePath();
114
+ try {
115
+ if (fs.existsSync(filePath)) {
116
+ const content = fs.readFileSync(filePath, 'utf-8');
117
+ const records = JSON.parse(content) as Record<string, FirstMessageAuthRecord>;
118
+ const now = Date.now();
119
+ const duration = this.config.firstMessageAuthDuration || 24 * 60 * 60 * 1000;
120
+
121
+ for (const [userId, record] of Object.entries(records)) {
122
+ if (now - record.verifiedAt < duration) {
123
+ this.verifiedForFirstMessage.set(userId, record.verifiedAt);
124
+ }
125
+ }
126
+
127
+ if (this.config.debug) {
128
+ console.log(
129
+ `[cclawd-mfa-auth] Loaded ${this.verifiedForFirstMessage.size} first message auth records from ${filePath}`,
130
+ );
131
+ }
132
+ }
133
+ } catch (error) {
134
+ console.error(`[cclawd-mfa-auth] Failed to load persisted auth state: ${String(error)}`);
135
+ }
136
+ }
137
+
138
+ /**
139
+ * 持久化首次消息认证状态
140
+ */
141
+ persistFirstMessageAuth(userId: string): void {
142
+ const filePath = this.getPersistFilePath();
143
+ try {
144
+ const records: Record<string, FirstMessageAuthRecord> = {};
145
+ this.verifiedForFirstMessage.forEach((timestamp, id) => {
146
+ records[id] = { verifiedAt: timestamp };
147
+ });
148
+
149
+ const dir = path.dirname(filePath);
150
+ if (!fs.existsSync(dir)) {
151
+ fs.mkdirSync(dir, { recursive: true });
152
+ }
153
+
154
+ fs.writeFileSync(filePath, JSON.stringify(records, null, 2), 'utf-8');
155
+
156
+ if (this.config.debug) {
157
+ console.log(`[cclawd-mfa-auth] Persisted first message auth state for ${userId}`);
158
+ }
159
+ } catch (error) {
160
+ console.error(`[cclawd-mfa-auth] Failed to persist auth state: ${String(error)}`);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * 清除用户的首次消息认证状态
166
+ */
167
+ clearFirstMessageAuth(userId: string): void {
168
+ this.verifiedForFirstMessage.delete(userId);
169
+ this.persistFirstMessageAuth(userId);
170
+ if (this.config.debug) {
171
+ console.log(`[cclawd-mfa-auth] Cleared first message auth for user ${userId}`);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * 检查用户是否已通过首次消息认证
177
+ */
178
+ isUserVerifiedForFirstMessage(userId: string): boolean {
179
+ if (!this.initialized) {
180
+ if (this.config.debug) {
181
+ console.log('[cclawd-mfa-auth] AuthManager not yet initialized, allowing first message check');
182
+ }
183
+ return false;
184
+ }
185
+
186
+ const verifiedTime = this.verifiedForFirstMessage.get(userId);
187
+ if (!verifiedTime) return false;
188
+
189
+ const duration = this.config.firstMessageAuthDuration || 24 * 60 * 60 * 1000;
190
+ if (Date.now() - verifiedTime > duration) {
191
+ this.verifiedForFirstMessage.delete(userId);
192
+ this.persistFirstMessageAuth(userId);
193
+ return false;
194
+ }
195
+
196
+ return true;
197
+ }
198
+
199
+ /**
200
+ * 检查用户是否已通过敏感操作认证
201
+ */
202
+ isUserVerifiedForSensitiveOps(userId: string): boolean {
203
+ if (!this.initialized) {
204
+ if (this.config.debug) {
205
+ console.log('[cclawd-mfa-auth] AuthManager not yet initialized, allowing sensitive ops check');
206
+ }
207
+ return false;
208
+ }
209
+
210
+ const verifiedTime = this.verifiedForSensitiveOps.get(userId);
211
+ if (!verifiedTime) return false;
212
+
213
+ if (Date.now() - verifiedTime > this.config.verificationDuration) {
214
+ this.verifiedForSensitiveOps.delete(userId);
215
+ return false;
216
+ }
217
+
218
+ return true;
219
+ }
220
+
221
+ /**
222
+ * 确保已初始化
223
+ */
224
+ async ensureInitialized(): Promise<void> {
225
+ await this.initialize();
226
+ }
227
+
228
+ /**
229
+ * 获取认证提供者
230
+ */
231
+ getProvider(methodType: string): AuthMethodProvider | undefined {
232
+ return this.providers.get(methodType);
233
+ }
234
+
235
+ /**
236
+ * 生成认证会话
237
+ */
238
+ async generateSession(
239
+ userId: string,
240
+ originalContext: PendingAuthContext,
241
+ authMethod: string = this.config.defaultAuthMethod,
242
+ extraFields?: Partial<AuthSession>,
243
+ ): Promise<AuthSession | null> {
244
+ const provider = this.getProvider(authMethod);
245
+ if (!provider) {
246
+ console.error(`[cclawd-mfa-auth] Auth provider not found: ${authMethod}`);
247
+ return null;
248
+ }
249
+
250
+ const sessionId = crypto.randomUUID();
251
+ const session: AuthSession = {
252
+ sessionId,
253
+ userId,
254
+ authMethod: authMethod as AuthSession['authMethod'],
255
+ timestamp: Date.now(),
256
+ originalContext,
257
+ ...extraFields,
258
+ };
259
+
260
+ this.sessions.set(sessionId, session);
261
+
262
+ try {
263
+ await provider.initialize(session);
264
+ } catch (error) {
265
+ console.error(`[cclawd-mfa-auth] Failed to initialize session: ${error}`);
266
+ this.sessions.delete(sessionId);
267
+ return null;
268
+ }
269
+
270
+ if (this.config.debug) {
271
+ console.log(`[cclawd-mfa-auth] Generated session: ${sessionId}`);
272
+ console.log(`[cclawd-mfa-auth] User ID: ${userId}`);
273
+ console.log(`[cclawd-mfa-auth] Auth method: ${authMethod}`);
274
+ console.log(`[cclawd-mfa-auth] Total sessions: ${this.sessions.size}`);
275
+ }
276
+
277
+ return session;
278
+ }
279
+
280
+ /**
281
+ * 验证会话
282
+ */
283
+ async verifySession(sessionId: string, userInput?: string): Promise<AuthResult> {
284
+ const session = this.sessions.get(sessionId);
285
+
286
+ if (!session) {
287
+ return { success: false, error: 'Session not found' };
288
+ }
289
+
290
+ if (Date.now() - session.timestamp > this.config.timeout) {
291
+ this.sessions.delete(sessionId);
292
+ return { success: false, error: 'Session expired' };
293
+ }
294
+
295
+ const provider = this.getProvider(session.authMethod);
296
+ if (!provider) {
297
+ return { success: false, error: 'Provider not found' };
298
+ }
299
+
300
+ const result = await provider.verify(sessionId, userInput);
301
+
302
+ if (result.success) {
303
+ const triggerType = session.originalContext.triggerType || 'sensitive_operation';
304
+ const isReauth = session.originalContext.commandBody.trim() === '/reauth';
305
+
306
+ this.markUserVerified(session.userId, triggerType, isReauth);
307
+
308
+ this.sessions.delete(sessionId);
309
+
310
+ if (this.config.debug) {
311
+ console.log(`[cclawd-mfa-auth] Session verified and deleted: ${sessionId}`);
312
+ console.log(`[cclawd-mfa-auth] User ${session.userId} marked as verified (${triggerType})`);
313
+ }
314
+ }
315
+
316
+ return result;
317
+ }
318
+
319
+ /**
320
+ * 检查并消费通知
321
+ */
322
+ checkAndConsumeNotification(userId: string): NotificationInfo | null {
323
+ const info = this.pendingNotifications.get(userId);
324
+ if (info) {
325
+ this.pendingNotifications.delete(userId);
326
+ return info;
327
+ }
328
+ return null;
329
+ }
330
+
331
+ /**
332
+ * 检查用户是否已认证(通用方法)
333
+ */
334
+ isUserVerified(userId: string): boolean {
335
+ return this.isUserVerifiedForSensitiveOps(userId);
336
+ }
337
+
338
+ /**
339
+ * 获取会话
340
+ */
341
+ getSession(sessionId: string): AuthSession | undefined {
342
+ return this.sessions.get(sessionId);
343
+ }
344
+
345
+ /**
346
+ * 获取所有会话ID
347
+ */
348
+ getSessionIds(): string[] {
349
+ return Array.from(this.sessions.keys());
350
+ }
351
+
352
+ /**
353
+ * 根据用户ID获取最新的会话
354
+ */
355
+ getLatestSessionByUserId(userId: string): AuthSession | undefined {
356
+ let latestSession: AuthSession | undefined;
357
+ let latestTimestamp = 0;
358
+
359
+ for (const session of this.sessions.values()) {
360
+ if (session.userId === userId && session.timestamp > latestTimestamp) {
361
+ latestSession = session;
362
+ latestTimestamp = session.timestamp;
363
+ }
364
+ }
365
+
366
+ return latestSession;
367
+ }
368
+
369
+ /**
370
+ * 设置会话元数据
371
+ */
372
+ setSessionMetadata(sessionId: string, metadata: Record<string, unknown>): void {
373
+ const session = this.sessions.get(sessionId);
374
+ if (session) {
375
+ session.metadata = { ...session.metadata, ...metadata };
376
+ if (this.config.debug) {
377
+ console.log(`[cclawd-mfa-auth] Set metadata for session ${sessionId}:`, metadata);
378
+ }
379
+ }
380
+ }
381
+
382
+ /**
383
+ * 更新认证状态
384
+ */
385
+ updateAuthStatus(
386
+ sessionId: string,
387
+ status: 'pending' | 'scanned' | 'verified' | 'failed' | 'expired',
388
+ ): void {
389
+ const session = this.sessions.get(sessionId);
390
+ if (session) {
391
+ session.authStatus = status;
392
+ if (this.config.debug) {
393
+ console.log(`[cclawd-mfa-auth] Session ${sessionId} status updated to: ${status}`);
394
+ }
395
+ }
396
+ }
397
+
398
+ /**
399
+ * 获取证书令牌
400
+ */
401
+ getCertToken(sessionId: string): string | undefined {
402
+ const session = this.sessions.get(sessionId);
403
+ return session?.certToken;
404
+ }
405
+
406
+ /**
407
+ * 清理过期数据
408
+ */
409
+ cleanup(): void {
410
+ const now = Date.now();
411
+ let cleanedCount = 0;
412
+
413
+ // 清理过期会话
414
+ for (const [id, session] of this.sessions.entries()) {
415
+ if (now - session.timestamp > this.config.timeout) {
416
+ const provider = this.getProvider(session.authMethod);
417
+ if (provider) {
418
+ provider.cleanup(id);
419
+ }
420
+ this.sessions.delete(id);
421
+ cleanedCount++;
422
+ }
423
+ }
424
+
425
+ // 清理过期的敏感操作认证
426
+ for (const [userId, verifiedTime] of this.verifiedForSensitiveOps.entries()) {
427
+ if (now - verifiedTime > this.config.verificationDuration) {
428
+ this.verifiedForSensitiveOps.delete(userId);
429
+ cleanedCount++;
430
+ }
431
+ }
432
+
433
+ // 清理过期的首次消息认证
434
+ const firstMessageDuration = this.config.firstMessageAuthDuration || 24 * 60 * 60 * 1000;
435
+ for (const [userId, verifiedTime] of this.verifiedForFirstMessage.entries()) {
436
+ if (now - verifiedTime > firstMessageDuration) {
437
+ this.verifiedForFirstMessage.delete(userId);
438
+ this.persistFirstMessageAuth(userId);
439
+ cleanedCount++;
440
+ }
441
+ }
442
+
443
+ // 清理过期的待执行操作
444
+ for (const [userId, pending] of this.pendingExecutions.entries()) {
445
+ if (now - pending.timestamp > 10 * 60 * 1000) {
446
+ this.pendingExecutions.delete(userId);
447
+ cleanedCount++;
448
+ }
449
+ }
450
+
451
+ if (this.config.debug && cleanedCount > 0) {
452
+ console.log(`[cclawd-mfa-auth] Cleanup: removed ${cleanedCount} expired entries`);
453
+ }
454
+ }
455
+
456
+ /**
457
+ * 注册待执行操作
458
+ */
459
+ registerPendingExecution(userId: string, sessionId: string): void {
460
+ this.pendingExecutions.set(userId, { sessionId, timestamp: Date.now() });
461
+ if (this.config.debug) {
462
+ console.log(`[cclawd-mfa-auth] Registered pending execution for user ${userId}: ${sessionId}`);
463
+ }
464
+ }
465
+
466
+ /**
467
+ * 获取并清除待执行操作
468
+ */
469
+ getAndClearPendingExecution(userId: string): string | null {
470
+ const pending = this.pendingExecutions.get(userId);
471
+ if (pending) {
472
+ this.pendingExecutions.delete(userId);
473
+ if (this.config.debug) {
474
+ console.log(
475
+ `[cclawd-mfa-auth] Cleared pending execution for user ${userId}: ${pending.sessionId}`,
476
+ );
477
+ }
478
+ return pending.sessionId;
479
+ }
480
+ return null;
481
+ }
482
+
483
+ /**
484
+ * 检查是否有待执行操作
485
+ */
486
+ hasPendingExecution(userId: string): boolean {
487
+ const pending = this.pendingExecutions.get(userId);
488
+ if (!pending) return false;
489
+ const now = Date.now();
490
+ return now - pending.timestamp < 10 * 60 * 1000;
491
+ }
492
+
493
+ /**
494
+ * 标记用户已验证
495
+ */
496
+ markUserVerified(
497
+ userId: string,
498
+ triggerType: 'first_message' | 'sensitive_operation' = 'sensitive_operation',
499
+ isReauth: boolean = false,
500
+ ): void {
501
+ if (triggerType === 'first_message') {
502
+ this.verifiedForFirstMessage.set(userId, Date.now());
503
+ this.persistFirstMessageAuth(userId);
504
+ } else {
505
+ this.verifiedForSensitiveOps.set(userId, Date.now());
506
+ }
507
+ this.pendingNotifications.set(userId, { triggerType, isReauth });
508
+ if (this.config.debug) {
509
+ console.log(
510
+ `[cclawd-mfa-auth] Marked user ${userId} as verified (${triggerType}, reauth=${isReauth})`,
511
+ );
512
+ }
513
+ }
514
+ }
515
+
516
+ /**
517
+ * 认证管理器工厂
518
+ */
519
+ class AuthManagerFactory {
520
+ private static instance: AuthManager | null = null;
521
+
522
+ static getInstance(): AuthManager {
523
+ if (!this.instance) {
524
+ this.instance = new AuthManager();
525
+ console.log('[AuthManagerFactory] Created new AuthManager instance');
526
+ }
527
+ return this.instance;
528
+ }
529
+
530
+ static reset(): void {
531
+ this.instance = null;
532
+ console.log('[AuthManagerFactory] Reset AuthManager instance');
533
+ }
534
+ }
535
+
536
+ export const authManager = AuthManagerFactory.getInstance();
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Cclawd MFA Auth Plugin - Configuration Management
3
+ */
4
+
5
+ import type { MfaConfig, DabbyConfig } from './types.js';
6
+
7
+ /**
8
+ * 解析布尔类型环境变量
9
+ */
10
+ function parseBooleanEnv(envValue?: string): boolean {
11
+ if (!envValue) return false;
12
+ const value = envValue.toLowerCase().trim();
13
+ return value === 'true' || value === '1' || value === 'yes';
14
+ }
15
+
16
+ /**
17
+ * 解析字符串数组环境变量
18
+ */
19
+ function parseStringArray(envValue?: string): string[] {
20
+ if (!envValue) return [];
21
+ return envValue
22
+ .split(',')
23
+ .map((item) => item.trim().toLowerCase())
24
+ .filter(Boolean);
25
+ }
26
+
27
+ /**
28
+ * MFA 配置
29
+ */
30
+ export const config: MfaConfig = {
31
+ debug: parseBooleanEnv(process.env.MFA_DEBUG) || true,
32
+ timeout: 5 * 60 * 1000, // 5 分钟
33
+ verificationDuration:
34
+ Number.parseInt(process.env.MFA_VERIFICATION_DURATION || '', 10) || 2 * 60 * 1000, // 2 分钟
35
+ domain: process.env.MFA_AUTH_DOMAIN || '',
36
+ allowlistUsers: parseStringArray(process.env.MFA_ALLOWLIST_USERS),
37
+ enabledAuthMethods: ['qr-code'],
38
+ defaultAuthMethod: 'qr-code',
39
+ persistAuthStateDir: process.env.MFA_AUTH_STATE_DIR || '~/.openclaw/mfa-auth/',
40
+ requireAuthOnSensitiveOperation:
41
+ process.env.MFA_REQUIRE_AUTH_ON_SENSITIVE_OPERATION === undefined
42
+ ? true
43
+ : parseBooleanEnv(process.env.MFA_REQUIRE_AUTH_ON_SENSITIVE_OPERATION),
44
+ sensitiveKeywords: parseStringArray(process.env.MFA_SENSITIVE_KEYWORDS) || [
45
+ 'rm',
46
+ 'delete',
47
+ 'drop',
48
+ 'shutdown',
49
+ 'reboot',
50
+ 'format',
51
+ 'fdisk',
52
+ ],
53
+ requireAuthOnFirstMessage:
54
+ parseBooleanEnv(process.env.MFA_REQUIRE_AUTH_ON_FIRST_MESSAGE) ?? true,
55
+ firstMessageAuthDuration:
56
+ Number.parseInt(process.env.MFA_FIRST_MESSAGE_AUTH_DURATION || '', 10) || 24 * 60 * 60 * 1000, // 24 小时
57
+ gatewayHost: process.env.MFA_GATEWAY_HOST || '127.0.0.1',
58
+ enableAuthNotification: parseBooleanEnv(process.env.MFA_ENABLE_AUTH_NOTIFICATION) ?? true,
59
+ };
60
+
61
+ /**
62
+ * Dabby API 配置
63
+ */
64
+ export const dabbyConfig: DabbyConfig = {
65
+ apiKey: process.env.MFA_AUTH_API_KEY || '',
66
+ apiBaseUrl: process.env.DABBY_API_BASE_URL || '',
67
+ pollInterval: 2000, // 2 秒
68
+ };
69
+
70
+ /**
71
+ * 打印配置信息(调试模式)
72
+ */
73
+ if (config.debug) {
74
+ console.log('[cclawd-mfa-auth] Configuration loaded:');
75
+ console.log(` - requireAuthOnFirstMessage: ${config.requireAuthOnFirstMessage}`);
76
+ console.log(` - requireAuthOnSensitiveOperation: ${config.requireAuthOnSensitiveOperation}`);
77
+ console.log(` - sensitiveKeywords: ${config.sensitiveKeywords.join(', ')}`);
78
+ console.log(` - firstMessageAuthDuration: ${config.firstMessageAuthDuration}ms`);
79
+ console.log(` - verificationDuration: ${config.verificationDuration}ms`);
80
+ console.log(` - gatewayHost: ${config.gatewayHost}`);
81
+ console.log(` - enableAuthNotification: ${config.enableAuthNotification}`);
82
+ }