sks-maximo-utils 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/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,29 +1,43 @@
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 || ['-h', '--help', '-?', '--version', '-v'].includes(env)) {
9
- console.log(`sks-maximo v${require('./package.json').version}`);
16
+ if (!cmd || ['-h', '--help', '-?', '--version', '-v'].includes(cmd)) {
17
+ console.log(`sks-maximo v${packageJson.version}`);
10
18
  console.log('');
11
19
  console.log('用法:');
12
20
  console.log(' sks-maximo # 检查配置文件并打开配置目录');
13
21
  console.log(' sks-maximo <env> # 加载指定环境配置');
22
+ console.log(' sks-maximo call <scriptName> [--file <fileName>] [--params <params>] # 调用脚本接口');
14
23
  console.log('');
15
24
  console.log('环境:');
16
25
  console.log(' local # 使用本地环境配置');
17
26
  console.log(' dev # 使用开发环境配置');
18
27
  console.log(' hd # 使用测试环境配置');
19
28
  console.log('');
29
+ console.log('命令:');
30
+ console.log(' call # 调用通用脚本接口');
31
+ console.log('');
20
32
  console.log('选项:');
21
33
  console.log(' -h, --help # 显示帮助信息');
22
34
  console.log(' -v, --version # 显示版本信息');
35
+ console.log(' --file <fileName> # 请求体文件路径(JSON)');
36
+ console.log(' --params <params> # URL参数(JSON格式)');
23
37
  console.log('');
24
38
  console.log('配置文件位置: ~/.sks/nodeutils/config.json');
25
39
 
26
- if (!env) {
40
+ if (!cmd) {
27
41
  const configPath = checkAndCreateConfig();
28
42
  const configDir = getConfigDir();
29
43
 
@@ -48,6 +62,53 @@ if (!env || ['-h', '--help', '-?', '--version', '-v'].includes(env)) {
48
62
  }
49
63
  });
50
64
  }
65
+ } else if (cmd === 'call') {
66
+ const scriptName = args[1];
67
+
68
+ if (!scriptName) {
69
+ console.error('错误: 请指定脚本名称');
70
+ console.log('用法: sks-maximo call <scriptName> [--file <fileName>] [--params <params>]');
71
+ process.exit(1);
72
+ }
73
+
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
+ }
94
+ }
95
+
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);
110
+ }
111
+ })();
51
112
  } else {
52
113
  loadConfig(env);
53
114
  }
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';
@@ -177,7 +177,7 @@ export async function importMaxDomain({fileName, logname}) {
177
177
 
178
178
  logger.warn(`[${logname}]收到响应`);
179
179
  logger.warn(`[${logname}]响应类型:`, typeof response);
180
- logger.debug(`[${logname}]响应内容:`, response);
180
+ logger.warn(`[${logname}]响应内容:`, response);
181
181
 
182
182
  // 检查响应 - requestHttp的响应拦截器已返回res.data
183
183
  if (response !== null && response !== undefined) {
@@ -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.5",
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": {