@ty_krystal/sei-ai 0.1.8 → 0.1.13

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 (59) hide show
  1. package/README.md +34 -65
  2. package/dist/commands/api-docs.d.ts +1 -2
  3. package/dist/commands/api-docs.js +1 -1
  4. package/dist/commands/call.d.ts +1 -2
  5. package/dist/commands/call.js +1 -1
  6. package/dist/commands/dict/add-category.d.ts +1 -2
  7. package/dist/commands/dict/add-category.js +1 -1
  8. package/dist/commands/dict/add-item.d.ts +1 -2
  9. package/dist/commands/dict/add-item.js +1 -1
  10. package/dist/commands/dict/list.d.ts +1 -2
  11. package/dist/commands/dict/list.js +1 -1
  12. package/dist/commands/init/base-data.d.ts +1 -2
  13. package/dist/commands/init/base-data.js +1 -1
  14. package/dist/commands/query-sql.d.ts +1 -2
  15. package/dist/commands/query-sql.js +1 -1
  16. package/dist/commands/query.d.ts +1 -2
  17. package/dist/commands/query.js +1 -1
  18. package/dist/commands/relogin.d.ts +1 -2
  19. package/dist/commands/relogin.js +1 -1
  20. package/dist/commands/save.d.ts +1 -2
  21. package/dist/commands/save.js +1 -1
  22. package/dist/commands.d.ts +24 -0
  23. package/dist/commands.js +23 -0
  24. package/dist/core/cli-actions.d.ts +94 -0
  25. package/dist/core/cli-actions.js +155 -0
  26. package/dist/core/cli-helpers.d.ts +39 -0
  27. package/dist/core/cli-helpers.js +246 -0
  28. package/dist/core/command-base/context.d.ts +6 -0
  29. package/dist/core/command-base/context.js +10 -0
  30. package/dist/core/command-base/output.d.ts +2 -0
  31. package/dist/core/command-base/output.js +6 -0
  32. package/dist/core/command-base/payload.d.ts +7 -0
  33. package/dist/core/command-base/payload.js +33 -0
  34. package/dist/core/command-base/sei-command.d.ts +40 -0
  35. package/dist/core/command-base/sei-command.js +85 -0
  36. package/dist/core/config.d.ts +3 -0
  37. package/dist/core/config.js +80 -0
  38. package/dist/core/constants.d.ts +35 -0
  39. package/dist/core/constants.js +48 -0
  40. package/dist/core/env.d.ts +1 -0
  41. package/dist/core/env.js +55 -0
  42. package/dist/core/errors.d.ts +10 -0
  43. package/dist/core/errors.js +55 -0
  44. package/dist/core/index.d.ts +14 -0
  45. package/dist/core/index.js +14 -0
  46. package/dist/core/logger.d.ts +2 -0
  47. package/dist/core/logger.js +71 -0
  48. package/dist/core/openapi.d.ts +2 -0
  49. package/dist/core/openapi.js +261 -0
  50. package/dist/core/sei-client.d.ts +25 -0
  51. package/dist/core/sei-client.js +524 -0
  52. package/dist/core/types.d.ts +135 -0
  53. package/dist/core/types.js +1 -0
  54. package/dist/core/utils.d.ts +12 -0
  55. package/dist/core/utils.js +53 -0
  56. package/dist/hooks/command_not_found.d.ts +3 -0
  57. package/dist/hooks/command_not_found.js +36 -0
  58. package/oclif.manifest.json +361 -423
  59. package/package.json +18 -8
@@ -0,0 +1,261 @@
1
+ import { SeiMcpError } from './errors.js';
2
+ const OPENAPI_HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'TRACE'];
3
+ export function buildOpenApiDocSummary(openapiDoc, keyword) {
4
+ if (!isObject(openapiDoc)) {
5
+ throw new SeiMcpError('OpenAPI 文档不是 JSON 对象', 'INVALID_REQUEST');
6
+ }
7
+ throwIfOpenApiErrorPayload(openapiDoc);
8
+ const info = isObject(openapiDoc.info) ? openapiDoc.info : {};
9
+ const rawPaths = openapiDoc.paths;
10
+ if (!isObject(rawPaths)) {
11
+ throw new SeiMcpError('OpenAPI 文档缺少 paths 对象', 'INVALID_REQUEST');
12
+ }
13
+ const normalizedKeyword = normalizeText(keyword).toLowerCase();
14
+ const items = [];
15
+ for (const [path, rawPathItem] of Object.entries(rawPaths)) {
16
+ if (!isObject(rawPathItem)) {
17
+ continue;
18
+ }
19
+ for (const [method, rawOperation] of Object.entries(rawPathItem)) {
20
+ const methodUpper = method.toUpperCase();
21
+ if (!OPENAPI_HTTP_METHODS.includes(methodUpper) || !isObject(rawOperation)) {
22
+ continue;
23
+ }
24
+ const tags = Array.isArray(rawOperation.tags)
25
+ ? rawOperation.tags.map((item) => normalizeText(item)).filter(Boolean)
26
+ : [];
27
+ const item = {
28
+ method: methodUpper,
29
+ path: normalizeText(path),
30
+ tags,
31
+ summary: normalizeText(rawOperation.summary),
32
+ description: normalizeText(rawOperation.description),
33
+ operationId: normalizeText(rawOperation.operationId),
34
+ parameters: extractOpenApiParameters(rawOperation),
35
+ requestBody: extractOpenApiRequestBody(rawOperation),
36
+ responses: extractOpenApiResponses(rawOperation),
37
+ };
38
+ if (normalizedKeyword) {
39
+ const searchable = JSON.stringify(item).toLowerCase();
40
+ if (!searchable.includes(normalizedKeyword)) {
41
+ continue;
42
+ }
43
+ }
44
+ items.push(item);
45
+ }
46
+ }
47
+ items.sort((a, b) => {
48
+ const tagA = normalizeText((Array.isArray(a.tags) ? a.tags[0] : '') || '未分组');
49
+ const tagB = normalizeText((Array.isArray(b.tags) ? b.tags[0] : '') || '未分组');
50
+ if (tagA !== tagB) {
51
+ return tagA.localeCompare(tagB, 'zh-CN');
52
+ }
53
+ const pathCompare = normalizeText(a.path).localeCompare(normalizeText(b.path), 'en');
54
+ if (pathCompare !== 0) {
55
+ return pathCompare;
56
+ }
57
+ return OPENAPI_HTTP_METHODS.indexOf(normalizeText(a.method)) -
58
+ OPENAPI_HTTP_METHODS.indexOf(normalizeText(b.method));
59
+ });
60
+ return {
61
+ title: normalizeText(info.title) || 'SEI API',
62
+ version: normalizeText(info.version),
63
+ openapi: normalizeText(openapiDoc.openapi ?? openapiDoc.swagger),
64
+ count: items.length,
65
+ items,
66
+ };
67
+ }
68
+ export function renderOpenApiMarkdown(summary) {
69
+ const lines = [`# ${normalizeText(summary.title) || 'SEI API'}`, ''];
70
+ const version = normalizeText(summary.version);
71
+ const openapiVersion = normalizeText(summary.openapi);
72
+ if (version) {
73
+ lines.push(`- 版本:${version}`);
74
+ }
75
+ if (openapiVersion) {
76
+ lines.push(`- OpenAPI:${openapiVersion}`);
77
+ }
78
+ lines.push(`- 接口数:${Number(summary.count ?? 0)}`);
79
+ let currentTag = '';
80
+ const items = Array.isArray(summary.items) ? summary.items : [];
81
+ for (const rawItem of items) {
82
+ if (!isObject(rawItem)) {
83
+ continue;
84
+ }
85
+ const tags = Array.isArray(rawItem.tags) ? rawItem.tags : [];
86
+ const tag = normalizeText(tags[0] ?? '未分组') || '未分组';
87
+ if (tag !== currentTag) {
88
+ lines.push('', `## ${tag}`);
89
+ currentTag = tag;
90
+ }
91
+ const method = normalizeText(rawItem.method);
92
+ const path = normalizeText(rawItem.path);
93
+ lines.push('', `### ${method} ${path}`);
94
+ const summaryText = normalizeText(rawItem.summary);
95
+ const description = normalizeText(rawItem.description);
96
+ const operationId = normalizeText(rawItem.operationId);
97
+ if (summaryText) {
98
+ lines.push(`- 摘要:${summaryText}`);
99
+ }
100
+ if (description && description !== summaryText) {
101
+ lines.push(`- 说明:${description}`);
102
+ }
103
+ if (operationId) {
104
+ lines.push(`- operationId:\`${operationId}\``);
105
+ }
106
+ const parameters = Array.isArray(rawItem.parameters) ? rawItem.parameters : [];
107
+ if (parameters.length > 0) {
108
+ lines.push('- 参数:');
109
+ for (const rawParameter of parameters) {
110
+ if (!isObject(rawParameter)) {
111
+ continue;
112
+ }
113
+ const name = normalizeText(rawParameter.name);
114
+ const location = normalizeText(rawParameter.in);
115
+ const schema = normalizeText(rawParameter.schema);
116
+ const required = rawParameter.required ? '必填' : '可选';
117
+ const descriptionText = normalizeText(rawParameter.description);
118
+ const detail = [schema, required, descriptionText].filter(Boolean).join(',');
119
+ lines.push(` - \`${location} ${name}\`${detail ? `:${detail}` : ''}`);
120
+ }
121
+ }
122
+ const requestBody = Array.isArray(rawItem.requestBody) ? rawItem.requestBody : [];
123
+ if (requestBody.length > 0) {
124
+ const parts = requestBody
125
+ .filter(isObject)
126
+ .map((body) => {
127
+ const contentType = normalizeText(body.contentType);
128
+ const schema = normalizeText(body.schema);
129
+ return schema ? `${contentType}(${schema})` : contentType;
130
+ })
131
+ .filter(Boolean);
132
+ if (parts.length > 0) {
133
+ lines.push(`- 请求体:${parts.join(', ')}`);
134
+ }
135
+ }
136
+ const responses = Array.isArray(rawItem.responses) ? rawItem.responses : [];
137
+ if (responses.length > 0) {
138
+ const parts = responses
139
+ .filter(isObject)
140
+ .map((response) => `${normalizeText(response.status)} ${normalizeText(response.description)}`.trim())
141
+ .filter(Boolean);
142
+ if (parts.length > 0) {
143
+ lines.push(`- 响应:${parts.join(', ')}`);
144
+ }
145
+ }
146
+ }
147
+ return `${lines.join('\n').trimEnd()}\n`;
148
+ }
149
+ function extractOpenApiParameters(operation) {
150
+ const rawParameters = operation.parameters;
151
+ if (!Array.isArray(rawParameters)) {
152
+ return [];
153
+ }
154
+ return rawParameters
155
+ .filter(isObject)
156
+ .map((parameter) => ({
157
+ name: normalizeText(parameter.name),
158
+ in: normalizeText(parameter.in),
159
+ required: Boolean(parameter.required),
160
+ schema: summarizeSchema(parameter.schema),
161
+ description: normalizeText(parameter.description),
162
+ }))
163
+ .filter((item) => item.name || item.in);
164
+ }
165
+ function extractOpenApiRequestBody(operation) {
166
+ const requestBody = operation.requestBody;
167
+ if (!isObject(requestBody) || !isObject(requestBody.content)) {
168
+ return [];
169
+ }
170
+ return Object.entries(requestBody.content)
171
+ .map(([contentType, contentInfo]) => ({
172
+ contentType: normalizeText(contentType),
173
+ schema: summarizeSchema(isObject(contentInfo) ? contentInfo.schema : undefined),
174
+ }))
175
+ .filter((item) => item.contentType);
176
+ }
177
+ function extractOpenApiResponses(operation) {
178
+ const rawResponses = operation.responses;
179
+ if (!isObject(rawResponses)) {
180
+ return [];
181
+ }
182
+ return Object.entries(rawResponses)
183
+ .map(([status, response]) => ({
184
+ status: normalizeText(status),
185
+ description: normalizeText(isObject(response) ? response.description : ''),
186
+ }))
187
+ .filter((item) => item.status);
188
+ }
189
+ function summarizeSchema(schema) {
190
+ if (!isObject(schema)) {
191
+ return '';
192
+ }
193
+ const ref = normalizeText(schema.$ref);
194
+ if (ref) {
195
+ return ref.split('/').pop() ?? ref;
196
+ }
197
+ const schemaType = normalizeText(schema.type);
198
+ const schemaFormat = normalizeText(schema.format);
199
+ if (schemaType === 'array') {
200
+ const itemText = summarizeSchema(schema.items);
201
+ return `array<${itemText || 'item'}>`;
202
+ }
203
+ if (schemaType && schemaFormat) {
204
+ return `${schemaType}/${schemaFormat}`;
205
+ }
206
+ if (schemaType) {
207
+ return schemaType;
208
+ }
209
+ return Array.isArray(schema.enum) && schema.enum.length > 0 ? 'enum' : '';
210
+ }
211
+ function throwIfOpenApiErrorPayload(openapiDoc) {
212
+ const rootCause = isObject(openapiDoc.rootCause) ? openapiDoc.rootCause : null;
213
+ const stackTrace = Array.isArray(rootCause?.stackTrace) ? rootCause.stackTrace : [];
214
+ const topFrame = stackTrace.find(isObject);
215
+ const rootMessage = normalizeText(rootCause?.message);
216
+ const rootException = normalizeText(rootCause?.exception);
217
+ const message = normalizeText(openapiDoc.message);
218
+ const error = normalizeText(openapiDoc.error);
219
+ const path = normalizeText(openapiDoc.path);
220
+ if (!rootCause && !message && !error) {
221
+ return;
222
+ }
223
+ const parts = [
224
+ message || error,
225
+ rootException,
226
+ rootMessage,
227
+ formatStackFrame(topFrame),
228
+ path ? `path=${path}` : '',
229
+ ].filter(Boolean);
230
+ throw new SeiMcpError(parts.length > 0
231
+ ? `OpenAPI 文档生成失败:${parts.join(' | ')}`
232
+ : 'OpenAPI 文档生成失败,服务端返回异常对象', 'INVALID_REQUEST', {
233
+ error,
234
+ message,
235
+ path,
236
+ rootCause: rootCause ?? undefined,
237
+ });
238
+ }
239
+ function formatStackFrame(frame) {
240
+ if (!isObject(frame)) {
241
+ return '';
242
+ }
243
+ const className = normalizeText(frame.className);
244
+ const methodName = normalizeText(frame.methodName);
245
+ const fileName = normalizeText(frame.fileName);
246
+ const lineNumber = Number(frame.lineNumber);
247
+ const locationParts = [className, methodName].filter(Boolean);
248
+ const sourceParts = [fileName, Number.isFinite(lineNumber) && lineNumber > 0 ? String(lineNumber) : ''].filter(Boolean);
249
+ const location = locationParts.join('#');
250
+ const source = sourceParts.join(':');
251
+ if (location && source) {
252
+ return `${location} (${source})`;
253
+ }
254
+ return location || source;
255
+ }
256
+ function normalizeText(value) {
257
+ return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
258
+ }
259
+ function isObject(value) {
260
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
261
+ }
@@ -0,0 +1,25 @@
1
+ import type { Logger, SeiAuthSettings, SeiClient, SeiCommandOptions, SeiMcpConfig, SeiRequestOptions } from './types.js';
2
+ interface JsonRequestOptions extends SeiRequestOptions {
3
+ allowEmpty?: boolean;
4
+ allowFailure?: boolean;
5
+ skipAuth?: boolean;
6
+ }
7
+ export interface CliSeiClient {
8
+ call(method: string, path: string, payload?: unknown, options?: JsonRequestOptions): Promise<unknown>;
9
+ query(payload: unknown, options?: JsonRequestOptions): Promise<unknown>;
10
+ save(payload: unknown, options?: JsonRequestOptions): Promise<unknown>;
11
+ querySql(sql: string, page?: number, size?: number, options?: JsonRequestOptions): Promise<unknown>;
12
+ readApiDocs(): Promise<unknown>;
13
+ relogin(): Promise<string>;
14
+ executeDdlStatements(statements: string[], options?: JsonRequestOptions): Promise<unknown>;
15
+ }
16
+ export declare function createSeiClient(config: SeiMcpConfig, logger: Logger, fetchImpl?: typeof fetch): SeiClient;
17
+ export declare function createSeiAuthSettings(config: SeiMcpConfig, options?: SeiCommandOptions): SeiAuthSettings;
18
+ export declare function createCliSeiClient(settings: SeiAuthSettings, logger: Logger, fetchImpl?: typeof fetch): CliSeiClient;
19
+ export declare function buildQuerySqlPayload(sql: string, page?: number, size?: number): {
20
+ sql: string;
21
+ page: number;
22
+ size: number;
23
+ };
24
+ export declare function stripTrailingQuerySqlLimit(sql: string): string;
25
+ export {};