@ty_krystal/sei-ai 0.1.1 → 0.1.2

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
@@ -209,3 +209,9 @@ SEI_AI_MCP_HTTP_ENDPOINT=/mcp
209
209
  ```bash
210
210
  http://127.0.0.1:3719/mcp
211
211
  ```
212
+
213
+
214
+
215
+ npm version patch --no-git-tag-version
216
+ pnpm run build
217
+ npm publish --access public --cache /tmp/sei-ai-npm-cache
package/dist/README.md CHANGED
@@ -209,3 +209,9 @@ SEI_AI_MCP_HTTP_ENDPOINT=/mcp
209
209
  ```bash
210
210
  http://127.0.0.1:3719/mcp
211
211
  ```
212
+
213
+
214
+
215
+ npm version patch --no-git-tag-version
216
+ pnpm run build
217
+ npm publish --access public --cache /tmp/sei-ai-npm-cache
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
3
  import { dirname, resolve } from "node:path";
5
4
  import { createHash, randomUUID } from "node:crypto";
5
+ import { homedir } from "node:os";
6
6
  import { createServer } from "node:http";
7
7
  import { ZodOptional, z } from "zod";
8
8
  import process$1 from "node:process";
@@ -58,7 +58,26 @@ var ACCOUNT_CHECK_CODE_PATH = "/api/sei/public/checkCode";
58
58
  var QUERY_PATH = "/api/sei/data/public/query";
59
59
  var SAVE_PATH = "/api/sei/data/public/save";
60
60
  var QUERY_SQL_PATH = "/api/sei/data/querySQL";
61
+ var DDL_EXECUTE_PATH = "/api/sei/mcp/ddl/execute";
61
62
  var OPENAPI_DOCS_PATH = "/v3/api-docs";
63
+ var DICT_MODULE = "sys_dic";
64
+ var DICT_FIELDS = [
65
+ "_UUID",
66
+ "_PID",
67
+ "_SYSID",
68
+ "_TYPE",
69
+ "_CTYPE",
70
+ "_CODE",
71
+ "_NAME",
72
+ "_ENAME",
73
+ "_DICT",
74
+ "_SORT",
75
+ "_BODY",
76
+ "_MEMO"
77
+ ];
78
+ var BASE_SEED_DEFAULT_ADMIN_UID = "admin";
79
+ var BASE_SEED_DEFAULT_ADMIN_ROLE = "admin";
80
+ var BASE_SEED_DEFAULT_ADMIN_PASSWORD = "123456";
62
81
  var ACCOUNT_LOGIN_DEFAULT_TYPE = "login";
63
82
  var TOKEN_CACHE_DIR_NAME = "sei-ai";
64
83
  var TOKEN_CACHE_FILE_PREFIX = "token-";
@@ -76,8 +95,205 @@ var SeiMcpError = class extends Error {
76
95
  }
77
96
  };
78
97
  //#endregion
98
+ //#region src/cli-helpers.ts
99
+ function buildDictQueryPayload(typeCode, keyword, size = 1e3) {
100
+ return {
101
+ head: { module: DICT_MODULE },
102
+ option: {
103
+ keyField: true,
104
+ privilege: true,
105
+ fields: DICT_FIELDS.join(","),
106
+ filter: buildDictQueryFilter(typeCode, keyword),
107
+ order: "_SORT ASC,_CODE ASC",
108
+ page: 1,
109
+ size
110
+ }
111
+ };
112
+ }
113
+ function buildDictSavePayload(row) {
114
+ return {
115
+ head: { module: DICT_MODULE },
116
+ data: [{
117
+ action: "add",
118
+ option: { keyVal: true },
119
+ rows: [{ row }]
120
+ }]
121
+ };
122
+ }
123
+ function buildDictCategoryRow(input) {
124
+ const typeCode = requiredText(input.typeCode, "字典类别");
125
+ const name = requiredText(input.name, "字典名称");
126
+ const uuid = requiredText(input.uuid || typeCode, "字典编号");
127
+ const code = requiredText(input.code || typeCode, "字典代码");
128
+ return {
129
+ _UUID: uuid,
130
+ _PID: "",
131
+ _SYSID: requiredText(input.sysid || "sys", "所属系统"),
132
+ _TYPE: typeCode,
133
+ _CTYPE: normalizeText$1(input.ctype),
134
+ _CODE: code,
135
+ _NAME: name,
136
+ _ENAME: normalizeText$1(input.ename),
137
+ _DICT: normalizeDictFlag(input.dictFlag),
138
+ _SORT: normalizeSort(input.sort),
139
+ _BODY: normalizeText$1(input.body),
140
+ _MEMO: normalizeText$1(input.memo)
141
+ };
142
+ }
143
+ function buildDictItemRow(input) {
144
+ const typeCode = requiredText(input.typeCode, "字典类别");
145
+ const code = requiredText(input.code, "字典代码");
146
+ const name = requiredText(input.name, "字典名称");
147
+ const parent = requiredText(input.parent || typeCode, "父级编号");
148
+ return {
149
+ _UUID: requiredText(input.uuid || `${parent}_${code}`, "字典编号"),
150
+ _PID: parent,
151
+ _SYSID: requiredText(input.sysid || "sys", "所属系统"),
152
+ _TYPE: typeCode,
153
+ _CTYPE: normalizeText$1(input.ctype),
154
+ _CODE: code,
155
+ _NAME: name,
156
+ _ENAME: normalizeText$1(input.ename),
157
+ _DICT: normalizeDictFlag(input.dictFlag),
158
+ _SORT: normalizeSort(input.sort),
159
+ _BODY: normalizeText$1(input.body),
160
+ _MEMO: normalizeText$1(input.memo)
161
+ };
162
+ }
163
+ function buildDictTree(rows) {
164
+ if (!Array.isArray(rows)) return [];
165
+ const recordMap = /* @__PURE__ */ new Map();
166
+ const childrenMap = /* @__PURE__ */ new Map();
167
+ const roots = [];
168
+ for (const row of rows) {
169
+ if (!isObject$5(row)) continue;
170
+ const key = normalizeText$1(row._UUID);
171
+ if (!key) continue;
172
+ recordMap.set(key, { ...row });
173
+ }
174
+ for (const record of recordMap.values()) {
175
+ const parentId = normalizeText$1(record._PID);
176
+ if (!parentId || !recordMap.has(parentId)) {
177
+ roots.push(record);
178
+ continue;
179
+ }
180
+ const children = childrenMap.get(parentId) ?? [];
181
+ children.push(record);
182
+ childrenMap.set(parentId, children);
183
+ }
184
+ for (const [key, record] of recordMap.entries()) {
185
+ const children = childrenMap.get(key);
186
+ if (children && children.length > 0) record.children = children.sort(dictSortKey);
187
+ }
188
+ return roots.sort(dictSortKey);
189
+ }
190
+ function buildBaseSeedSqlVars(input) {
191
+ const adminPassword = input.adminPassword || "123456";
192
+ const passwordHash = createHash("md5").update(adminPassword, "utf8").digest("hex");
193
+ const adminUid = input.adminUid || "admin";
194
+ const adminRole = input.adminRole || "admin";
195
+ const resetSql = input.resetAdminPassword === true ? `UPDATE _SYS_USER\nSET _PWD = ${sqlLiteral(passwordHash)}\nWHERE _UID = ${sqlLiteral(adminUid)};` : "";
196
+ return {
197
+ SYSID: sqlLiteral(input.sysid || "sys"),
198
+ ADMIN_UID: sqlLiteral(adminUid),
199
+ ADMIN_NAME: sqlLiteral(input.adminName || "管理员"),
200
+ ADMIN_ROLE: sqlLiteral(adminRole),
201
+ ADMIN_ROLE_NAME: sqlLiteral(input.adminRoleName || "管理员"),
202
+ ADMIN_PASSWORD_MD5: sqlLiteral(passwordHash),
203
+ RESET_ADMIN_PASSWORD_SQL: resetSql
204
+ };
205
+ }
206
+ function renderBaseSeedSql(template, values) {
207
+ let rendered = template;
208
+ for (const [key, value] of Object.entries(values)) rendered = rendered.replaceAll(`{{${key}}}`, value);
209
+ const missing = [...rendered.matchAll(/\{\{[A-Z0-9_]+\}\}/g)].map((item) => item[0]);
210
+ if (missing.length > 0) throw new SeiMcpError(`初始化 SQL 存在未替换变量:${[...new Set(missing)].join(", ")}`, "INVALID_PARAMS");
211
+ return rendered;
212
+ }
213
+ function removeSqlCommentLines(sql) {
214
+ return sql.split("\n").filter((line) => !line.trimStart().startsWith("--")).join("\n");
215
+ }
216
+ function splitSqlStatements(sql) {
217
+ const statements = [];
218
+ let current = "";
219
+ let quote = null;
220
+ let escaped = false;
221
+ for (const char of sql) {
222
+ if (escaped) {
223
+ current += char;
224
+ escaped = false;
225
+ continue;
226
+ }
227
+ if (char === "\\" && quote !== null) {
228
+ current += char;
229
+ escaped = true;
230
+ continue;
231
+ }
232
+ if (quote !== null) {
233
+ current += char;
234
+ if (char === quote) quote = null;
235
+ continue;
236
+ }
237
+ if (char === "'" || char === "\"" || char === "`") {
238
+ current += char;
239
+ quote = char;
240
+ continue;
241
+ }
242
+ if (char === ";") {
243
+ const statement = current.trim();
244
+ if (statement) statements.push(statement);
245
+ current = "";
246
+ continue;
247
+ }
248
+ current += char;
249
+ }
250
+ const finalStatement = current.trim();
251
+ if (finalStatement) statements.push(finalStatement);
252
+ return statements;
253
+ }
254
+ function buildDictQueryFilter(typeCode, keyword) {
255
+ const filters = [];
256
+ const normalizedType = normalizeText$1(typeCode);
257
+ const normalizedKeyword = normalizeText$1(keyword);
258
+ if (normalizedType) filters.push({ _TYPE: { "=": normalizedType } });
259
+ if (normalizedKeyword) filters.push({ OR: [
260
+ { _UUID: { "*LIKE*": normalizedKeyword } },
261
+ { _TYPE: { "*LIKE*": normalizedKeyword } },
262
+ { _CODE: { "*LIKE*": normalizedKeyword } },
263
+ { _NAME: { "*LIKE*": normalizedKeyword } }
264
+ ] });
265
+ return filters;
266
+ }
267
+ function dictSortKey(a, b) {
268
+ const sortA = normalizeSort(typeof a._SORT === "number" ? a._SORT : Number(a._SORT ?? 0));
269
+ const sortB = normalizeSort(typeof b._SORT === "number" ? b._SORT : Number(b._SORT ?? 0));
270
+ if (sortA !== sortB) return sortA - sortB;
271
+ return normalizeText$1(a._CODE).localeCompare(normalizeText$1(b._CODE), "zh-CN");
272
+ }
273
+ function normalizeDictFlag(value) {
274
+ return value === 1 ? 1 : 0;
275
+ }
276
+ function normalizeSort(value) {
277
+ return Number.isInteger(value) && Number(value) >= 0 ? Number(value) : 100;
278
+ }
279
+ function requiredText(value, label) {
280
+ const normalized = normalizeText$1(value);
281
+ if (!normalized) throw new SeiMcpError(`${label} 不能为空`, "INVALID_PARAMS");
282
+ return normalized;
283
+ }
284
+ function normalizeText$1(value) {
285
+ return typeof value === "string" ? value.trim() : "";
286
+ }
287
+ function sqlLiteral(value) {
288
+ if (value === null || value === void 0) return "NULL";
289
+ return `'${String(value).replace(/'/g, "''")}'`;
290
+ }
291
+ function isObject$5(value) {
292
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
293
+ }
294
+ //#endregion
79
295
  //#region src/utils.ts
80
- function isObject$3(value) {
296
+ function isObject$4(value) {
81
297
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
82
298
  }
83
299
  function toTrimmedString$1(value) {
@@ -169,6 +385,17 @@ function createCliSeiClient(settings, logger, fetchImpl = fetch) {
169
385
  },
170
386
  async readApiDocs() {
171
387
  return requestJsonRaw(settings, logger, fetchImpl, "GET", OPENAPI_DOCS_PATH, void 0, { skipAuth: true });
388
+ },
389
+ async executeDdlStatements(statements, options = {}) {
390
+ const results = [];
391
+ for (const [index, statement] of statements.entries()) {
392
+ const response = await requestSeiJson(settings, logger, fetchImpl, auth, "POST", DDL_EXECUTE_PATH, { ddl: statement }, options);
393
+ results.push({
394
+ index: index + 1,
395
+ response
396
+ });
397
+ }
398
+ return results;
172
399
  }
173
400
  };
174
401
  }
@@ -333,7 +560,7 @@ async function fetchAccountLoginRow(settings, logger, fetchImpl, adminToken, acc
333
560
  const rows = extractResponseRows(response);
334
561
  if (rows.length === 0) throw new SeiMcpError(`未找到账号:${account}`, "INVALID_REQUEST");
335
562
  const row = rows[0];
336
- if (!isObject$2(row)) throw new SeiMcpError(`账号 ${account} 查询结果不是 JSON 对象`, "INVALID_REQUEST");
563
+ if (!isObject$3(row)) throw new SeiMcpError(`账号 ${account} 查询结果不是 JSON 对象`, "INVALID_REQUEST");
337
564
  return row;
338
565
  }
339
566
  async function requestCookieHeader(settings, logger, fetchImpl, payload) {
@@ -370,19 +597,19 @@ async function resolveRequestToken(settings, auth, options) {
370
597
  }
371
598
  function validateSeiEnvelope(response, context, allowFailure, allowEmpty) {
372
599
  if (response === null && allowEmpty) return;
373
- if (!isObject$2(response)) throw new SeiMcpError(`${context} 响应不是 JSON 对象`, "INVALID_REQUEST");
600
+ if (!isObject$3(response)) throw new SeiMcpError(`${context} 响应不是 JSON 对象`, "INVALID_REQUEST");
374
601
  if (!allowFailure && isSeiFailure(response)) throw new SeiMcpError(`${context} 失败:${String(response.message ?? response.msg ?? response.code ?? "未知错误")}`, "INVALID_REQUEST", { response: safeResponse(response) });
375
602
  }
376
603
  function extractToken(response) {
377
604
  validateSeiEnvelope(response, "login", false, false);
378
- if (!isObject$2(response) || !isObject$2(response.data) || typeof response.data.token !== "string" || !response.data.token.trim()) throw new SeiMcpError("登录响应中没有 data.token", "INVALID_REQUEST");
605
+ if (!isObject$3(response) || !isObject$3(response.data) || typeof response.data.token !== "string" || !response.data.token.trim()) throw new SeiMcpError("登录响应中没有 data.token", "INVALID_REQUEST");
379
606
  return response.data.token.trim();
380
607
  }
381
608
  function extractResponseRows(response) {
382
- if (!isObject$2(response)) return [];
609
+ if (!isObject$3(response)) return [];
383
610
  const data = response.data;
384
611
  if (Array.isArray(data)) return data;
385
- if (isObject$2(data)) for (const key of [
612
+ if (isObject$3(data)) for (const key of [
386
613
  "rows",
387
614
  "records",
388
615
  "list",
@@ -456,24 +683,24 @@ function escapeSqlLiteral(value) {
456
683
  return String(value).replace(/'/g, "''");
457
684
  }
458
685
  function isUnauthenticatedResponse(response) {
459
- if (!isObject$2(response)) return false;
686
+ if (!isObject$3(response)) return false;
460
687
  return Number(response.code) === -2;
461
688
  }
462
689
  function isSeiFailure(response) {
463
- if (!isObject$2(response)) return false;
690
+ if (!isObject$3(response)) return false;
464
691
  if (response.success === false) return true;
465
692
  const code = Number(response.code);
466
693
  return Number.isFinite(code) && code < 1;
467
694
  }
468
695
  function safeResponse(response) {
469
- if (!isObject$2(response)) return response;
696
+ if (!isObject$3(response)) return response;
470
697
  return {
471
698
  code: response.code,
472
699
  success: response.success,
473
700
  message: response.message
474
701
  };
475
702
  }
476
- function isObject$2(value) {
703
+ function isObject$3(value) {
477
704
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
478
705
  }
479
706
  function normalizeTimeout(value) {
@@ -504,17 +731,17 @@ var OPENAPI_HTTP_METHODS = [
504
731
  "TRACE"
505
732
  ];
506
733
  function buildOpenApiDocSummary(openapiDoc, keyword) {
507
- if (!isObject$1(openapiDoc)) throw new Error("OpenAPI 文档不是 JSON 对象");
508
- const info = isObject$1(openapiDoc.info) ? openapiDoc.info : {};
734
+ if (!isObject$2(openapiDoc)) throw new Error("OpenAPI 文档不是 JSON 对象");
735
+ const info = isObject$2(openapiDoc.info) ? openapiDoc.info : {};
509
736
  const rawPaths = openapiDoc.paths;
510
- if (!isObject$1(rawPaths)) throw new Error("OpenAPI 文档缺少 paths 对象");
737
+ if (!isObject$2(rawPaths)) throw new Error("OpenAPI 文档缺少 paths 对象");
511
738
  const normalizedKeyword = normalizeText(keyword).toLowerCase();
512
739
  const items = [];
513
740
  for (const [path, rawPathItem] of Object.entries(rawPaths)) {
514
- if (!isObject$1(rawPathItem)) continue;
741
+ if (!isObject$2(rawPathItem)) continue;
515
742
  for (const [method, rawOperation] of Object.entries(rawPathItem)) {
516
743
  const methodUpper = method.toUpperCase();
517
- if (!OPENAPI_HTTP_METHODS.includes(methodUpper) || !isObject$1(rawOperation)) continue;
744
+ if (!OPENAPI_HTTP_METHODS.includes(methodUpper) || !isObject$2(rawOperation)) continue;
518
745
  const tags = Array.isArray(rawOperation.tags) ? rawOperation.tags.map((item) => normalizeText(item)).filter(Boolean) : [];
519
746
  const item = {
520
747
  method: methodUpper,
@@ -559,7 +786,7 @@ function renderOpenApiMarkdown(summary) {
559
786
  let currentTag = "";
560
787
  const items = Array.isArray(summary.items) ? summary.items : [];
561
788
  for (const rawItem of items) {
562
- if (!isObject$1(rawItem)) continue;
789
+ if (!isObject$2(rawItem)) continue;
563
790
  const tag = normalizeText((Array.isArray(rawItem.tags) ? rawItem.tags : [])[0] ?? "未分组") || "未分组";
564
791
  if (tag !== currentTag) {
565
792
  lines.push("", `## ${tag}`);
@@ -578,7 +805,7 @@ function renderOpenApiMarkdown(summary) {
578
805
  if (parameters.length > 0) {
579
806
  lines.push("- 参数:");
580
807
  for (const rawParameter of parameters) {
581
- if (!isObject$1(rawParameter)) continue;
808
+ if (!isObject$2(rawParameter)) continue;
582
809
  const name = normalizeText(rawParameter.name);
583
810
  const location = normalizeText(rawParameter.in);
584
811
  const detail = [
@@ -591,7 +818,7 @@ function renderOpenApiMarkdown(summary) {
591
818
  }
592
819
  const requestBody = Array.isArray(rawItem.requestBody) ? rawItem.requestBody : [];
593
820
  if (requestBody.length > 0) {
594
- const parts = requestBody.filter(isObject$1).map((body) => {
821
+ const parts = requestBody.filter(isObject$2).map((body) => {
595
822
  const contentType = normalizeText(body.contentType);
596
823
  const schema = normalizeText(body.schema);
597
824
  return schema ? `${contentType}(${schema})` : contentType;
@@ -600,7 +827,7 @@ function renderOpenApiMarkdown(summary) {
600
827
  }
601
828
  const responses = Array.isArray(rawItem.responses) ? rawItem.responses : [];
602
829
  if (responses.length > 0) {
603
- const parts = responses.filter(isObject$1).map((response) => `${normalizeText(response.status)} ${normalizeText(response.description)}`.trim()).filter(Boolean);
830
+ const parts = responses.filter(isObject$2).map((response) => `${normalizeText(response.status)} ${normalizeText(response.description)}`.trim()).filter(Boolean);
604
831
  if (parts.length > 0) lines.push(`- 响应:${parts.join(", ")}`);
605
832
  }
606
833
  }
@@ -609,7 +836,7 @@ function renderOpenApiMarkdown(summary) {
609
836
  function extractOpenApiParameters(operation) {
610
837
  const rawParameters = operation.parameters;
611
838
  if (!Array.isArray(rawParameters)) return [];
612
- return rawParameters.filter(isObject$1).map((parameter) => ({
839
+ return rawParameters.filter(isObject$2).map((parameter) => ({
613
840
  name: normalizeText(parameter.name),
614
841
  in: normalizeText(parameter.in),
615
842
  required: Boolean(parameter.required),
@@ -619,22 +846,22 @@ function extractOpenApiParameters(operation) {
619
846
  }
620
847
  function extractOpenApiRequestBody(operation) {
621
848
  const requestBody = operation.requestBody;
622
- if (!isObject$1(requestBody) || !isObject$1(requestBody.content)) return [];
849
+ if (!isObject$2(requestBody) || !isObject$2(requestBody.content)) return [];
623
850
  return Object.entries(requestBody.content).map(([contentType, contentInfo]) => ({
624
851
  contentType: normalizeText(contentType),
625
- schema: summarizeSchema(isObject$1(contentInfo) ? contentInfo.schema : void 0)
852
+ schema: summarizeSchema(isObject$2(contentInfo) ? contentInfo.schema : void 0)
626
853
  })).filter((item) => item.contentType);
627
854
  }
628
855
  function extractOpenApiResponses(operation) {
629
856
  const rawResponses = operation.responses;
630
- if (!isObject$1(rawResponses)) return [];
857
+ if (!isObject$2(rawResponses)) return [];
631
858
  return Object.entries(rawResponses).map(([status, response]) => ({
632
859
  status: normalizeText(status),
633
- description: normalizeText(isObject$1(response) ? response.description : "")
860
+ description: normalizeText(isObject$2(response) ? response.description : "")
634
861
  })).filter((item) => item.status);
635
862
  }
636
863
  function summarizeSchema(schema) {
637
- if (!isObject$1(schema)) return "";
864
+ if (!isObject$2(schema)) return "";
638
865
  const ref = normalizeText(schema.$ref);
639
866
  if (ref) return ref.split("/").pop() ?? ref;
640
867
  const schemaType = normalizeText(schema.type);
@@ -647,7 +874,7 @@ function summarizeSchema(schema) {
647
874
  function normalizeText(value) {
648
875
  return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
649
876
  }
650
- function isObject$1(value) {
877
+ function isObject$2(value) {
651
878
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
652
879
  }
653
880
  //#endregion
@@ -658,7 +885,7 @@ async function runCli(argv, options) {
658
885
  process.stdout.write(`${renderCliHelp()}\n`);
659
886
  return;
660
887
  }
661
- const client = createCliSeiClient(createSeiAuthSettings(options.config, {
888
+ const settings = createSeiAuthSettings(options.config, {
662
889
  baseUrl: toUndefined(readOption(argv, "--base-url")),
663
890
  token: toUndefined(readOption(argv, "--token")),
664
891
  aiKey: toUndefined(readOption(argv, "--ai-key")),
@@ -667,7 +894,8 @@ async function runCli(argv, options) {
667
894
  accountProject: toUndefined(readOption(argv, "--account-project")),
668
895
  accountCheckcode: toUndefined(readOption(argv, "--account-checkcode")),
669
896
  timeoutMs: readOptionNumber(argv, "--timeout")
670
- }), options.logger, options.fetchImpl ?? fetch);
897
+ });
898
+ const client = createCliSeiClient(settings, options.logger, options.fetchImpl ?? fetch);
671
899
  switch (command) {
672
900
  case "api-docs": {
673
901
  const summary = buildOpenApiDocSummary(await client.readApiDocs(), readOption(argv, "--keyword") ?? void 0);
@@ -703,6 +931,80 @@ async function runCli(argv, options) {
703
931
  printJson(await client.querySql(sql, readOptionNumber(argv, "--page"), readOptionNumber(argv, "--size"), { allowFailure: hasFlag(argv, "--allow-failure") }));
704
932
  return;
705
933
  }
934
+ case "dict-list": {
935
+ const payload = buildDictQueryPayload(readOption(argv, "--type") ?? void 0, readOption(argv, "--keyword") ?? void 0, readOptionNumber(argv, "--size") ?? 1e3);
936
+ const response = await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") });
937
+ if (!hasFlag(argv, "--flat") && isObject$1(response) && isObject$1(response.data) && Array.isArray(response.data.rows)) response.data.tree = buildDictTree(response.data.rows);
938
+ printJson(response);
939
+ return;
940
+ }
941
+ case "dict-add-category": {
942
+ const [typeCode, name] = positionalAfter(argv, command, 2);
943
+ if (!typeCode || !name) throw new SeiMcpError("dict-add-category 需要 type_code 和 name", "INVALID_PARAMS");
944
+ const payload = buildDictSavePayload(buildDictCategoryRow({
945
+ body: readOption(argv, "--body") ?? void 0,
946
+ code: readOption(argv, "--code") ?? void 0,
947
+ ctype: readOption(argv, "--ctype") ?? void 0,
948
+ dictFlag: readOptionInteger(argv, "--dict-flag"),
949
+ ename: readOption(argv, "--ename") ?? void 0,
950
+ memo: readOption(argv, "--memo") ?? void 0,
951
+ name,
952
+ sort: readOptionInteger(argv, "--sort"),
953
+ sysid: readOption(argv, "--sysid") ?? void 0,
954
+ typeCode,
955
+ uuid: readOption(argv, "--uuid") ?? void 0
956
+ }));
957
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
958
+ return;
959
+ }
960
+ case "dict-add-item": {
961
+ const [typeCode, code, name] = positionalAfter(argv, command, 3);
962
+ if (!typeCode || !code || !name) throw new SeiMcpError("dict-add-item 需要 type_code、code 和 name", "INVALID_PARAMS");
963
+ const payload = buildDictSavePayload(buildDictItemRow({
964
+ body: readOption(argv, "--body") ?? void 0,
965
+ code,
966
+ ctype: readOption(argv, "--ctype") ?? void 0,
967
+ dictFlag: readOptionInteger(argv, "--dict-flag"),
968
+ ename: readOption(argv, "--ename") ?? void 0,
969
+ memo: readOption(argv, "--memo") ?? void 0,
970
+ name,
971
+ parent: readOption(argv, "--parent") ?? void 0,
972
+ sort: readOptionInteger(argv, "--sort"),
973
+ sysid: readOption(argv, "--sysid") ?? void 0,
974
+ typeCode,
975
+ uuid: readOption(argv, "--uuid") ?? void 0
976
+ }));
977
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
978
+ return;
979
+ }
980
+ case "init-base-data": {
981
+ const sqlFile = resolveBaseSeedSqlPath(readOption(argv, "--sql-file"));
982
+ const statements = splitSqlStatements(removeSqlCommentLines(renderBaseSeedSql(await readFile(sqlFile, "utf8"), buildBaseSeedSqlVars({
983
+ adminName: readOption(argv, "--admin-name") ?? void 0,
984
+ adminPassword: readOption(argv, "--admin-password") ?? void 0,
985
+ adminRole: readOption(argv, "--admin-role") ?? void 0,
986
+ adminRoleName: readOption(argv, "--admin-role-name") ?? void 0,
987
+ adminUid: readOption(argv, "--admin-uid") ?? void 0,
988
+ resetAdminPassword: hasFlag(argv, "--reset-admin-password"),
989
+ sysid: readOption(argv, "--sysid") ?? void 0
990
+ }))));
991
+ if (statements.length === 0) throw new SeiMcpError("初始化 SQL 文件没有可执行语句", "INVALID_PARAMS");
992
+ const executed = await client.executeDdlStatements(statements, { allowFailure: hasFlag(argv, "--allow-failure") });
993
+ await client.call("POST", "/api/sei/data/removeAllCache", {}, { allowFailure: false });
994
+ printJson({
995
+ success: true,
996
+ code: 1,
997
+ message: "基础源数据初始化完成",
998
+ data: {
999
+ executed: executed.length,
1000
+ sqlFile,
1001
+ admin: readOption(argv, "--admin-uid") ?? "admin",
1002
+ role: readOption(argv, "--admin-role") ?? "admin",
1003
+ baseUrl: settings.baseUrl
1004
+ }
1005
+ });
1006
+ return;
1007
+ }
706
1008
  default: throw new SeiMcpError(`未知 CLI 子命令:${command}`, "INVALID_PARAMS");
707
1009
  }
708
1010
  }
@@ -716,6 +1018,10 @@ function renderCliHelp() {
716
1018
  ` ${BIN_NAME} cli save [--json <text> | --file <path> | --stdin]`,
717
1019
  ` ${BIN_NAME} cli query-sql "<sql>" [--page 1] [--size 20]`,
718
1020
  ` ${BIN_NAME} cli api-docs [--keyword <text>] [--format markdown|json] [--output <path>]`,
1021
+ ` ${BIN_NAME} cli dict-list [--type <type>] [--keyword <text>] [--size 1000] [--flat]`,
1022
+ ` ${BIN_NAME} cli dict-add-category <type_code> <name> [--code <code>]`,
1023
+ ` ${BIN_NAME} cli dict-add-item <type_code> <code> <name> [--parent <uuid>]`,
1024
+ ` ${BIN_NAME} cli init-base-data [--sysid sys] [--admin-uid admin]`,
719
1025
  "",
720
1026
  "Common Options:",
721
1027
  " --base-url <url>",
@@ -727,11 +1033,29 @@ function renderCliHelp() {
727
1033
  " --token <token>",
728
1034
  " --timeout <seconds>",
729
1035
  " --allow-failure",
1036
+ " --sysid <sysid>",
1037
+ " --uuid <uuid>",
1038
+ " --ctype <ctype>",
1039
+ " --ename <ename>",
1040
+ " --sort <number>",
1041
+ " --body <json-or-text>",
1042
+ " --memo <text>",
1043
+ " --dict-flag <0|1>",
730
1044
  "",
731
1045
  "call Options:",
732
1046
  " --no-token",
733
1047
  " --allow-empty",
734
1048
  "",
1049
+ "init-base-data Options:",
1050
+ ` --admin-uid <uid> default ${BASE_SEED_DEFAULT_ADMIN_UID}`,
1051
+ " --admin-name <name> default 管理员",
1052
+ ` --admin-role <role> default ${BASE_SEED_DEFAULT_ADMIN_ROLE}`,
1053
+ " --admin-role-name <name> default 管理员",
1054
+ ` --admin-password <pwd> default ${BASE_SEED_DEFAULT_ADMIN_PASSWORD}`,
1055
+ ` --sysid <sysid> default sys`,
1056
+ " --reset-admin-password",
1057
+ " --sql-file <path>",
1058
+ "",
735
1059
  "Payload Options:",
736
1060
  " --json <text>",
737
1061
  " --file <path>",
@@ -797,6 +1121,13 @@ function readOptionNumber(argv, name) {
797
1121
  if (!Number.isFinite(parsed) || parsed <= 0) throw new SeiMcpError(`${name} 必须是正数`, "INVALID_PARAMS");
798
1122
  return parsed;
799
1123
  }
1124
+ function readOptionInteger(argv, name) {
1125
+ const value = readOption(argv, name);
1126
+ if (value === null) return;
1127
+ const parsed = Number.parseInt(value, 10);
1128
+ if (!Number.isFinite(parsed)) throw new SeiMcpError(`${name} 必须是整数`, "INVALID_PARAMS");
1129
+ return parsed;
1130
+ }
800
1131
  function firstPositional(argv) {
801
1132
  for (const item of argv) if (item && !item.startsWith("-")) return item;
802
1133
  return null;
@@ -824,6 +1155,13 @@ function isHelpFlag(value) {
824
1155
  function toUndefined(value) {
825
1156
  return value ?? void 0;
826
1157
  }
1158
+ function resolveBaseSeedSqlPath(path) {
1159
+ if (path) return resolve(path);
1160
+ return resolve(process.cwd(), ".codex/skills/sei-ai/scripts/init-base-data.sql");
1161
+ }
1162
+ function isObject$1(value) {
1163
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1164
+ }
827
1165
  async function readStdin() {
828
1166
  const chunks = [];
829
1167
  for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -836,10 +1174,10 @@ async function loadConfig(argv = process.argv.slice(2), env = process.env) {
836
1174
  return normalizeConfig(configPath ? await readConfigFile(configPath) : {}, env);
837
1175
  }
838
1176
  function normalizeConfig(input = {}, env = process.env) {
839
- const config = isObject$3(input) ? input : {};
840
- const auth = isObject$3(config.auth) ? config.auth : {};
841
- const tools = isObject$3(config.tools) ? config.tools : {};
842
- const targets = isObject$3(config.targets) ? config.targets : {};
1177
+ const config = isObject$4(input) ? input : {};
1178
+ const auth = isObject$4(config.auth) ? config.auth : {};
1179
+ const tools = isObject$4(config.tools) ? config.tools : {};
1180
+ const targets = isObject$4(config.targets) ? config.targets : {};
843
1181
  return {
844
1182
  baseUrl: normalizeBaseUrl(config.baseUrl, env.SEI_BASE_URL),
845
1183
  auth: {
@@ -896,8 +1234,8 @@ function stringOrEnv(value, envValue) {
896
1234
  return toTrimmedString$1(envValue);
897
1235
  }
898
1236
  function toTargetMap(value) {
899
- if (!isObject$3(value)) return {};
900
- return Object.fromEntries(Object.entries(value).filter(([, item]) => isObject$3(item)));
1237
+ if (!isObject$4(value)) return {};
1238
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => isObject$4(item)));
901
1239
  }
902
1240
  //#endregion
903
1241
  //#region src/logger.ts
@@ -5929,7 +6267,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
5929
6267
  return (payload, ctx) => fn(shape, payload, ctx);
5930
6268
  };
5931
6269
  let fastpass;
5932
- const isObject$4 = isObject;
6270
+ const isObject$6 = isObject;
5933
6271
  const jit = !globalConfig.jitless;
5934
6272
  const fastEnabled = jit && allowsEval.value;
5935
6273
  const catchall = def.catchall;
@@ -5937,7 +6275,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
5937
6275
  inst._zod.parse = (payload, ctx) => {
5938
6276
  value ?? (value = _normalized.value);
5939
6277
  const input = payload.value;
5940
- if (!isObject$4(input)) {
6278
+ if (!isObject$6(input)) {
5941
6279
  payload.issues.push({
5942
6280
  expected: "object",
5943
6281
  code: "invalid_type",
@@ -21222,7 +21560,7 @@ function authorizeQuery(config, input) {
21222
21560
  return {
21223
21561
  head,
21224
21562
  option: {
21225
- ...isObject$3(input.option) ? input.option : {},
21563
+ ...isObject$4(input.option) ? input.option : {},
21226
21564
  fields: effectiveFields.join(","),
21227
21565
  limit: toPositiveInteger(input.option?.limit, 20)
21228
21566
  },
@@ -21244,7 +21582,7 @@ function authorizeSave(config, input) {
21244
21582
  const action = normalizeAction(item?.action);
21245
21583
  if (!allowedActions.includes(action)) throw new SeiMcpError(`save 动作不在白名单内: ${action}`, "INVALID_PARAMS");
21246
21584
  for (const row of Array.isArray(item?.rows) ? item.rows : []) {
21247
- const current = isObject$3(row?.row) ? row.row : {};
21585
+ const current = isObject$4(row?.row) ? row.row : {};
21248
21586
  ensureFieldsAllowed(Object.keys(current).filter((key) => !RESERVED_SAVE_ROW_KEYS.has(key)), allowedFields, "save");
21249
21587
  }
21250
21588
  }
@@ -21272,7 +21610,7 @@ function authorizeQuerySql(config, input) {
21272
21610
  };
21273
21611
  }
21274
21612
  function requireHead(head) {
21275
- if (!isObject$3(head) || Array.isArray(head)) throw new SeiMcpError("head is required", "INVALID_PARAMS");
21613
+ if (!isObject$4(head) || Array.isArray(head)) throw new SeiMcpError("head is required", "INVALID_PARAMS");
21276
21614
  const result = {};
21277
21615
  for (const [key, value] of Object.entries(head)) {
21278
21616
  const trimmed = toTrimmedString$1(value);
@@ -21312,7 +21650,7 @@ function isTargetAllowed(config, tableName) {
21312
21650
  ];
21313
21651
  for (const pool of pools) {
21314
21652
  if (pool[normalized]) return true;
21315
- for (const target of Object.values(pool)) if (isObject$3(target)) {
21653
+ for (const target of Object.values(pool)) if (isObject$4(target)) {
21316
21654
  const mainTable = toTrimmedString$1(target.mainTable);
21317
21655
  if (mainTable && mainTable === normalized) return true;
21318
21656
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ty_krystal/sei-ai",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "SEI MCP server and developer CLI for query, save, query-sql, and api-docs workflows",
5
5
  "keywords": [
6
6
  "sei",