koishi-plugin-openai-compatible 1.0.5 → 1.0.6

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/lib/types.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ export interface Message {
2
+ role: 'system' | 'user' | 'assistant';
3
+ content: string;
4
+ }
5
+ export interface ChatCompletionRequest {
6
+ model: string;
7
+ messages: Message[];
8
+ temperature?: number;
9
+ max_tokens?: number;
10
+ top_p?: number;
11
+ frequency_penalty?: number;
12
+ presence_penalty?: number;
13
+ }
14
+ export interface ChatCompletionResponse {
15
+ id: string;
16
+ object: string;
17
+ created: number;
18
+ model: string;
19
+ choices: Array<{
20
+ index: number;
21
+ message: {
22
+ role: string;
23
+ content: string;
24
+ };
25
+ finish_reason: string;
26
+ }>;
27
+ usage?: {
28
+ prompt_tokens: number;
29
+ completion_tokens: number;
30
+ total_tokens: number;
31
+ };
32
+ }
package/lib/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-openai-compatible",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "这是一个适用于Koishi的OpenAI兼容聊天插件,支持与所有兼容OpenAI API的大模型进行聊天交互。",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/api.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type { ChatCompletionRequest, ChatCompletionResponse } from './types';
2
+
3
+ export class OpenAICompatibleAPI {
4
+ private endpoint: string;
5
+ private apiKey: string;
6
+
7
+ constructor(endpoint: string, apiKey: string) {
8
+ this.endpoint = endpoint;
9
+ this.apiKey = apiKey;
10
+ }
11
+
12
+ async chatCompletion(request: ChatCompletionRequest): Promise<string> {
13
+ const headers: Record<string, string> = {
14
+ 'Content-Type': 'application/json',
15
+ };
16
+
17
+ if (this.apiKey) {
18
+ headers['Authorization'] = `Bearer ${this.apiKey}`;
19
+ }
20
+
21
+ const response = await fetch(this.endpoint, {
22
+ method: 'POST',
23
+ headers,
24
+ body: JSON.stringify(request),
25
+ });
26
+
27
+ if (!response.ok) {
28
+ const errorText = await response.text();
29
+ throw new Error(`API 请求失败: ${response.status} ${response.statusText}\n${errorText}`);
30
+ }
31
+
32
+ const data: ChatCompletionResponse = await response.json();
33
+
34
+ if (!data.choices || data.choices.length === 0) {
35
+ throw new Error('API 返回了空的选择列表');
36
+ }
37
+
38
+ return data.choices[0].message.content;
39
+ }
40
+ }
package/src/config.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { Schema } from 'koishi';
2
+
3
+ export interface Config {
4
+ // API 配置
5
+ endpoint: string;
6
+ apiKey: string;
7
+ model: string;
8
+
9
+ // 情绪分析配置
10
+ enableEmotion: boolean;
11
+ emotionEndpoint: string;
12
+ emotionApiKey: string;
13
+ emotionModel: string;
14
+ emotionImages: Record<string, string>;
15
+
16
+ // 提示词配置
17
+ systemPrompt: string;
18
+ emotionSystemPrompt: string;
19
+
20
+ // 参数配置
21
+ temperature: number;
22
+ maxTokens: number;
23
+ topP: number;
24
+ frequencyPenalty: number;
25
+ presencePenalty: number;
26
+
27
+ // 黑名单配置
28
+ blacklist: string[];
29
+
30
+ // 冷却配置
31
+ cooldown: number;
32
+
33
+ // 命令名配置
34
+ commandName: string;
35
+
36
+ // 提示配置
37
+ enableMessage: boolean;
38
+ }
39
+
40
+ export const Config: Schema<Config> = Schema.intersect([
41
+ Schema.object({
42
+ endpoint: Schema.string().default('https://api.openai.com/v1/chat/completions'),
43
+ apiKey: Schema.string().default('').description('OpenAI API Key'),
44
+ model: Schema.string().default('gpt-3.5-turbo'),
45
+ }).description('API 配置'),
46
+ Schema.object({
47
+ enableEmotion: Schema.boolean().default(false).description('是否启用情绪分析'),
48
+ emotionEndpoint: Schema.string().default('https://api.openai.com/v1/chat/completions'),
49
+ emotionApiKey: Schema.string().default('').description('情绪分析 API Key'),
50
+ emotionModel: Schema.string().default('gpt-3.5-turbo').description('推荐使用较快的模型'),
51
+ emotionImages: Schema.dict(
52
+ Schema.string().description('情绪表情包图片链接')
53
+ ).default({
54
+ '快乐': 'https://example.com/happy.gif',
55
+ '开心': 'https://example.com/happy.gif',
56
+ '高兴': 'https://example.com/happy.gif',
57
+ '伤心': 'https://example.com/sad.gif',
58
+ '难过': 'https://example.com/sad.gif',
59
+ '悲伤': 'https://example.com/sad.gif',
60
+ '生气': 'https://example.com/angry.gif',
61
+ '愤怒': 'https://example.com/angry.gif',
62
+ '惊讶': 'https://example.com/surprised.gif',
63
+ '疑惑': 'https://example.com/confused.gif',
64
+ '害怕': 'https://example.com/afraid.gif',
65
+ '恐惧': 'https://example.com/afraid.gif',
66
+ '平静': 'https://example.com/calm.gif',
67
+ '中立': 'https://example.com/neutral.gif',
68
+ }).description('情绪名称对应的表情包图片链接'),
69
+ }).description('情绪分析配置'),
70
+ Schema.object({
71
+ systemPrompt: Schema.string()
72
+ .default('你是一个友好的 AI 助手,请用简洁、准确的语言回答用户的问题。')
73
+ .description('系统提示词'),
74
+ emotionSystemPrompt: Schema.string()
75
+ .default('你是一个情绪分析助手。分析给定的文本内容,并判断其中表达的情绪。\n\n情绪类型包括:快乐、开心、高兴、伤心、难过、悲伤、生气、愤怒、惊讶、疑惑、害怕、恐惧、平静、中立。\n\n请直接输出一个情绪名称,不要输出任何其他内容,不要解释,不要添加标点符号。\n\n例如:\n- "今天天气真好,我很开心!" -> 快乐\n- "这次考试失败了,我好难过。" -> 伤心\n- "什么?这太不可思议了!" -> 惊讶\n- "我不知道该怎么办。" -> 疑惑\n- "这真是太可怕了!" -> 害怕\n- "一切都很平静。" -> 平静')
76
+ .description('情绪分析系统提示词'),
77
+ }).description('提示词配置'),
78
+ Schema.object({
79
+ temperature: Schema.number().min(0).max(2).default(0.7).description('温度参数'),
80
+ maxTokens: Schema.number().min(1).default(2000).description('最大令牌数'),
81
+ topP: Schema.number().min(0).max(1).default(1).description('Top P 参数'),
82
+ frequencyPenalty: Schema.number().min(-2).max(2).default(0).description('频率惩罚'),
83
+ presencePenalty: Schema.number().min(-2).max(2).default(0).description('存在惩罚'),
84
+ }).description('参数配置'),
85
+ Schema.object({
86
+ blacklist: Schema.array(Schema.string()).default([]).description('黑名单用户 ID'),
87
+ cooldown: Schema.number().min(0).default(10000).description('请求冷却时间(毫秒)'),
88
+ commandName: Schema.string().default('ai').description('自定义指令名称'),
89
+ enableMessage: Schema.boolean().default(true).description('是否开启提示(关闭后所有错误提示都不会输出)'),
90
+ }).description('其他配置'),
91
+ ])
package/src/emotion.ts ADDED
@@ -0,0 +1,37 @@
1
+ import type { OpenAICompatibleAPI } from './api';
2
+ import type { Config } from './config';
3
+ import type { Message, ChatCompletionRequest } from './types';
4
+
5
+ export class EmotionAnalyzer {
6
+ private api: OpenAICompatibleAPI;
7
+ private config: Config;
8
+
9
+ constructor(api: OpenAICompatibleAPI, config: Config) {
10
+ this.api = api;
11
+ this.config = config;
12
+ }
13
+
14
+ async analyzeEmotion(text: string): Promise<string> {
15
+ const request: ChatCompletionRequest = {
16
+ model: this.config.emotionModel,
17
+ messages: [
18
+ { role: 'system', content: this.config.emotionSystemPrompt } as Message,
19
+ { role: 'user', content: text } as Message,
20
+ ],
21
+ temperature: 0.3,
22
+ max_tokens: 50,
23
+ };
24
+
25
+ try {
26
+ const emotion = await this.api.chatCompletion(request);
27
+ return emotion.trim();
28
+ } catch (error) {
29
+ console.error('情绪分析失败:', error);
30
+ return '';
31
+ }
32
+ }
33
+
34
+ getEmotionImage(emotion: string): string | null {
35
+ return this.config.emotionImages[emotion] || null;
36
+ }
37
+ }
package/src/index.ts CHANGED
@@ -1,113 +1,171 @@
1
- import { Context, Schema } from 'koishi'
2
-
3
- export const name = 'openai-compatible'
4
-
5
- export interface Config {
6
- apiEndpoint: string
7
- apiKey: string
8
- model: string
9
- temperature?: number
10
- maxTokens?: number
11
- systemPrompt?: string
12
- commandName?: string
13
- triggerPrefix?: string
14
- userPrompts?: Record<string, string>
15
- thinkingPrompt?: string
16
- blacklist?: string[]
17
- blacklistResponse?: string
18
- }
1
+ import { Context, Schema } from 'koishi';
2
+ import type { Config } from './config';
3
+ import { OpenAICompatibleAPI } from './api';
4
+ import { EmotionAnalyzer } from './emotion';
5
+ import { Storage } from './storage';
6
+ import type { Message } from './types';
7
+
8
+ export const name = 'openai-compatible';
19
9
 
20
- export const Config: Schema<Config> = Schema.object({
21
- apiEndpoint: Schema.string().required().description('API endpoint URL, e.g., "https://api.openai.com/v1"'),
22
- apiKey: Schema.string().required().description('API key'),
23
- model: Schema.string().required().description('Model name, e.g., "gpt-3.5-turbo"'),
24
- temperature: Schema.number().min(0).max(2).default(1).description('Controls randomness of responses (0-2)'),
25
- maxTokens: Schema.number().min(1).default(2048).description('Maximum number of tokens to generate'),
26
- systemPrompt: Schema.string().description('System prompt (optional)'),
27
- commandName: Schema.string().description('Custom command name (optional)'),
28
- triggerPrefix: Schema.string().description('Trigger prefix (optional, used when no commandName is set)'),
29
- userPrompts: Schema.dict(Schema.string()).description('User-specific prompts (optional)'),
30
- thinkingPrompt: Schema.string().description('Thinking prompt (optional)'),
31
- blacklist: Schema.array(Schema.string()).description('List of blacklisted user IDs (optional)'),
32
- blacklistResponse: Schema.string().description('Response for blacklisted users (text or image URL)'),
33
- })
10
+ export const inject = {
11
+ required: [],
12
+ optional: [],
13
+ };
34
14
 
35
15
  export function apply(ctx: Context, config: Config) {
36
- const { apiEndpoint, apiKey, model, temperature, maxTokens, systemPrompt, commandName, triggerPrefix, userPrompts, thinkingPrompt, blacklist, blacklistResponse } = config
37
-
38
- // Register command if commandName is provided
39
- if (commandName) {
40
- ctx.command(commandName, 'Chat with OpenAI-compatible API')
41
- .action(async ({ session }, ...args) => {
42
- if (!session || !session.userId || blacklist?.includes(session.userId)) {
43
- return blacklistResponse || 'You are not allowed to use this command.'
44
- }
45
- const userPrompt = !session || !session.userId ? '' : userPrompts?.[session.userId] || ''
46
- const prompt = args.join(' ')
47
- const fullPrompt = systemPrompt ? `${systemPrompt}\n${userPrompt}\n${prompt}` : `${userPrompt}\n${prompt}`
16
+ // 导出配置 Schema
17
+ ctx.i18n.define('zh', require('./locales/zh'));
48
18
 
49
- if (thinkingPrompt) {
50
- if (session) session.send(thinkingPrompt)
51
- }
19
+ // 初始化存储
20
+ const storage = new Storage();
21
+
22
+ // 初始化 API 客户端
23
+ const api = new OpenAICompatibleAPI(config.endpoint, config.apiKey);
24
+
25
+ // 初始化情绪分析器
26
+ let emotionAnalyzer: EmotionAnalyzer | null = null;
27
+ if (config.enableEmotion) {
28
+ const emotionApi = new OpenAICompatibleAPI(config.emotionEndpoint, config.emotionApiKey);
29
+ emotionAnalyzer = new EmotionAnalyzer(emotionApi, config);
30
+ }
31
+
32
+ // 注册命令
33
+ ctx.command(config.commandName, { authority: 1 })
34
+ .alias('chat')
35
+ .action(async ({ session }, content) => {
36
+ if (!session) return;
52
37
 
53
- try {
54
- const response = await ctx.http.post(`${apiEndpoint}/chat/completions`, {
55
- model,
56
- messages: [
57
- { role: 'user', content: fullPrompt },
58
- ],
59
- temperature,
60
- max_tokens: maxTokens,
61
- }, {
62
- headers: {
63
- 'Authorization': `Bearer ${apiKey}`,
64
- 'Content-Type': 'application/json',
65
- },
66
- })
67
-
68
- return response.choices[0].message.content
69
- } catch (error) {
70
- ctx.logger.error('OpenAI API error:', error)
71
- return 'Failed to get response from the API.'
38
+ const userId = session.userId;
39
+ if (!userId) return;
40
+
41
+ // 检查黑名单
42
+ if (config.blacklist.includes(userId)) {
43
+ if (config.enableMessage) {
44
+ await session.send('您在黑名单中,无法使用此功能。');
72
45
  }
73
- })
74
- } else {
75
- // Handle all messages if no commandName is provided
76
- ctx.middleware(async (session, next) => {
77
- if (!session || !session.userId || blacklist?.includes(session.userId)) {
78
- return blacklistResponse || 'You are not allowed to use this command.'
46
+ return;
79
47
  }
80
- const content = session.content
81
- if (!content || (triggerPrefix && !content.startsWith(triggerPrefix))) return next()
82
48
 
83
- const userPrompt = !session || !session.userId ? '' : userPrompts?.[session.userId] || ''
84
- const prompt = !content ? '' : triggerPrefix ? content.slice(triggerPrefix.length) : content
85
- const fullPrompt = systemPrompt ? `${systemPrompt}\n${userPrompt}\n${prompt}` : `${userPrompt}\n${prompt}`
49
+ // 检查冷却时间
50
+ if (storage.isOnCooldown(userId, config.cooldown)) {
51
+ const data = storage.getCooldown(userId)!;
52
+ const remaining = config.cooldown - (Date.now() - data.lastRequestTime);
53
+ const remainingSeconds = Math.ceil(remaining / 1000);
54
+ if (config.enableMessage) {
55
+ await session.send(`请求过于频繁,请等待 ${remainingSeconds} 秒后再试。`);
56
+ }
57
+ return;
58
+ }
86
59
 
87
- if (thinkingPrompt) {
88
- if (session) session.send(thinkingPrompt)
60
+ // 获取用户输入
61
+ if (!content || content.trim() === '') {
62
+ if (config.enableMessage) {
63
+ await session.send('请输入要聊天的内容。');
64
+ }
65
+ return;
89
66
  }
90
67
 
91
68
  try {
92
- const response = await ctx.http.post(`${apiEndpoint}/chat/completions`, {
93
- model,
94
- messages: [
95
- { role: 'user', content: fullPrompt },
96
- ],
97
- temperature,
98
- max_tokens: maxTokens,
99
- }, {
100
- headers: {
101
- 'Authorization': `Bearer ${apiKey}`,
102
- 'Content-Type': 'application/json',
103
- },
104
- })
105
-
106
- return response.choices[0].message.content
69
+ // 构建消息列表
70
+ const messages: Message[] = [
71
+ { role: 'system', content: config.systemPrompt },
72
+ { role: 'user', content: content },
73
+ ];
74
+
75
+ // 调用 AI
76
+ const request = {
77
+ model: config.model,
78
+ messages,
79
+ temperature: config.temperature,
80
+ max_tokens: config.maxTokens,
81
+ top_p: config.topP,
82
+ frequency_penalty: config.frequencyPenalty,
83
+ presence_penalty: config.presencePenalty,
84
+ };
85
+
86
+ const response = await api.chatCompletion(request);
87
+ let finalResponse = response;
88
+
89
+ // 如果启用了情绪分析,则分析 AI 输出的内容
90
+ if (config.enableEmotion && emotionAnalyzer) {
91
+ const emotion = await emotionAnalyzer.analyzeEmotion(response);
92
+ const emotionImage = emotionAnalyzer.getEmotionImage(emotion);
93
+
94
+ if (emotion && emotionImage) {
95
+ finalResponse = `${response}\n\n${emotionImage}`;
96
+ }
97
+ }
98
+
99
+ // 发送回复
100
+ await session.send(finalResponse);
101
+
102
+ // 更新冷却时间
103
+ storage.setCooldown(userId, { lastRequestTime: Date.now() });
104
+
107
105
  } catch (error) {
108
- ctx.logger.error('OpenAI API error:', error)
109
- return 'Failed to get response from the API.'
106
+ console.error('AI 请求失败:', error);
107
+ if (config.enableMessage) {
108
+ await session.send(`请求失败: ${error instanceof Error ? error.message : '未知错误'}`);
109
+ }
110
110
  }
111
- })
112
- }
113
- }
111
+ });
112
+
113
+ // 黑名单管理命令
114
+ ctx.command(`${config.commandName}.admin`)
115
+ .subcommand('.add <userId:string>', '添加黑名单')
116
+ .action(async ({ session }, userId) => {
117
+ if (!session) return;
118
+ config.blacklist.push(userId);
119
+ await session.send(`已将用户 ${userId} 添加到黑名单。`);
120
+ });
121
+
122
+ ctx.command(`${config.commandName}.admin`)
123
+ .subcommand('.remove <userId:string>', '移除黑名单')
124
+ .action(async ({ session }, userId) => {
125
+ if (!session) return;
126
+ const index = config.blacklist.indexOf(userId);
127
+ if (index > -1) {
128
+ config.blacklist.splice(index, 1);
129
+ await session.send(`已将用户 ${userId} 从黑名单中移除。`);
130
+ } else {
131
+ await session.send(`用户 ${userId} 不在黑名单中。`);
132
+ }
133
+ });
134
+
135
+ ctx.command(`${config.commandName}.admin`)
136
+ .subcommand('.list', '查看黑名单')
137
+ .action(async ({ session }) => {
138
+ if (!session) return;
139
+ if (config.blacklist.length === 0) {
140
+ await session.send('黑名单为空。');
141
+ } else {
142
+ await session.send(`黑名单列表:\n${config.blacklist.join('\n')}`);
143
+ }
144
+ });
145
+
146
+ // 冷却时间管理命令
147
+ ctx.command(`${config.commandName}.admin`)
148
+ .subcommand('.clearcooldown <userId:string>', '清除用户冷却')
149
+ .action(async ({ session }, userId) => {
150
+ if (!session) return;
151
+ storage.deleteCooldown(userId);
152
+ await session.send(`已清除用户 ${userId} 的冷却时间。`);
153
+ });
154
+
155
+ // 配置信息查看命令
156
+ ctx.command(`${config.commandName}.admin`)
157
+ .subcommand('.status', '查看插件状态')
158
+ .action(async ({ session }) => {
159
+ if (!session) return;
160
+ const status = [
161
+ `模型: ${config.model}`,
162
+ `端点: ${config.endpoint}`,
163
+ `情绪分析: ${config.enableEmotion ? '已启用' : '未启用'}`,
164
+ `冷却时间: ${config.cooldown / 1000} 秒`,
165
+ `黑名单数量: ${config.blacklist.length}`,
166
+ ];
167
+ await session.send(`插件状态:\n${status.join('\n')}`);
168
+ });
169
+
170
+ ctx.logger.info('koishi-plugin-openai-compatible 插件已加载');
171
+ }
@@ -0,0 +1,43 @@
1
+ export default {
2
+ commands: {
3
+ ai: {
4
+ name: 'AI 对话',
5
+ description: '与 AI 进行对话',
6
+ input: '请输入要聊天的内容。',
7
+ cooldown: '请求过于频繁,请等待 {0} 秒后再试。',
8
+ blacklist: '您在黑名单中,无法使用此功能。',
9
+ error: '请求失败: {0}',
10
+ },
11
+ admin: {
12
+ add: {
13
+ description: '添加黑名单',
14
+ success: '已将用户 {0} 添加到黑名单。',
15
+ },
16
+ remove: {
17
+ description: '移除黑名单',
18
+ success: '已将用户 {0} 从黑名单中移除。',
19
+ notFound: '用户 {0} 不在黑名单中。',
20
+ },
21
+ list: {
22
+ description: '查看黑名单',
23
+ empty: '黑名单为空。',
24
+ title: '黑名单列表:',
25
+ },
26
+ clearcooldown: {
27
+ description: '清除用户冷却',
28
+ success: '已清除用户 {0} 的冷却时间。',
29
+ },
30
+ status: {
31
+ description: '查看插件状态',
32
+ title: '插件状态:',
33
+ model: '模型: {0}',
34
+ endpoint: '端点: {0}',
35
+ emotion: '情绪分析: {0}',
36
+ enabled: '已启用',
37
+ disabled: '未启用',
38
+ cooldown: '冷却时间: {0} 秒',
39
+ blacklistCount: '黑名单数量: {0}',
40
+ },
41
+ },
42
+ },
43
+ };
package/src/storage.ts ADDED
@@ -0,0 +1,27 @@
1
+ export interface CooldownData {
2
+ lastRequestTime: number;
3
+ }
4
+
5
+ export class Storage {
6
+ private cooldowns = new Map<string, CooldownData>();
7
+
8
+ getCooldown(userId: string): CooldownData | undefined {
9
+ return this.cooldowns.get(userId);
10
+ }
11
+
12
+ setCooldown(userId: string, data: CooldownData): void {
13
+ this.cooldowns.set(userId, data);
14
+ }
15
+
16
+ deleteCooldown(userId: string): void {
17
+ this.cooldowns.delete(userId);
18
+ }
19
+
20
+ isOnCooldown(userId: string, cooldownTime: number): boolean {
21
+ const data = this.getCooldown(userId);
22
+ if (!data) return false;
23
+
24
+ const elapsed = Date.now() - data.lastRequestTime;
25
+ return elapsed < cooldownTime;
26
+ }
27
+ }
package/src/types.ts ADDED
@@ -0,0 +1,34 @@
1
+ export interface Message {
2
+ role: 'system' | 'user' | 'assistant';
3
+ content: string;
4
+ }
5
+
6
+ export interface ChatCompletionRequest {
7
+ model: string;
8
+ messages: Message[];
9
+ temperature?: number;
10
+ max_tokens?: number;
11
+ top_p?: number;
12
+ frequency_penalty?: number;
13
+ presence_penalty?: number;
14
+ }
15
+
16
+ export interface ChatCompletionResponse {
17
+ id: string;
18
+ object: string;
19
+ created: number;
20
+ model: string;
21
+ choices: Array<{
22
+ index: number;
23
+ message: {
24
+ role: string;
25
+ content: string;
26
+ };
27
+ finish_reason: string;
28
+ }>;
29
+ usage?: {
30
+ prompt_tokens: number;
31
+ completion_tokens: number;
32
+ total_tokens: number;
33
+ };
34
+ }
package/tsconfig.json CHANGED
@@ -1,14 +1,21 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2020",
3
+ "target": "ES2019",
4
4
  "module": "commonjs",
5
+ "outDir": "./lib",
6
+ "rootDir": "./src",
5
7
  "strict": true,
6
8
  "esModuleInterop": true,
7
9
  "skipLibCheck": true,
8
10
  "forceConsistentCasingInFileNames": true,
9
- "outDir": "./dist",
10
- "rootDir": "./src"
11
+ "declaration": true,
12
+ "resolveJsonModule": true
11
13
  },
12
- "include": ["src/**/*"],
13
- "exclude": ["node_modules"]
14
+ "include": [
15
+ "src/**/*"
16
+ ],
17
+ "exclude": [
18
+ "node_modules",
19
+ "lib"
20
+ ]
14
21
  }