koishi-plugin-cfmrmod 1.1.4 → 1.1.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/README.md CHANGED
@@ -19,6 +19,15 @@ Star就是我维护的动力🤤
19
19
  - `cnmc.mod/.data/.pack/.tutorial/.author/.user <关键词>`
20
20
  - `cf.help` / `mr.help` / `cnmc.help`
21
21
 
22
+ #### AI 自然语言查询(可选)
23
+ 开启 `nlu.enabled` 并配置 OpenAI 兼容接口后,只有 `@机器人` 的消息会进入自然语言理解;不 @ 机器人时仍只响应上面的显式命令。
24
+
25
+ 示例:
26
+ - `@机器人 查询钠模组`:默认在 MCMod 查询 Mod
27
+ - `@机器人 在 cf 查一下 jei`:在 CurseForge 查询 Mod
28
+ - `@机器人 在 mr 查一下 iris 光影`:在 Modrinth 查询 Shader
29
+ - `@机器人 在 cnmc 查一下 Create: EasyFilling`:在 MCMod 查询 Mod
30
+
22
31
  #### 更新通知(notify)
23
32
  - `notify.add <platform> <projectId>` 添加订阅
24
33
  - `notify.remove <platform> <projectId>` 删除订阅
@@ -42,6 +51,20 @@ Star就是我维护的动力🤤
42
51
  - `timeouts`: 搜索会话超时(毫秒)
43
52
  - `debug`: 调试日志开关
44
53
 
54
+ #### AI 自然语言理解(nlu)
55
+ - `nlu.enabled`: 是否启用 `@机器人` 自然语言查询
56
+ - `nlu.endpoint`: OpenAI 兼容 Chat Completions 接口地址,默认 `https://api.openai.com/v1/chat/completions`
57
+ - `nlu.apiKey`: API Key
58
+ - `nlu.model`: 模型名称,默认 `gpt-4o-mini`
59
+ - `nlu.timeout`: AI 请求超时(毫秒)
60
+ - `nlu.temperature`: AI 温度参数,默认 `0`
61
+
62
+ #### MCMod(mcmod)
63
+ - `mcmod.cookie`: 手动填写 mcmod.cn Cookie
64
+ - `mcmod.autoCookie`: 自动从 `cookie-manager` 获取 Cookie(存在该模块时生效)
65
+ - `mcmod.cookieCheckInterval`: Cookie / `MCMOD_SEED` 检查间隔(毫秒)
66
+ - 未配置 Cookie 时,插件会自动访问 MCMod 首页获取 `MCMOD_SEED`,用于通过站点的基础 Cookie 校验。
67
+
45
68
  #### 更新通知(notify)
46
69
  - `notify.enabled`: 是否开启更新通知
47
70
  - `notify.interval`: 全局轮询间隔(毫秒)
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ exports.apply = apply;
38
38
  const koishi_1 = require("koishi");
39
39
  const cfmr = __importStar(require("./plugins/cfmr"));
40
40
  const mcmod = __importStar(require("./plugins/mcmod"));
41
+ const nlu = __importStar(require("./nlu"));
41
42
  const notify = __importStar(require("./notify"));
42
43
  exports.name = 'minecraft-search';
43
44
  exports.inject = ['database'];
@@ -46,7 +47,7 @@ exports.Config = koishi_1.Schema.object({
46
47
  cf: koishi_1.Schema.string().default('cf'),
47
48
  mr: koishi_1.Schema.string().default('mr'),
48
49
  cnmc: koishi_1.Schema.string().default('cnmc'),
49
- }).description('指令前缀设置'),
50
+ }).default({ cf: 'cf', mr: 'mr', cnmc: 'cnmc' }).description('指令前缀设置'),
50
51
  notify: koishi_1.Schema.object({
51
52
  enabled: koishi_1.Schema.boolean().default(false).description('是否开启模组更新通知'),
52
53
  interval: koishi_1.Schema.number().default(30 * 60 * 1000).description('轮询间隔(ms),默认 30 分钟'),
@@ -62,11 +63,19 @@ exports.Config = koishi_1.Schema.object({
62
63
  interval: koishi_1.Schema.number().default(30 * 60 * 1000).description('单独轮询间隔(ms),默认 30 分钟,<= 0 禁用该订阅'),
63
64
  })).role('table').default([]).description('订阅列表'),
64
65
  })).role('table').default([]).description('通知群与订阅列表'),
66
+ }).default({
67
+ enabled: false,
68
+ interval: 30 * 60 * 1000,
69
+ adminAuthority: 3,
70
+ stateFile: 'data/cfmrmod_notify_state.json',
71
+ configFile: 'data/cfmrmod_notify_config.json',
72
+ groups: [],
65
73
  }).description('—— 更新通知 ——'),
66
74
  timeouts: koishi_1.Schema.number().default(60000).description('搜索会话超时时间(ms)'),
67
75
  debug: koishi_1.Schema.boolean().default(false).description('开启调试日志'),
68
- cfmr: cfmr.Config.description('CurseForge/Modrinth 搜索与图片卡片'),
69
- mcmod: mcmod.Config.description('MCMod.cn 搜索与图片卡片'),
76
+ nlu: nlu.Config,
77
+ cfmr: cfmr.Config.default({}).description('CurseForge/Modrinth 搜索与图片卡片'),
78
+ mcmod: mcmod.Config.default({}).description('MCMod.cn 搜索与图片卡片'),
70
79
  });
71
80
  function apply(ctx, config) {
72
81
  const logger = ctx.logger(exports.name);
@@ -98,6 +107,8 @@ function apply(ctx, config) {
98
107
  cfmr.apply(ctx, { ...((config === null || config === void 0 ? void 0 : config.cfmr) || {}), ...shared });
99
108
  if (mcmod.apply)
100
109
  mcmod.apply(ctx, { ...((config === null || config === void 0 ? void 0 : config.mcmod) || {}), ...shared });
110
+ if (nlu.apply)
111
+ nlu.apply(ctx, (config === null || config === void 0 ? void 0 : config.nlu) || {}, shared);
101
112
  if (notify.apply && canvasAdapter)
102
113
  notify.apply(ctx, (config === null || config === void 0 ? void 0 : config.notify) || {}, { cfmr: (config === null || config === void 0 ? void 0 : config.cfmr) || {} });
103
114
  if (!canvasAdapter)
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setMcmodCookie = setMcmodCookie;
4
+ exports.configureMcmodCookie = configureMcmodCookie;
4
5
  exports.getMcmodCookie = getMcmodCookie;
5
6
  exports.loadManagedCookie = loadManagedCookie;
6
7
  exports.fetchWithTimeout = fetchWithTimeout;
@@ -20,7 +21,8 @@ catch (e) {
20
21
  }
21
22
  let globalCookie = '';
22
23
  let cookieLastCheck = 0;
23
- const COOKIE_CHECK_INTERVAL = 30 * 60 * 1000;
24
+ let cookieCheckInterval = 30 * 60 * 1000;
25
+ let useManagedCookie = false;
24
26
  function mergeCookie(name, value) {
25
27
  if (!name || !value)
26
28
  return;
@@ -66,12 +68,22 @@ function setMcmodCookie(cookie, checkedAt = Date.now()) {
66
68
  globalCookie = String(cookie || '');
67
69
  cookieLastCheck = checkedAt;
68
70
  }
71
+ function configureMcmodCookie(options = {}) {
72
+ useManagedCookie = !!options.autoCookie;
73
+ const interval = Number(options.checkInterval);
74
+ if (Number.isFinite(interval) && interval > 0)
75
+ cookieCheckInterval = interval;
76
+ if (options.cookie !== undefined) {
77
+ setMcmodCookie(options.cookie || '', options.cookie ? Date.now() : 0);
78
+ }
79
+ }
69
80
  function getMcmodCookie() {
70
81
  return globalCookie;
71
82
  }
72
83
  function loadManagedCookie(logger) {
73
84
  if (!cookieManager)
74
85
  return;
86
+ useManagedCookie = true;
75
87
  cookieManager.getCookie().then(cookie => {
76
88
  var _a;
77
89
  if (cookie) {
@@ -125,10 +137,10 @@ function getImageHeaders(url, referer = `${constants_1.BASE_URL}/`) {
125
137
  }
126
138
  async function ensureValidCookie() {
127
139
  const now = Date.now();
128
- if (hasCookie('MCMOD_SEED') && (now - cookieLastCheck) < COOKIE_CHECK_INTERVAL) {
140
+ if (hasCookie('MCMOD_SEED') && (now - cookieLastCheck) < cookieCheckInterval) {
129
141
  return;
130
142
  }
131
- if (cookieManager) {
143
+ if (useManagedCookie && cookieManager) {
132
144
  try {
133
145
  const cookie = await cookieManager.getCookie();
134
146
  if (cookie) {
@@ -15,7 +15,9 @@ const searchStates = new Map();
15
15
  exports.name = 'mcmod-search';
16
16
  exports.Config = Schema.object({
17
17
  sendLink: Schema.boolean().default(true).description('发送卡片后是否附带链接'),
18
- cookie: Schema.string().description('【可选】手动填写 mcmod.cn 的 Cookie'),
18
+ cookie: Schema.string().default('').description('【可选】手动填写 mcmod.cn 的 Cookie'),
19
+ autoCookie: Schema.boolean().default(false).description('自动从 cookie-manager 获取 mcmod.cn Cookie(存在该模块时生效)'),
20
+ cookieCheckInterval: Schema.number().default(30 * 60 * 1000).description('Cookie/Seed 检查间隔(ms)'),
19
21
  fontPath: Schema.string().role('path').description('可选:自定义字体文件路径'),
20
22
  debug: Schema.boolean().default(false).description('输出渲染调试日志'),
21
23
  render: Schema.object({
@@ -34,9 +36,13 @@ function apply(ctx, config) {
34
36
  if (!(0, rendering_1.configureRenderer)(config === null || config === void 0 ? void 0 : config.canvas, config, logger)) {
35
37
  return;
36
38
  }
39
+ (0, http_1.configureMcmodCookie)({
40
+ cookie: config.cookie,
41
+ autoCookie: config.autoCookie,
42
+ checkInterval: config.cookieCheckInterval,
43
+ });
37
44
  // 初始化 Cookie
38
45
  if (config.cookie) {
39
- (0, http_1.setMcmodCookie)(config.cookie);
40
46
  logger.info('使用手动配置的 Cookie');
41
47
  }
42
48
  else if (config.autoCookie) {
package/dist/nlu.js ADDED
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Config = void 0;
4
+ exports.normalizeAiDecision = normalizeAiDecision;
5
+ exports.buildCommand = buildCommand;
6
+ exports.apply = apply;
7
+ const { Schema } = require('koishi');
8
+ const fetch = require('node-fetch');
9
+ const DEFAULT_ENDPOINT = 'https://api.openai.com/v1/chat/completions';
10
+ const DEFAULT_MODEL = 'gpt-4o-mini';
11
+ exports.Config = Schema.object({
12
+ enabled: Schema.boolean().default(false).description('启用 @机器人 自然语言查询入口'),
13
+ endpoint: Schema.string().default(DEFAULT_ENDPOINT).description('OpenAI 兼容 Chat Completions 接口地址'),
14
+ apiKey: Schema.string().default('').description('OpenAI 兼容接口 API Key'),
15
+ model: Schema.string().default(DEFAULT_MODEL).description('模型名称'),
16
+ timeout: Schema.number().default(15000).description('AI 请求超时(ms)'),
17
+ temperature: Schema.number().default(0).description('AI 温度参数'),
18
+ }).default({}).description('—— AI 自然语言理解 ——');
19
+ const PLATFORM_ALIASES = {
20
+ mcmod: 'mcmod', cnmc: 'mcmod', mc: 'mcmod', 'mcmod.cn': 'mcmod', 'mc百科': 'mcmod',
21
+ cf: 'cf', curseforge: 'cf', curse: 'cf',
22
+ mr: 'mr', modrinth: 'mr',
23
+ };
24
+ const TYPE_ALIASES = {
25
+ mod: 'mod', mods: 'mod', 模组: 'mod',
26
+ pack: 'pack', modpack: 'pack', 整合包: 'pack',
27
+ resource: 'resource', resourcepack: 'resource', 材质: 'resource', 资源包: 'resource', 材质包: 'resource',
28
+ shader: 'shader', 光影: 'shader',
29
+ plugin: 'plugin', 插件: 'plugin',
30
+ data: 'data', item: 'data', 资料: 'data', 物品: 'data',
31
+ tutorial: 'tutorial', post: 'tutorial', 教程: 'tutorial',
32
+ author: 'author', 作者: 'author',
33
+ user: 'user', 用户: 'user',
34
+ };
35
+ const PLATFORM_TYPES = {
36
+ mcmod: new Set(['mod', 'pack', 'data', 'tutorial', 'author', 'user']),
37
+ cf: new Set(['mod', 'pack', 'resource', 'shader', 'plugin']),
38
+ mr: new Set(['mod', 'pack', 'resource', 'shader', 'plugin']),
39
+ };
40
+ function normalizeEndpoint(endpoint) {
41
+ const value = String(endpoint || DEFAULT_ENDPOINT).trim().replace(/\/+$/, '');
42
+ if (/\/chat\/completions$/i.test(value))
43
+ return value;
44
+ if (/\/v\d+$/i.test(value))
45
+ return `${value}/chat/completions`;
46
+ return value;
47
+ }
48
+ function withTimeout(timeout) {
49
+ const controller = new AbortController();
50
+ const timer = setTimeout(() => controller.abort(), Math.max(1000, Number(timeout) || 15000));
51
+ return { controller, done: () => clearTimeout(timer) };
52
+ }
53
+ function extractJsonObject(text) {
54
+ const value = String(text || '').trim();
55
+ try {
56
+ return JSON.parse(value);
57
+ }
58
+ catch { }
59
+ const match = value.match(/\{[\s\S]*\}/);
60
+ if (!match)
61
+ throw new Error('AI 返回内容不是 JSON');
62
+ return JSON.parse(match[0]);
63
+ }
64
+ function normalizePlatform(value) {
65
+ const key = String(value || '').trim().toLowerCase();
66
+ return PLATFORM_ALIASES[key] || 'mcmod';
67
+ }
68
+ function normalizeType(value, platform) {
69
+ var _a;
70
+ const key = String(value || '').trim().toLowerCase();
71
+ const type = TYPE_ALIASES[key] || 'mod';
72
+ return ((_a = PLATFORM_TYPES[platform]) === null || _a === void 0 ? void 0 : _a.has(type)) ? type : 'mod';
73
+ }
74
+ function normalizeAiDecision(raw) {
75
+ const action = String((raw === null || raw === void 0 ? void 0 : raw.action) || '').trim().toLowerCase();
76
+ if (action && action !== 'search')
77
+ return { action: 'ignore' };
78
+ const query = String((raw === null || raw === void 0 ? void 0 : raw.query) || (raw === null || raw === void 0 ? void 0 : raw.keyword) || '').replace(/[\r\n]+/g, ' ').trim();
79
+ if (!query)
80
+ return { action: 'ignore' };
81
+ const platform = normalizePlatform(raw === null || raw === void 0 ? void 0 : raw.platform);
82
+ const type = normalizeType(raw === null || raw === void 0 ? void 0 : raw.type, platform);
83
+ return { action: 'search', platform, type, query };
84
+ }
85
+ async function requestAi(config, text) {
86
+ var _a, _b, _c, _d, _e, _f;
87
+ const endpoint = normalizeEndpoint(config === null || config === void 0 ? void 0 : config.endpoint);
88
+ const headers = { 'Content-Type': 'application/json' };
89
+ if (config === null || config === void 0 ? void 0 : config.apiKey)
90
+ headers['Authorization'] = `Bearer ${config.apiKey}`;
91
+ const body = {
92
+ model: (config === null || config === void 0 ? void 0 : config.model) || DEFAULT_MODEL,
93
+ temperature: Number((_a = config === null || config === void 0 ? void 0 : config.temperature) !== null && _a !== void 0 ? _a : 0) || 0,
94
+ response_format: { type: 'json_object' },
95
+ messages: [
96
+ {
97
+ role: 'system',
98
+ content: [
99
+ '你是 Minecraft 模组搜索意图解析器,只返回 JSON,不要解释。',
100
+ 'JSON 格式: {"action":"search|ignore","platform":"mcmod|cf|mr","type":"mod|pack|data|tutorial|author|user|resource|shader|plugin","query":"关键词"}',
101
+ '默认 platform 为 mcmod,默认 type 为 mod。',
102
+ 'cf/curseforge 表示 CurseForge,mr/modrinth 表示 Modrinth,cnmc/mcmod/MC百科 表示 mcmod.cn。',
103
+ '“查询/搜索/查一下/找一下/模组”等只是意图词,不要放进 query;保留具体模组名、英文名、ID 或关键词。',
104
+ '如果不是搜索请求或没有明确关键词,返回 {"action":"ignore"}。',
105
+ ].join('\n'),
106
+ },
107
+ { role: 'user', content: text },
108
+ ],
109
+ };
110
+ const run = async (payload) => {
111
+ const { controller, done } = withTimeout(config === null || config === void 0 ? void 0 : config.timeout);
112
+ try {
113
+ const res = await fetch(endpoint, {
114
+ method: 'POST',
115
+ headers,
116
+ body: JSON.stringify(payload),
117
+ signal: controller.signal,
118
+ });
119
+ const responseText = await res.text();
120
+ if (!res.ok) {
121
+ const err = new Error(`AI 请求失败: HTTP ${res.status} ${responseText.slice(0, 300)}`);
122
+ err.status = res.status;
123
+ throw err;
124
+ }
125
+ return JSON.parse(responseText);
126
+ }
127
+ finally {
128
+ done();
129
+ }
130
+ };
131
+ let json;
132
+ try {
133
+ json = await run(body);
134
+ }
135
+ catch (e) {
136
+ if ((e === null || e === void 0 ? void 0 : e.status) !== 400 || !body.response_format)
137
+ throw e;
138
+ const retryBody = { ...body };
139
+ delete retryBody.response_format;
140
+ json = await run(retryBody);
141
+ }
142
+ const content = ((_d = (_c = (_b = json === null || json === void 0 ? void 0 : json.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) || ((_f = (_e = json === null || json === void 0 ? void 0 : json.choices) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.text) || (json === null || json === void 0 ? void 0 : json.output_text);
143
+ if (!content)
144
+ throw new Error('AI 返回中没有 message.content');
145
+ return normalizeAiDecision(extractJsonObject(content));
146
+ }
147
+ function botIds(session) {
148
+ var _a, _b;
149
+ return [session === null || session === void 0 ? void 0 : session.selfId, (_a = session === null || session === void 0 ? void 0 : session.bot) === null || _a === void 0 ? void 0 : _a.selfId, (_b = session === null || session === void 0 ? void 0 : session.bot) === null || _b === void 0 ? void 0 : _b.userId]
150
+ .filter(Boolean)
151
+ .map(id => String(id));
152
+ }
153
+ function hasAtSelf(session) {
154
+ var _a, _b, _c, _d;
155
+ const ids = botIds(session);
156
+ const elements = Array.isArray(session === null || session === void 0 ? void 0 : session.elements) ? session.elements : [];
157
+ for (const element of elements) {
158
+ if ((element === null || element === void 0 ? void 0 : element.type) !== 'at')
159
+ continue;
160
+ const id = String(((_a = element === null || element === void 0 ? void 0 : element.attrs) === null || _a === void 0 ? void 0 : _a.id) || ((_b = element === null || element === void 0 ? void 0 : element.attrs) === null || _b === void 0 ? void 0 : _b.userId) || ((_c = element === null || element === void 0 ? void 0 : element.attrs) === null || _c === void 0 ? void 0 : _c.qq) || '');
161
+ if (id && (!ids.length || ids.includes(id)))
162
+ return true;
163
+ }
164
+ const content = String((session === null || session === void 0 ? void 0 : session.content) || '');
165
+ const atRegex = /<at\s+([^>]*?)\/?>(?:<\/at>)?/gi;
166
+ let match;
167
+ while ((match = atRegex.exec(content))) {
168
+ const attrs = match[1] || '';
169
+ const id = (_d = attrs.match(/(?:id|user-id|qq)=(['"]?)([^'"\s/>]+)\1/i)) === null || _d === void 0 ? void 0 : _d[2];
170
+ if (id && (!ids.length || ids.includes(String(id))))
171
+ return true;
172
+ }
173
+ return false;
174
+ }
175
+ function stripAt(text) {
176
+ return String(text || '')
177
+ .replace(/<at\s+[^>]*\/?>(?:<\/at>)?/gi, ' ')
178
+ .replace(/\[CQ:at,[^\]]+\]/gi, ' ')
179
+ .replace(/\s+/g, ' ')
180
+ .trim();
181
+ }
182
+ function getMentionText(session) {
183
+ var _a;
184
+ const stripped = String(((_a = session === null || session === void 0 ? void 0 : session.stripped) === null || _a === void 0 ? void 0 : _a.content) || '').trim();
185
+ const raw = stripAt((session === null || session === void 0 ? void 0 : session.content) || '');
186
+ return (stripped && stripped !== String((session === null || session === void 0 ? void 0 : session.content) || '').trim()) ? stripped : raw;
187
+ }
188
+ function isExplicitCommand(text, prefixes) {
189
+ const value = String(text || '').trim().toLowerCase();
190
+ return Object.values(prefixes || {}).some(prefix => {
191
+ const p = String(prefix || '').trim().toLowerCase();
192
+ return p && (value === p || value.startsWith(`${p} `) || value.startsWith(`${p}.`));
193
+ });
194
+ }
195
+ function buildCommand(decision, prefixes) {
196
+ if (!decision || decision.action !== 'search')
197
+ return '';
198
+ const platform = normalizePlatform(decision.platform);
199
+ const type = normalizeType(decision.type, platform);
200
+ const query = String(decision.query || '').replace(/[\r\n]+/g, ' ').trim();
201
+ if (!query)
202
+ return '';
203
+ const prefix = platform === 'mcmod'
204
+ ? ((prefixes === null || prefixes === void 0 ? void 0 : prefixes.cnmc) || 'cnmc')
205
+ : platform === 'cf'
206
+ ? ((prefixes === null || prefixes === void 0 ? void 0 : prefixes.cf) || 'cf')
207
+ : ((prefixes === null || prefixes === void 0 ? void 0 : prefixes.mr) || 'mr');
208
+ return `${prefix}.${type} ${query}`;
209
+ }
210
+ function apply(ctx, config, shared = {}) {
211
+ if (!(config === null || config === void 0 ? void 0 : config.enabled))
212
+ return;
213
+ const logger = ctx.logger('minecraft-nlu');
214
+ const prefixes = (shared === null || shared === void 0 ? void 0 : shared.prefixes) || {};
215
+ ctx.middleware(async (session, next) => {
216
+ if (!hasAtSelf(session))
217
+ return next();
218
+ const text = getMentionText(session);
219
+ if (!text || isExplicitCommand(text, prefixes))
220
+ return next();
221
+ let decision;
222
+ try {
223
+ decision = await requestAi(config, text);
224
+ }
225
+ catch (e) {
226
+ logger.warn(`AI 自然语言解析失败: ${(e === null || e === void 0 ? void 0 : e.message) || e}`);
227
+ await session.send(`AI 自然语言解析失败: ${(e === null || e === void 0 ? void 0 : e.message) || e}`);
228
+ return;
229
+ }
230
+ const command = buildCommand(decision, prefixes);
231
+ if (!command)
232
+ return next();
233
+ if ((shared === null || shared === void 0 ? void 0 : shared.debug) || (config === null || config === void 0 ? void 0 : config.debug))
234
+ logger.info(`NLU: ${text} -> ${command}`);
235
+ const result = await session.execute(command);
236
+ if (result)
237
+ await session.send(result);
238
+ });
239
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-cfmrmod",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Koishi 插件:搜索 CurseForge/Modrinth/MCMod 并渲染图片卡片",
5
5
  "main": "dist/index.js",
6
6
  "files": [