@insta-dev01/intclaw 1.0.11 → 1.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.
Files changed (49) hide show
  1. package/LICENSE +1 -1
  2. package/README.en.md +424 -0
  3. package/README.md +365 -164
  4. package/index.ts +28 -0
  5. package/openclaw.plugin.json +12 -41
  6. package/package.json +69 -40
  7. package/src/channel.ts +557 -0
  8. package/src/config/accounts.ts +230 -0
  9. package/src/config/schema.ts +144 -0
  10. package/src/core/connection.ts +733 -0
  11. package/src/core/message-handler.ts +1268 -0
  12. package/src/core/provider.ts +106 -0
  13. package/src/core/state.ts +54 -0
  14. package/src/directory.ts +95 -0
  15. package/src/gateway-methods.ts +237 -0
  16. package/src/onboarding.ts +387 -0
  17. package/src/policy.ts +19 -0
  18. package/src/probe.ts +213 -0
  19. package/src/reply-dispatcher.ts +674 -0
  20. package/src/runtime.ts +7 -0
  21. package/src/sdk/helpers.ts +317 -0
  22. package/src/sdk/types.ts +515 -0
  23. package/src/secret-input.ts +19 -0
  24. package/src/services/media/audio.ts +54 -0
  25. package/src/services/media/chunk-upload.ts +293 -0
  26. package/src/services/media/common.ts +154 -0
  27. package/src/services/media/file.ts +70 -0
  28. package/src/services/media/image.ts +67 -0
  29. package/src/services/media/index.ts +10 -0
  30. package/src/services/media/video.ts +162 -0
  31. package/src/services/media.ts +1134 -0
  32. package/src/services/messaging/index.ts +16 -0
  33. package/src/services/messaging/send.ts +137 -0
  34. package/src/services/messaging.ts +800 -0
  35. package/src/targets.ts +45 -0
  36. package/src/types/index.ts +52 -0
  37. package/src/utils/agent.ts +63 -0
  38. package/src/utils/async.ts +51 -0
  39. package/src/utils/constants.ts +9 -0
  40. package/src/utils/http-client.ts +84 -0
  41. package/src/utils/index.ts +8 -0
  42. package/src/utils/logger.ts +78 -0
  43. package/src/utils/session.ts +118 -0
  44. package/src/utils/token.ts +94 -0
  45. package/src/utils/utils-legacy.ts +506 -0
  46. package/.env.example +0 -11
  47. package/skills/intclaw_matrix/SKILL.md +0 -20
  48. package/src/channel/intclaw_channel.js +0 -155
  49. package/src/index.js +0 -23
@@ -0,0 +1,515 @@
1
+ /**
2
+ * IntClaw Connector SDK Types
3
+ *
4
+ * 完全独立的类型定义,不依赖任何外部 SDK。
5
+ * 这是IntClaw连接器插件的核心类型系统。
6
+ */
7
+
8
+ // ============================================================================
9
+ // 基础类型
10
+ // ============================================================================
11
+
12
+ /**
13
+ * SecretInput 支持多种输入方式
14
+ */
15
+ export type SecretInput =
16
+ | string // 直接字符串
17
+ | SecretInputRef; // 引用
18
+
19
+ /**
20
+ * SecretInput 引用类型
21
+ */
22
+ export interface SecretInputRef {
23
+ source: "env" | "file" | "exec";
24
+ provider: string;
25
+ id: string;
26
+ }
27
+
28
+ /**
29
+ * 工具权限策略
30
+ */
31
+ export interface ToolPolicy {
32
+ allow?: string[];
33
+ deny?: string[];
34
+ }
35
+
36
+ /**
37
+ * DM 策略
38
+ */
39
+ export type DmPolicy = "open" | "pairing" | "allowlist";
40
+
41
+ /**
42
+ * 群组策略
43
+ */
44
+ export type GroupPolicy = "open" | "allowlist" | "disabled";
45
+
46
+ /**
47
+ * 会话作用域
48
+ */
49
+ export type GroupSessionScope = "group" | "group_sender";
50
+
51
+ /**
52
+ * 发送模式
53
+ */
54
+ export type DeliveryMode = "direct" | "queued";
55
+
56
+ // ============================================================================
57
+ // 配置类型
58
+ // ============================================================================
59
+
60
+ /**
61
+ * IntClaw工具配置
62
+ */
63
+ export interface IntclawToolsConfig {
64
+ docs?: boolean;
65
+ media?: boolean;
66
+ }
67
+
68
+ /**
69
+ * IntClaw群组配置
70
+ */
71
+ export interface IntclawGroupConfig {
72
+ requireMention?: boolean;
73
+ tools?: ToolPolicy;
74
+ enabled?: boolean;
75
+ allowFrom?: (string | number)[];
76
+ systemPrompt?: string;
77
+ groupSessionScope?: GroupSessionScope;
78
+ }
79
+
80
+ /**
81
+ * IntClaw账号配置
82
+ */
83
+ export interface IntclawAccountConfig {
84
+ enabled?: boolean;
85
+ name?: string;
86
+ clientId?: string | number;
87
+ clientSecret?: SecretInput;
88
+ enableMediaUpload?: boolean;
89
+ systemPrompt?: string;
90
+ dmPolicy?: DmPolicy;
91
+ allowFrom?: (string | number)[];
92
+ groupPolicy?: GroupPolicy;
93
+ groupAllowFrom?: (string | number)[];
94
+ requireMention?: boolean;
95
+ groupSessionScope?: GroupSessionScope;
96
+ separateSessionByConversation?: boolean;
97
+ sharedMemoryAcrossConversations?: boolean;
98
+ historyLimit?: number;
99
+ dmHistoryLimit?: number;
100
+ textChunkLimit?: number;
101
+ mediaMaxMb?: number;
102
+ typingIndicator?: boolean;
103
+ tools?: IntclawToolsConfig;
104
+ }
105
+
106
+ /**
107
+ * IntClaw通道配置
108
+ */
109
+ export interface IntclawConfig {
110
+ enabled?: boolean;
111
+ defaultAccount?: string;
112
+ clientId?: string | number;
113
+ clientSecret?: SecretInput;
114
+ enableMediaUpload?: boolean;
115
+ systemPrompt?: string;
116
+ dmPolicy?: DmPolicy;
117
+ allowFrom?: (string | number)[];
118
+ groupPolicy?: GroupPolicy;
119
+ groupAllowFrom?: (string | number)[];
120
+ requireMention?: boolean;
121
+ groupSessionScope?: GroupSessionScope;
122
+ separateSessionByConversation?: boolean;
123
+ sharedMemoryAcrossConversations?: boolean;
124
+ historyLimit?: number;
125
+ dmHistoryLimit?: number;
126
+ textChunkLimit?: number;
127
+ mediaMaxMb?: number;
128
+ typingIndicator?: boolean;
129
+ resolveSenderNames?: boolean;
130
+ tools?: IntclawToolsConfig;
131
+ groups?: Record<string, IntclawGroupConfig>;
132
+ accounts?: Record<string, IntclawAccountConfig>;
133
+ }
134
+
135
+ /**
136
+ * 通道配置映射
137
+ */
138
+ export interface ChannelsConfig {
139
+ [key: string]: unknown;
140
+ "intclaw-connector"?: IntclawConfig;
141
+ }
142
+
143
+ /**
144
+ * 全局配置
145
+ */
146
+ export interface ClawdbotConfig {
147
+ channels?: ChannelsConfig;
148
+ [key: string]: unknown;
149
+ }
150
+
151
+ // ============================================================================
152
+ // 通道插件类型
153
+ // ============================================================================
154
+
155
+ /**
156
+ * 通道元数据
157
+ */
158
+ export interface ChannelMeta {
159
+ id: string;
160
+ label: string;
161
+ selectionLabel: string;
162
+ docsPath: string;
163
+ docsLabel: string;
164
+ blurb: string;
165
+ aliases: string[];
166
+ order: number;
167
+ }
168
+
169
+ /**
170
+ * 通道能力
171
+ */
172
+ export interface ChannelCapabilities {
173
+ chatTypes: ("direct" | "group")[];
174
+ polls: boolean;
175
+ threads: boolean;
176
+ media: boolean;
177
+ reactions: boolean;
178
+ edit: boolean;
179
+ reply: boolean;
180
+ nativeCommands?: boolean;
181
+ }
182
+
183
+ /**
184
+ * 配对功能
185
+ */
186
+ export interface ChannelPairing {
187
+ idLabel: string;
188
+ normalizeAllowEntry: (entry: string) => string;
189
+ notifyApproval: (params: { cfg: ClawdbotConfig; id: string }) => Promise<void>;
190
+ }
191
+
192
+ /**
193
+ * Agent 提示
194
+ */
195
+ export interface ChannelAgentPrompt {
196
+ messageToolHints: () => string[];
197
+ }
198
+
199
+ /**
200
+ * 群组功能
201
+ */
202
+ export interface ChannelGroups {
203
+ resolveToolPolicy: (params: { account: unknown; groupId: string }) => ToolPolicy | undefined;
204
+ }
205
+
206
+ /**
207
+ * 提及功能
208
+ */
209
+ export interface ChannelMentions {
210
+ stripPatterns: () => RegExp[];
211
+ }
212
+
213
+ /**
214
+ * 重载配置
215
+ */
216
+ export interface ChannelReload {
217
+ configPrefixes: string[];
218
+ }
219
+
220
+ /**
221
+ * 配置 Schema
222
+ */
223
+ export interface ChannelConfigSchema {
224
+ schema: unknown;
225
+ }
226
+
227
+ /**
228
+ * 配置管理
229
+ */
230
+ export interface ChannelConfig<TAccount> {
231
+ listAccountIds: (cfg: ClawdbotConfig) => string[];
232
+ resolveAccount: (cfg: ClawdbotConfig, accountId: string) => TAccount;
233
+ defaultAccountId: (cfg: ClawdbotConfig) => string;
234
+ setAccountEnabled: (params: { cfg: ClawdbotConfig; accountId: string; enabled: boolean }) => ClawdbotConfig;
235
+ deleteAccount: (params: { cfg: ClawdbotConfig; accountId: string }) => ClawdbotConfig;
236
+ isConfigured: (account: TAccount) => boolean;
237
+ describeAccount: (account: TAccount) => unknown;
238
+ resolveAllowFrom: (params: { cfg: ClawdbotConfig; accountId: string }) => string[];
239
+ formatAllowFrom: (params: { allowFrom: (string | number)[] }) => string[];
240
+ }
241
+
242
+ /**
243
+ * 安全功能
244
+ */
245
+ export interface ChannelSecurity<TAccount> {
246
+ collectWarnings: (params: { cfg: ClawdbotConfig; accountId: string }) => string[];
247
+ }
248
+
249
+ /**
250
+ * 设置功能
251
+ */
252
+ export interface ChannelSetup {
253
+ resolveAccountId: () => string;
254
+ applyAccountConfig: (params: { cfg: ClawdbotConfig; accountId: string }) => ClawdbotConfig;
255
+ }
256
+
257
+ /**
258
+ * 入引导适配器
259
+ */
260
+ export interface ChannelOnboardingAdapter {
261
+ channel: string;
262
+ getStatus: (params: { cfg: ClawdbotConfig }) => Promise<{
263
+ channel: string;
264
+ configured: boolean;
265
+ statusLines: string[];
266
+ selectionHint: string;
267
+ quickstartScore: number;
268
+ }>;
269
+ configure: (params: { cfg: ClawdbotConfig; prompter: unknown }) => Promise<{
270
+ cfg: ClawdbotConfig;
271
+ accountId: string;
272
+ }>;
273
+ dmPolicy?: {
274
+ label: string;
275
+ channel: string;
276
+ policyKey: string;
277
+ allowFromKey: string;
278
+ getCurrent: (cfg: ClawdbotConfig) => string;
279
+ setPolicy: (cfg: ClawdbotConfig, policy: string) => ClawdbotConfig;
280
+ promptAllowFrom: (params: { cfg: ClawdbotConfig; prompter: unknown }) => Promise<ClawdbotConfig>;
281
+ };
282
+ disable: (cfg: ClawdbotConfig) => ClawdbotConfig;
283
+ }
284
+
285
+ /**
286
+ * 消息功能
287
+ */
288
+ export interface ChannelMessaging {
289
+ normalizeTarget: (raw: string) => string | undefined;
290
+ targetResolver: {
291
+ looksLikeId: (raw: string) => boolean;
292
+ hint: string;
293
+ };
294
+ }
295
+
296
+ /**
297
+ * 目录功能
298
+ */
299
+ export interface ChannelDirectory {
300
+ self: () => Promise<unknown>;
301
+ listPeers: (params: { cfg: ClawdbotConfig; query?: string; limit?: number; accountId?: string }) => Promise<unknown[]>;
302
+ listGroups: (params: { cfg: ClawdbotConfig; query?: string; limit?: number; accountId?: string }) => Promise<unknown[]>;
303
+ listPeersLive: (params: { cfg: ClawdbotConfig; query?: string; limit?: number; accountId?: string }) => Promise<unknown[]>;
304
+ listGroupsLive: (params: { cfg: ClawdbotConfig; query?: string; limit?: number; accountId?: string }) => Promise<unknown[]>;
305
+ }
306
+
307
+ /**
308
+ * 发送结果
309
+ */
310
+ export interface SendResult {
311
+ channel: string;
312
+ messageId: string;
313
+ conversationId: string;
314
+ }
315
+
316
+ /**
317
+ * 出站消息功能
318
+ */
319
+ export interface ChannelOutbound {
320
+ deliveryMode: DeliveryMode;
321
+ chunker: (text: string, limit: number) => string[];
322
+ chunkerMode: "markdown" | "text";
323
+ textChunkLimit: number;
324
+ sendText: (params: {
325
+ cfg: ClawdbotConfig;
326
+ to: string;
327
+ text: string;
328
+ accountId?: string;
329
+ replyToId?: string;
330
+ threadId?: string;
331
+ }) => Promise<SendResult>;
332
+ sendMedia: (params: {
333
+ cfg: ClawdbotConfig;
334
+ to: string;
335
+ text?: string;
336
+ mediaUrl?: string;
337
+ accountId?: string;
338
+ mediaLocalRoots?: string[];
339
+ replyToId?: string;
340
+ threadId?: string;
341
+ }) => Promise<SendResult>;
342
+ }
343
+
344
+ /**
345
+ * 探测结果
346
+ */
347
+ export interface BaseProbeResult<T> {
348
+ ok: boolean;
349
+ error?: string;
350
+ data?: T;
351
+ }
352
+
353
+ /**
354
+ * 运行时状态
355
+ */
356
+ export interface ChannelRuntimeState {
357
+ running: boolean;
358
+ lastStartAt: string | null;
359
+ lastStopAt: string | null;
360
+ lastError: string | null;
361
+ port: number | null;
362
+ [key: string]: unknown;
363
+ }
364
+
365
+ /**
366
+ * 状态功能
367
+ */
368
+ export interface ChannelStatus<TAccount> {
369
+ defaultRuntime: ChannelRuntimeState;
370
+ buildChannelSummary: (params: { snapshot: unknown }) => unknown;
371
+ probeAccount: (params: { account: TAccount }) => Promise<BaseProbeResult<unknown>>;
372
+ buildAccountSnapshot: (params: { account: TAccount; runtime?: ChannelRuntimeState; probe?: BaseProbeResult<unknown> }) => unknown;
373
+ }
374
+
375
+ /**
376
+ * 网关启动上下文
377
+ */
378
+ export interface GatewayStartContext {
379
+ cfg: ClawdbotConfig;
380
+ accountId: string;
381
+ runtime: ChannelRuntimeState;
382
+ abortSignal: AbortSignal;
383
+ setStatus: (params: { accountId: string; port: number | null }) => void;
384
+ log?: {
385
+ info: (message: string) => void;
386
+ error: (message: string) => void;
387
+ warn: (message: string) => void;
388
+ };
389
+ }
390
+
391
+ /**
392
+ * 网关功能
393
+ */
394
+ export interface ChannelGateway {
395
+ startAccount: (ctx: GatewayStartContext) => Promise<void>;
396
+ }
397
+
398
+ /**
399
+ * 通道插件接口
400
+ */
401
+ export interface ChannelPlugin<TAccount> {
402
+ id: string;
403
+ meta: ChannelMeta;
404
+ pairing?: ChannelPairing;
405
+ capabilities?: ChannelCapabilities;
406
+ agentPrompt?: ChannelAgentPrompt;
407
+ groups?: ChannelGroups;
408
+ mentions?: ChannelMentions;
409
+ reload?: ChannelReload;
410
+ configSchema?: ChannelConfigSchema;
411
+ config: ChannelConfig<TAccount>;
412
+ security?: ChannelSecurity<TAccount>;
413
+ setup?: ChannelSetup;
414
+ onboarding?: ChannelOnboardingAdapter;
415
+ messaging?: ChannelMessaging;
416
+ directory?: ChannelDirectory;
417
+ outbound?: ChannelOutbound;
418
+ status?: ChannelStatus<TAccount>;
419
+ gateway?: ChannelGateway;
420
+ streaming?: unknown;
421
+ actions?: unknown;
422
+ resolver?: unknown;
423
+ threading?: unknown;
424
+ }
425
+
426
+ /**
427
+ * 插件 API
428
+ */
429
+ export interface PluginApi {
430
+ registerChannel: (opts: { plugin: ChannelPlugin<unknown> }) => void;
431
+ runtime: {
432
+ env: Record<string, string | undefined>;
433
+ [key: string]: unknown;
434
+ };
435
+ [key: string]: unknown;
436
+ }
437
+
438
+ // ============================================================================
439
+ // 常量
440
+ // ============================================================================
441
+
442
+ /**
443
+ * 默认账号 ID
444
+ */
445
+ export const DEFAULT_ACCOUNT_ID = "default" as const;
446
+
447
+ // ============================================================================
448
+ // 运行时类型
449
+ // ============================================================================
450
+
451
+ /**
452
+ * 运行时环境
453
+ */
454
+ export interface RuntimeEnv {
455
+ env: Record<string, string | undefined>;
456
+ [key: string]: unknown;
457
+ }
458
+
459
+ /**
460
+ * 历史记录条目
461
+ */
462
+ export interface HistoryEntry {
463
+ role: "user" | "assistant" | "system";
464
+ content: string;
465
+ timestamp?: string;
466
+ [key: string]: unknown;
467
+ }
468
+
469
+ /**
470
+ * 插件运行时
471
+ */
472
+ export interface PluginRuntime {
473
+ env: RuntimeEnv;
474
+ [key: string]: unknown;
475
+ }
476
+
477
+ // ============================================================================
478
+ // 向导类型
479
+ // ============================================================================
480
+
481
+ /**
482
+ * 向导提示器
483
+ */
484
+ export interface WizardPrompter {
485
+ note: (message: string, title?: string) => Promise<void>;
486
+ text: (params: {
487
+ message: string;
488
+ placeholder?: string;
489
+ initialValue?: string;
490
+ validate?: (value: unknown) => string | undefined;
491
+ }) => Promise<string>;
492
+ select: <T>(params: {
493
+ message: string;
494
+ options: Array<{ value: T; label: string }>;
495
+ initialValue?: T;
496
+ }) => Promise<T>;
497
+ confirm: (params: {
498
+ message: string;
499
+ initialValue?: boolean;
500
+ }) => Promise<boolean>;
501
+ [key: string]: unknown;
502
+ }
503
+
504
+ /**
505
+ * DM 策略入引导
506
+ */
507
+ export interface ChannelOnboardingDmPolicy {
508
+ label: string;
509
+ channel: string;
510
+ policyKey: string;
511
+ allowFromKey: string;
512
+ getCurrent: (cfg: ClawdbotConfig) => string;
513
+ setPolicy: (cfg: ClawdbotConfig, policy: string) => ClawdbotConfig;
514
+ promptAllowFrom: (params: { cfg: ClawdbotConfig; prompter: WizardPrompter }) => Promise<ClawdbotConfig>;
515
+ }
@@ -0,0 +1,19 @@
1
+ import {
2
+ hasConfiguredSecretInput,
3
+ normalizeResolvedSecretInputString,
4
+ normalizeSecretInputString,
5
+ } from "./sdk/helpers.ts";
6
+ import { z } from "zod";
7
+
8
+ export { hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString };
9
+
10
+ export function buildSecretInputSchema() {
11
+ return z.union([
12
+ z.string(),
13
+ z.object({
14
+ source: z.enum(["env", "file", "exec"]),
15
+ provider: z.string().min(1),
16
+ id: z.string().min(1),
17
+ }),
18
+ ]);
19
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * 音频处理模块
3
+ * 支持音频消息发送
4
+ */
5
+
6
+ import type { IntclawConfig } from '../../types/index.ts';
7
+ import { AUDIO_MARKER_PATTERN, toLocalPath, uploadMediaToIntClaw } from './common.ts';
8
+ import * as fs from 'fs';
9
+
10
+ /**
11
+ * 提取音频标记并发送音频消息
12
+ */
13
+ export async function processAudioMarkers(
14
+ content: string,
15
+ sessionWebhook: string,
16
+ config: IntclawConfig,
17
+ oapiToken: string | null,
18
+ log?: any,
19
+ useProactiveApi: boolean = false,
20
+ target?: any,
21
+ ): Promise<string> {
22
+ const logPrefix = useProactiveApi ? '[IntClaw][Audio][Proactive]' : '[IntClaw][Audio]';
23
+
24
+ if (!oapiToken) {
25
+ log?.warn?.(`${logPrefix} 无 oapiToken,跳过音频处理`);
26
+ return content;
27
+ }
28
+
29
+ const matches = [...content.matchAll(AUDIO_MARKER_PATTERN)];
30
+ if (matches.length === 0) return content;
31
+
32
+ log?.info?.(`${logPrefix} 检测到 ${matches.length} 个音频,开始上传...`);
33
+
34
+ let result = content;
35
+ for (const match of matches) {
36
+ const full = match[0];
37
+ try {
38
+ const audioData = JSON.parse(match[1]);
39
+ const absPath = toLocalPath(audioData.path);
40
+ if (!fs.existsSync(absPath)) {
41
+ log?.warn?.(`${logPrefix} 音频文件不存在:${absPath}`);
42
+ result = result.replace(full, '⚠️ 音频文件不存在');
43
+ continue;
44
+ }
45
+ const uploadResult = await uploadMediaToIntClaw(absPath, 'voice', oapiToken, 20 * 1024 * 1024, log);
46
+ result = result.replace(full, uploadResult ? `[音频已上传:${uploadResult.downloadUrl}]` : '⚠️ 音频上传失败');
47
+ } catch {
48
+ log?.warn?.(`${logPrefix} 解析音频标记失败:${match[1]}`);
49
+ result = result.replace(full, '');
50
+ }
51
+ }
52
+
53
+ return result.trim();
54
+ }