sks-maximo-utils 1.0.4 → 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/CLI.md ADDED
@@ -0,0 +1,137 @@
1
+ # CLI 命令使用说明
2
+
3
+ ## 命令概览
4
+
5
+ | 命令 | 说明 |
6
+ |------|------|
7
+ | `sks-maximo` | 检查配置文件并打开配置目录 |
8
+ | `sks-maximo <env>` | 加载指定环境配置 |
9
+ | `sks-maximo call <scriptName>` | 调用通用脚本接口 |
10
+
11
+ ---
12
+
13
+ ## 1. 打开配置目录
14
+
15
+ ```bash
16
+ sks-maximo
17
+ ```
18
+
19
+ **功能:**
20
+ - 检查 `~/.sks/nodeutils/config.json` 是否存在
21
+ - 不存在则复制 `sample.config.json` 创建
22
+ - 自动打开配置文件所在目录
23
+
24
+ ---
25
+
26
+ ## 2. 加载环境配置
27
+
28
+ ```bash
29
+ sks-maximo local # 使用本地环境配置
30
+ sks-maximo dev # 使用开发环境配置
31
+ sks-maximo hd # 使用测试环境配置
32
+ ```
33
+
34
+ **功能:**
35
+ - 加载指定环境的配置到全局
36
+ - 后续所有 API 调用使用该环境的 baseUrl、apiKey 等配置
37
+
38
+ ---
39
+
40
+ ## 3. 调用通用脚本接口
41
+
42
+ ```bash
43
+ sks-maximo call <scriptName> [--file <fileName>] [--params <params>] [--logname <logname>]
44
+ ```
45
+
46
+ **参数说明:**
47
+
48
+ | 参数 | 简写 | 说明 | 必填 |
49
+ |------|------|------|------|
50
+ | `<scriptName>` | - | Maximo 脚本接口名称 | 是 |
51
+ | `--file` | `-f` | 请求体文件路径(JSON/XML等) | 否 |
52
+ | `--params` | `-p` | URL 查询参数(JSON格式) | 否 |
53
+ | `--logname` | `-l` | 日志名称 | 否 |
54
+
55
+ ### 3.1 GET 请求(无请求体)
56
+
57
+ ```bash
58
+ sks-maximo call SHARPTREE.AUTOSCRIPT.LIBRARY --params '{"name":"TEST"}'
59
+ ```
60
+
61
+ ### 3.2 POST 请求(带请求体文件)
62
+
63
+ ```bash
64
+ # JSON 请求体
65
+ sks-maximo call SKS_DEPLOY_AUTOKEY --file data/autokey.json
66
+
67
+ # XML 请求体
68
+ sks-maximo call SHARPTREE.AUTOSCRIPT.SCREENS --file screens/test.xml
69
+
70
+ # 同时带参数和请求体
71
+ sks-maximo call SKS_IMP_MAXAPPINFO --file appinfo.json --params '{"debug":true}' --logname "导入应用信息"
72
+ ```
73
+
74
+ ### 3.3 请求体文件处理
75
+
76
+ - **`.json` 文件**:自动解析为 JSON 对象作为请求体
77
+ - **其他文件**:读取文件内容作为字符串请求体
78
+
79
+ ---
80
+
81
+ ## 配置文件说明
82
+
83
+ 配置文件位置:`~/.sks/nodeutils/config.json`
84
+
85
+ ```json
86
+ {
87
+ "logLevel": "INFO",
88
+ "langcode": "zh",
89
+ "envs": {
90
+ "local": {
91
+ "baseUrl": "http://127.0.0.1:9080/maximo",
92
+ "apiKey": "your-api-key",
93
+ "maxauth": "",
94
+ "authType": "apiKey",
95
+ "langcode": "zh"
96
+ },
97
+ "dev": {
98
+ "baseUrl": "https://mdev/maximo",
99
+ "apiKey": "your-api-key",
100
+ "maxauth": "",
101
+ "authType": "apiKey"
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## 示例
110
+
111
+ ### 示例 1:查询脚本库
112
+
113
+ ```bash
114
+ sks-maximo call SHARPTREE.AUTOSCRIPT.LIBRARY --params '{"name":"MY_SCRIPT"}'
115
+ ```
116
+
117
+ ### 示例 2:部署自动编码
118
+
119
+ ```bash
120
+ sks-maximo call SKS_DEPLOY_AUTOKEY --file autokey/test.json --logname "部署自动编码"
121
+ ```
122
+
123
+ ### 示例 3:导入应用信息
124
+
125
+ ```bash
126
+ sks-maximo call SKS_IMP_MAXAPPINFO --file appinfo/APP001.json
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 帮助信息
132
+
133
+ ```bash
134
+ sks-maximo --help # 显示帮助
135
+ sks-maximo -h # 显示帮助
136
+ sks-maximo -v # 显示版本号
137
+ ```
package/README.md CHANGED
@@ -11,7 +11,7 @@ npm install -g shoukaiseki-maximo-utils
11
11
 
12
12
  ### 项目中安装(引用使用)
13
13
  ```bash
14
- npm install shoukaiseki-maximo-utils
14
+ npm install sks-maximo-utils
15
15
  ```
16
16
 
17
17
  ## 使用方式
@@ -148,7 +148,7 @@ npm install
148
148
  npm link
149
149
 
150
150
  # 在其他项目中链接测试
151
- npm link shoukaiseki-maximo-utils
151
+ npm link sks-maximo-utils
152
152
  ```
153
153
 
154
154
  ## License
package/cli.js CHANGED
@@ -1,38 +1,115 @@
1
1
  #!/usr/bin/env node
2
2
  import { exec } from 'child_process';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath, URL } from 'url';
3
5
  import { checkAndCreateConfig, getConfigDir, loadConfig } from './nodeutils/config.js';
6
+ import { callMaxScript } from './nodeutils/maximoTools.js';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = new URL('.', import.meta.url).pathname;
10
+ const packageJson = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8'));
4
11
 
5
12
  const args = process.argv.slice(2);
13
+ const cmd = args[0];
6
14
  const env = args[0];
7
15
 
8
- if (!env) {
9
- // 不带参数:检查配置并打开目录
10
- const configPath = checkAndCreateConfig();
11
- const configDir = getConfigDir();
16
+ if (!cmd || ['-h', '--help', '-?', '--version', '-v'].includes(cmd)) {
17
+ console.log(`sks-maximo v${packageJson.version}`);
18
+ console.log('');
19
+ console.log('用法:');
20
+ console.log(' sks-maximo # 检查配置文件并打开配置目录');
21
+ console.log(' sks-maximo <env> # 加载指定环境配置');
22
+ console.log(' sks-maximo call <scriptName> [--file <fileName>] [--params <params>] # 调用脚本接口');
23
+ console.log('');
24
+ console.log('环境:');
25
+ console.log(' local # 使用本地环境配置');
26
+ console.log(' dev # 使用开发环境配置');
27
+ console.log(' hd # 使用测试环境配置');
28
+ console.log('');
29
+ console.log('命令:');
30
+ console.log(' call # 调用通用脚本接口');
31
+ console.log('');
32
+ console.log('选项:');
33
+ console.log(' -h, --help # 显示帮助信息');
34
+ console.log(' -v, --version # 显示版本信息');
35
+ console.log(' --file <fileName> # 请求体文件路径(JSON)');
36
+ console.log(' --params <params> # URL参数(JSON格式)');
37
+ console.log('');
38
+ console.log('配置文件位置: ~/.sks/nodeutils/config.json');
12
39
 
13
- console.log(`配置文件位置: ${configPath}`);
14
- console.log(`正在打开配置目录...`);
40
+ if (!cmd) {
41
+ const configPath = checkAndCreateConfig();
42
+ const configDir = getConfigDir();
43
+
44
+ console.log(`\n配置文件位置: ${configPath}`);
45
+ console.log(`正在打开配置目录...`);
46
+
47
+ const platform = process.platform;
48
+ let command;
49
+
50
+ if (platform === 'win32') {
51
+ command = `explorer "${configDir}"`;
52
+ } else if (platform === 'darwin') {
53
+ command = `open "${configDir}"`;
54
+ } else {
55
+ command = `xdg-open "${configDir}"`;
56
+ }
57
+
58
+ exec(command, (error) => {
59
+ if (error) {
60
+ console.error(`打开目录失败: ${error.message}`);
61
+ console.log(`请手动打开: ${configDir}`);
62
+ }
63
+ });
64
+ }
65
+ } else if (cmd === 'call') {
66
+ const scriptName = args[1];
15
67
 
16
- // 根据操作系统打开目录
17
- const platform = process.platform;
18
- let command;
68
+ if (!scriptName) {
69
+ console.error('错误: 请指定脚本名称');
70
+ console.log('用法: sks-maximo call <scriptName> [--file <fileName>] [--params <params>]');
71
+ process.exit(1);
72
+ }
19
73
 
20
- if (platform === 'win32') {
21
- command = `explorer "${configDir}"`;
22
- } else if (platform === 'darwin') {
23
- command = `open "${configDir}"`;
24
- } else {
25
- command = `xdg-open "${configDir}"`;
74
+ let fileName = null;
75
+ let params = {};
76
+ let logname = null;
77
+
78
+ for (let i = 2; i < args.length; i++) {
79
+ if (args[i] === '--file' || args[i] === '-f') {
80
+ fileName = args[i + 1];
81
+ i++;
82
+ } else if (args[i] === '--params' || args[i] === '-p') {
83
+ try {
84
+ params = JSON.parse(args[i + 1]);
85
+ } catch (e) {
86
+ console.error(`参数解析失败: ${e.message}`);
87
+ process.exit(1);
88
+ }
89
+ i++;
90
+ } else if (args[i] === '--logname' || args[i] === '-l') {
91
+ logname = args[i + 1];
92
+ i++;
93
+ }
26
94
  }
27
95
 
28
- exec(command, (error) => {
29
- if (error) {
30
- console.error(`打开目录失败: ${error.message}`);
31
- console.log(`请手动打开: ${configDir}`);
96
+ loadConfig('local');
97
+
98
+ (async () => {
99
+ try {
100
+ const response = await callMaxScript({ apiScriptName: scriptName, fileName, params, logname });
101
+ if (response) {
102
+ console.log('响应:', JSON.stringify(response, null, 2));
103
+ } else {
104
+ console.error('调用失败');
105
+ process.exit(1);
106
+ }
107
+ } catch (error) {
108
+ console.error('调用出错:', error.message);
109
+ process.exit(1);
32
110
  }
33
- });
111
+ })();
34
112
  } else {
35
- // 带参数:加载配置
36
113
  loadConfig(env);
37
114
  }
38
115
 
package/index.js CHANGED
@@ -3,5 +3,5 @@ export { loadConfig, getConfig, getWorkDir } from './nodeutils/config.js';
3
3
  export { default as logger } from './nodeutils/logger.js';
4
4
  export { default as request } from './nodeutils/requestHttp.js';
5
5
  export { readFileContent, readJsonFile, readXmlFile, fileExists } from './nodeutils/fileUtils.js';
6
- export { importMaxObject, importMaxPresentation, importMaxDomain, importMaxAutoKey, importMaxScript, saveScriptHistory, importMaxAppInfo } from './nodeutils/maximoTools.js';
6
+ export { importMaxObject, importMaxPresentation, importMaxDomain, importMaxAutoKey, importMaxScript, saveScriptHistory, importMaxAppInfo, callMaxScript } from './nodeutils/maximoTools.js';
7
7
  export { sendMockData, sendMockDataBatch, sendPOMockData, sendPOMockDataBatch, sendRlImportMockDataBatch } from './nodeutils/mockdataTools.js';
@@ -44,7 +44,7 @@ export async function importMaxObject({fileName, logname}) {
44
44
 
45
45
  logger.info(`[${logname}]收到响应`);
46
46
  logger.info(`[${logname}]响应类型:`, typeof response);
47
- logger.info(`[${logname}]响应内容:`, response);
47
+ logger.debug(`[${logname}]响应内容:`, response);
48
48
 
49
49
  // 检查响应 - requestHttp的响应拦截器已返回res.data
50
50
  if (response !== null && response !== undefined) {
@@ -113,7 +113,7 @@ export async function importMaxPresentation({fileName, logname}) {
113
113
 
114
114
  logger.info(`[${logname}]收到响应`);
115
115
  logger.info(`[${logname}]响应类型:`, typeof response);
116
- logger.info(`[${logname}]响应内容:`, response);
116
+ logger.debug(`[${logname}]响应内容:`, response);
117
117
 
118
118
  if (response !== null && response !== undefined) {
119
119
  logger.warn(`[${logname}]导入成功`)
@@ -175,9 +175,9 @@ export async function importMaxDomain({fileName, logname}) {
175
175
  data: fileContent // 将文件内容作为请求体发送
176
176
  })
177
177
 
178
- logger.info(`[${logname}]收到响应`);
179
- logger.info(`[${logname}]响应类型:`, typeof response);
180
- logger.info(`[${logname}]响应内容:`, response);
178
+ logger.warn(`[${logname}]收到响应`);
179
+ logger.warn(`[${logname}]响应类型:`, typeof response);
180
+ logger.warn(`[${logname}]响应内容:`, response);
181
181
 
182
182
  // 检查响应 - requestHttp的响应拦截器已返回res.data
183
183
  if (response !== null && response !== undefined) {
@@ -237,7 +237,7 @@ export async function saveScriptHistory({autoscript, source, version = '', alias
237
237
  }
238
238
  });
239
239
 
240
- logger.info(`[${autoscript}]脚本历史记录保存成功`);
240
+ logger.warn(`[${autoscript}]脚本历史记录保存成功`);
241
241
  return response !== null && response !== undefined;
242
242
  } catch (error) {
243
243
  logger.error(`[${autoscript}]保存脚本历史记录失败:`, error.message);
@@ -276,8 +276,8 @@ export async function importMaxScript({fileName, logname}) {
276
276
 
277
277
  const actualLogname = logname || autoscript;
278
278
  logger.info(`[${actualLogname}]成功读取配置文件: ${fileName}`);
279
- logger.info(`[${actualLogname}]脚本名称: ${autoscript}`);
280
- logger.info(`[${actualLogname}]脚本语言: ${scriptLanguage}`);
279
+ logger.warn(`[${actualLogname}]脚本名称: ${autoscript}`);
280
+ logger.warn(`[${actualLogname}]脚本语言: ${scriptLanguage}`);
281
281
 
282
282
  const dirname = fileName.substring(0, fileName.lastIndexOf('/')) || fileName.substring(0, fileName.lastIndexOf('\\')) || '';
283
283
  const scriptFileName = dirname ? `${dirname}/${autoscript}${scriptExt}` : `${autoscript}${scriptExt}`;
@@ -525,7 +525,7 @@ export async function importMaxAutoKey({fileName, logname}) {
525
525
 
526
526
  logger.info(`[${logname}]收到响应`);
527
527
  logger.info(`[${logname}]响应类型:`, typeof response);
528
- logger.info(`[${logname}]响应内容:`, response);
528
+ logger.debug(`[${logname}]响应内容:`, response);
529
529
 
530
530
  if (response !== null && response !== undefined) {
531
531
  logger.warn(`[${logname}]导入成功`)
@@ -607,4 +607,73 @@ export async function importMaxAppInfo({fileName, logname}) {
607
607
  }
608
608
  return false
609
609
  }
610
- }
610
+ }
611
+ export async function callMaxScript({ apiScriptName, fileName, params = {}, logname }) {
612
+ if (!apiScriptName) {
613
+ logger.error('[callMaxScript]脚本名称不能为空')
614
+ return false
615
+ }
616
+
617
+ const actualLogname = logname || apiScriptName
618
+
619
+ try {
620
+ logger.info(`[${actualLogname}]开始调用脚本: ${apiScriptName}`)
621
+
622
+ let data = null
623
+ if (fileName) {
624
+ const fileContent = readFileContent(fileName)
625
+ if (!fileContent) {
626
+ logger.error(`[${actualLogname}]文件读取失败: ${fileName}`)
627
+ return false
628
+ }
629
+ logger.info(`[${actualLogname}]已读取请求体文件: ${fileName}`)
630
+
631
+ if (fileName.endsWith('.json')) {
632
+ try {
633
+ data = JSON.parse(fileContent)
634
+ } catch (e) {
635
+ logger.error(`[${actualLogname}]JSON解析失败: ${e.message}`)
636
+ return false
637
+ }
638
+ } else {
639
+ data = fileContent
640
+ }
641
+ }
642
+
643
+ const requestConfig = {
644
+ url: `/api/script/${apiScriptName}`,
645
+ method: data ? 'POST' : 'GET',
646
+ params: {
647
+ ...params,
648
+ _langcode: getConfig().langcode,
649
+ }
650
+ }
651
+
652
+ if (data !== null) {
653
+ requestConfig.data = data
654
+ }
655
+
656
+ const response = await request(requestConfig)
657
+
658
+ logger.info(`[${actualLogname}]收到响应`)
659
+ logger.debug(`[${actualLogname}]响应内容:`, response)
660
+
661
+ if (response !== null && response !== undefined) {
662
+ logger.info(`[${actualLogname}]调用成功`)
663
+ return response
664
+ } else {
665
+ logger.error(`[${actualLogname}]调用失败,响应为空或null`)
666
+ return null
667
+ }
668
+ } catch (error) {
669
+ logger.error(`[${actualLogname}]请求出错:`, error.message)
670
+ if (error.response) {
671
+ logger.error(`[${actualLogname}]服务器返回错误:`, error.response.data)
672
+ } else if (error.request) {
673
+ logger.error(`[${actualLogname}]请求已发出但无响应`)
674
+ } else {
675
+ logger.error(`[${actualLogname}]错误详情:`, error)
676
+ }
677
+ return null
678
+ }
679
+ }
@@ -27,6 +27,10 @@ function limitLogOutput(data, maxLength = 200) {
27
27
  }
28
28
  }
29
29
 
30
+ function joinUrl(base, path) {
31
+ return `${base.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
32
+ }
33
+
30
34
  function getLogDir() {
31
35
  return path.join(os.homedir(), '.sks', 'nodeutils', 'logs');
32
36
  }
@@ -39,7 +43,7 @@ function saveRequestLog(config, globalConfig) {
39
43
  }
40
44
 
41
45
  const method = config.method?.toUpperCase() || 'GET';
42
- let url = `${config.url}`;
46
+ let url = joinUrl(globalConfig.baseUrl, config.url);
43
47
 
44
48
  if (config.params && Object.keys(config.params).length > 0) {
45
49
  const paramsStr = Object.entries(config.params)
@@ -79,6 +83,44 @@ function saveRequestLog(config, globalConfig) {
79
83
  }
80
84
  }
81
85
 
86
+ function saveErrorLog(error) {
87
+ try {
88
+ const logDir = getLogDir();
89
+ if (!fs.existsSync(logDir)) {
90
+ fs.mkdirSync(logDir, { recursive: true });
91
+ }
92
+
93
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
94
+ const method = error.config?.method?.toUpperCase() || 'GET';
95
+ const fullUrl = error.config ? joinUrl(getConfig().baseUrl, error.config.url) : 'unknown';
96
+
97
+ const errorLog = {
98
+ timestamp: timestamp,
99
+ method: method,
100
+ url: fullUrl,
101
+ message: error.message,
102
+ status: error.response?.status,
103
+ statusText: error.response?.statusText,
104
+ headers: error.response?.headers,
105
+ data: error.response?.data || error.data,
106
+ config: {
107
+ url: error.config?.url,
108
+ method: error.config?.method,
109
+ params: error.config?.params,
110
+ data: error.config?.data
111
+ }
112
+ };
113
+
114
+ const fileName = `response-error-${method}-${timestamp}.json`;
115
+ const filePath = path.join(logDir, fileName);
116
+
117
+ fs.writeFileSync(filePath, JSON.stringify(errorLog, null, 2), 'utf-8');
118
+
119
+ logger.warn(`[HTTP Log] 错误日志已保存: ${filePath}`);
120
+ } catch (e) {
121
+ logger.error('[HTTP Log] 保存错误日志失败:', e.message);
122
+ }
123
+ }
82
124
  function saveResponseLog(response) {
83
125
  try {
84
126
  const logDir = getLogDir();
@@ -88,12 +130,24 @@ function saveResponseLog(response) {
88
130
 
89
131
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
90
132
  const method = response.config.method?.toUpperCase() || 'GET';
133
+ const fullUrl = joinUrl(getConfig().baseUrl, response.config.url);
134
+
135
+ const responseLog = {
136
+ timestamp: timestamp,
137
+ method: method,
138
+ url: fullUrl,
139
+ status: response.status,
140
+ statusText: response.statusText,
141
+ headers: response.headers,
142
+ data: response.data
143
+ };
144
+
91
145
  const fileName = `response-${method}-${timestamp}.json`;
92
146
  const filePath = path.join(logDir, fileName);
93
147
 
94
- fs.writeFileSync(filePath, JSON.stringify(response.data, null, 2), 'utf-8');
148
+ fs.writeFileSync(filePath, JSON.stringify(responseLog, null, 2), 'utf-8');
95
149
 
96
- logger.error(`[HTTP Log] 响应日志已保存: ${filePath}`);
150
+ logger.warn(`[HTTP Log] 响应日志已保存: ${filePath}`);
97
151
  } catch (error) {
98
152
  logger.error('[HTTP Log] 保存响应日志失败:', error.message);
99
153
  }
@@ -140,7 +194,7 @@ function createService() {
140
194
  if (!config.params['_langcode']) {
141
195
  config.params['_langcode'] = globalConfig.langcode || 'zh';
142
196
  }
143
- var url = `${config.method} ${globalConfig.baseUrl}${config.url}`;
197
+ var url = `${config.method} ${joinUrl(globalConfig.baseUrl, config.url)}`;
144
198
  if (config.params && Object.keys(config.params).length > 0) {
145
199
  if (url.indexOf('?') > -1) {
146
200
  url += `&`;
@@ -200,18 +254,29 @@ function createService() {
200
254
  return Promise.reject(new Error('未知错误'));
201
255
  }
202
256
  }, error => {
203
- logger.error('[Response Error] 请求错误:', error.message);
204
257
  try{
205
258
  if(error){
259
+ logger.error('[Response Error] 请求错误:', error.message);
260
+ saveErrorLog(error);
206
261
  logger.debug('[Response Error] 错误对象:', error);
207
262
  if (error.data) {
208
263
  logger.info('[Response Error] 错误error.data:', error.data);
209
264
  }
210
265
  if (error.response&&error.response.data) {
211
266
  logger.info('[Response Error] 错误error.response.data:', error.response.data);
212
- if(error.response.data['oslc:Error']&&error.response.data['oslc:Error']['oslc:extendedError']&&error.response.data['oslc:Error']['oslc:extendedError']['oslc:moreInfo']){
213
- logger.info('[Response Error] 错误error.response.data:', error.response.data['oslc:Error']['oslc:extendedError']['oslc:moreInfo']);
267
+ if (error.response.data['oslc:Error']) {
268
+ if (error.response.data['oslc:Error']['oslc:extendedError']) {
269
+ if (error.response.data['oslc:Error']['oslc:extendedError']['oslc:moreInfo']) {
270
+ logger.error('[Response Error] 错误error.response.data:', error.response.data['oslc:Error']['oslc:extendedError']['oslc:moreInfo']);
214
271
 
272
+ }
273
+ } else {
274
+ if(error.response.data['oslc:Error']['oslc:message']){
275
+ logger.error('[Response Error] 错误error.response.data.oslc:Error.oslc:message:', error.response.data['oslc:Error']['oslc:message']);
276
+ }else{
277
+ logger.error('[Response Error] 错误error.response.data.oslc:Error.oslc:message:', error.response.data['oslc:Error']);
278
+ }
279
+ }
215
280
  }
216
281
  }
217
282
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sks-maximo-utils",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Maximo helper tools for updating table structures and data",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -10,7 +10,10 @@
10
10
  "scripts": {
11
11
  "test": "echo \"Error: no test specified\" && exit 1"
12
12
  },
13
- "keywords": ["maximo", "sks"],
13
+ "keywords": [
14
+ "maximo",
15
+ "sks"
16
+ ],
14
17
  "author": "shoukaiseki",
15
18
  "license": "ISC",
16
19
  "dependencies": {