@ty_krystal/sei-ai 0.1.1 → 0.1.3

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
@@ -63,6 +63,13 @@ sei-ai cli api-docs --keyword login --format json
63
63
  - `--token`
64
64
  - `--timeout`
65
65
 
66
+ 环境变量加载规则:
67
+
68
+ - 启动时会自动读取当前工作目录下的 `.env.local` 和 `.env`
69
+ - 加载顺序为 `.env.local` 再 `.env`
70
+ - 已经存在的进程环境变量不会被 `.env` 覆盖
71
+ - 显式 CLI 参数仍然优先于环境变量
72
+
66
73
  载荷输入支持:
67
74
 
68
75
  - `--json '<payload>'`
@@ -123,6 +130,14 @@ docker compose -f apps/sei-ai-mcp/docker-compose.yml up -d --build
123
130
  - `SEI_MCP_PATH`
124
131
  - `SEI_MCP_HTTP_JSON_RESPONSE`
125
132
 
133
+ 例如可以在项目目录写:
134
+
135
+ ```bash
136
+ SEI_BASE_URL=http://127.0.0.1:8081
137
+ SEI_AI_LOGIN_KEY=dev-ai-secret
138
+ SEI_AI_LOGIN_ROLE=admin
139
+ ```
140
+
126
141
  ## 权限模型
127
142
 
128
143
  - 未配置目标时默认拒绝。
@@ -209,3 +224,9 @@ SEI_AI_MCP_HTTP_ENDPOINT=/mcp
209
224
  ```bash
210
225
  http://127.0.0.1:3719/mcp
211
226
  ```
227
+
228
+
229
+
230
+ npm version patch --no-git-tag-version
231
+ pnpm run build
232
+ npm publish --access public --cache /tmp/sei-ai-npm-cache
package/dist/README.md CHANGED
@@ -63,6 +63,13 @@ sei-ai cli api-docs --keyword login --format json
63
63
  - `--token`
64
64
  - `--timeout`
65
65
 
66
+ 环境变量加载规则:
67
+
68
+ - 启动时会自动读取当前工作目录下的 `.env.local` 和 `.env`
69
+ - 加载顺序为 `.env.local` 再 `.env`
70
+ - 已经存在的进程环境变量不会被 `.env` 覆盖
71
+ - 显式 CLI 参数仍然优先于环境变量
72
+
66
73
  载荷输入支持:
67
74
 
68
75
  - `--json '<payload>'`
@@ -123,6 +130,14 @@ docker compose -f apps/sei-ai-mcp/docker-compose.yml up -d --build
123
130
  - `SEI_MCP_PATH`
124
131
  - `SEI_MCP_HTTP_JSON_RESPONSE`
125
132
 
133
+ 例如可以在项目目录写:
134
+
135
+ ```bash
136
+ SEI_BASE_URL=http://127.0.0.1:8081
137
+ SEI_AI_LOGIN_KEY=dev-ai-secret
138
+ SEI_AI_LOGIN_ROLE=admin
139
+ ```
140
+
126
141
  ## 权限模型
127
142
 
128
143
  - 未配置目标时默认拒绝。
@@ -209,3 +224,9 @@ SEI_AI_MCP_HTTP_ENDPOINT=/mcp
209
224
  ```bash
210
225
  http://127.0.0.1:3719/mcp
211
226
  ```
227
+
228
+
229
+
230
+ npm version patch --no-git-tag-version
231
+ pnpm run build
232
+ npm publish --access public --cache /tmp/sei-ai-npm-cache
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
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
+ import { parseEnv } from "node:util";
6
7
  import { createServer } from "node:http";
7
8
  import { ZodOptional, z } from "zod";
8
9
  import process$1 from "node:process";
@@ -49,6 +50,7 @@ var PACKAGE_NAME = "sei-ai";
49
50
  var BIN_NAME = "sei-ai";
50
51
  var SERVER_NAME = "sei-ai-mcp-server";
51
52
  var SERVER_VERSION = "0.2.0";
53
+ var DEFAULT_AI_KEY = "dev-ai-secret";
52
54
  var DEFAULT_TIMEOUT_MS = 3e4;
53
55
  var DEFAULT_LOG_LEVEL = "info";
54
56
  var MAX_RESPONSE_TEXT = 2e4;
@@ -58,7 +60,27 @@ var ACCOUNT_CHECK_CODE_PATH = "/api/sei/public/checkCode";
58
60
  var QUERY_PATH = "/api/sei/data/public/query";
59
61
  var SAVE_PATH = "/api/sei/data/public/save";
60
62
  var QUERY_SQL_PATH = "/api/sei/data/querySQL";
63
+ var DDL_EXECUTE_PATH = "/api/sei/mcp/ddl/execute";
61
64
  var OPENAPI_DOCS_PATH = "/v3/api-docs";
65
+ var DICT_MODULE = "sys_dic";
66
+ var DICT_FIELDS = [
67
+ "_UUID",
68
+ "_PID",
69
+ "_SYSID",
70
+ "_TYPE",
71
+ "_CTYPE",
72
+ "_CODE",
73
+ "_NAME",
74
+ "_ENAME",
75
+ "_DICT",
76
+ "_SORT",
77
+ "_BODY",
78
+ "_MEMO"
79
+ ];
80
+ var BASE_SEED_DEFAULT_ADMIN_UID = "admin";
81
+ var BASE_SEED_DEFAULT_ADMIN_ROLE = "admin";
82
+ var BASE_SEED_DEFAULT_ADMIN_PASSWORD = "123456";
83
+ var ACCOUNT_LOGIN_DEFAULT_CHECKCODE = "abcd";
62
84
  var ACCOUNT_LOGIN_DEFAULT_TYPE = "login";
63
85
  var TOKEN_CACHE_DIR_NAME = "sei-ai";
64
86
  var TOKEN_CACHE_FILE_PREFIX = "token-";
@@ -75,9 +97,235 @@ var SeiMcpError = class extends Error {
75
97
  this.details = details;
76
98
  }
77
99
  };
100
+ function isSeiMcpError(error) {
101
+ return error instanceof SeiMcpError;
102
+ }
103
+ function normalizeErrorMessage(error) {
104
+ return error instanceof Error ? error.message : "Unknown error";
105
+ }
106
+ function formatCliError(error) {
107
+ if (error instanceof SeiMcpError) return `[${error.code}] ${error.message}`;
108
+ return `[INTERNAL_ERROR] ${normalizeErrorMessage(error)}`;
109
+ }
110
+ function toErrorLogMeta(error) {
111
+ if (error instanceof SeiMcpError) return {
112
+ code: error.code,
113
+ message: error.message,
114
+ details: toJsonValue(error.details)
115
+ };
116
+ if (error instanceof Error) return {
117
+ message: error.message,
118
+ stack: error.stack ?? ""
119
+ };
120
+ return { message: normalizeErrorMessage(error) };
121
+ }
122
+ function toJsonValue(value) {
123
+ if (value === null) return null;
124
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
125
+ if (Array.isArray(value)) return value.map((item) => toJsonValue(item));
126
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, toJsonValue(item)]));
127
+ return String(value);
128
+ }
129
+ //#endregion
130
+ //#region src/cli-helpers.ts
131
+ function buildDictQueryPayload(typeCode, keyword, size = 1e3) {
132
+ return {
133
+ head: { module: DICT_MODULE },
134
+ option: {
135
+ keyField: true,
136
+ privilege: true,
137
+ fields: DICT_FIELDS.join(","),
138
+ filter: buildDictQueryFilter(typeCode, keyword),
139
+ order: "_SORT ASC,_CODE ASC",
140
+ page: 1,
141
+ size
142
+ }
143
+ };
144
+ }
145
+ function buildDictSavePayload(row) {
146
+ return {
147
+ head: { module: DICT_MODULE },
148
+ data: [{
149
+ action: "add",
150
+ option: { keyVal: true },
151
+ rows: [{ row }]
152
+ }]
153
+ };
154
+ }
155
+ function buildDictCategoryRow(input) {
156
+ const typeCode = requiredText(input.typeCode, "字典类别");
157
+ const name = requiredText(input.name, "字典名称");
158
+ const uuid = requiredText(input.uuid || typeCode, "字典编号");
159
+ const code = requiredText(input.code || typeCode, "字典代码");
160
+ return {
161
+ _UUID: uuid,
162
+ _PID: "",
163
+ _SYSID: requiredText(input.sysid || "sys", "所属系统"),
164
+ _TYPE: typeCode,
165
+ _CTYPE: normalizeText$1(input.ctype),
166
+ _CODE: code,
167
+ _NAME: name,
168
+ _ENAME: normalizeText$1(input.ename),
169
+ _DICT: normalizeDictFlag(input.dictFlag),
170
+ _SORT: normalizeSort(input.sort),
171
+ _BODY: normalizeText$1(input.body),
172
+ _MEMO: normalizeText$1(input.memo)
173
+ };
174
+ }
175
+ function buildDictItemRow(input) {
176
+ const typeCode = requiredText(input.typeCode, "字典类别");
177
+ const code = requiredText(input.code, "字典代码");
178
+ const name = requiredText(input.name, "字典名称");
179
+ const parent = requiredText(input.parent || typeCode, "父级编号");
180
+ return {
181
+ _UUID: requiredText(input.uuid || `${parent}_${code}`, "字典编号"),
182
+ _PID: parent,
183
+ _SYSID: requiredText(input.sysid || "sys", "所属系统"),
184
+ _TYPE: typeCode,
185
+ _CTYPE: normalizeText$1(input.ctype),
186
+ _CODE: code,
187
+ _NAME: name,
188
+ _ENAME: normalizeText$1(input.ename),
189
+ _DICT: normalizeDictFlag(input.dictFlag),
190
+ _SORT: normalizeSort(input.sort),
191
+ _BODY: normalizeText$1(input.body),
192
+ _MEMO: normalizeText$1(input.memo)
193
+ };
194
+ }
195
+ function buildDictTree(rows) {
196
+ if (!Array.isArray(rows)) return [];
197
+ const recordMap = /* @__PURE__ */ new Map();
198
+ const childrenMap = /* @__PURE__ */ new Map();
199
+ const roots = [];
200
+ for (const row of rows) {
201
+ if (!isObject$5(row)) continue;
202
+ const key = normalizeText$1(row._UUID);
203
+ if (!key) continue;
204
+ recordMap.set(key, { ...row });
205
+ }
206
+ for (const record of recordMap.values()) {
207
+ const parentId = normalizeText$1(record._PID);
208
+ if (!parentId || !recordMap.has(parentId)) {
209
+ roots.push(record);
210
+ continue;
211
+ }
212
+ const children = childrenMap.get(parentId) ?? [];
213
+ children.push(record);
214
+ childrenMap.set(parentId, children);
215
+ }
216
+ for (const [key, record] of recordMap.entries()) {
217
+ const children = childrenMap.get(key);
218
+ if (children && children.length > 0) record.children = children.sort(dictSortKey);
219
+ }
220
+ return roots.sort(dictSortKey);
221
+ }
222
+ function buildBaseSeedSqlVars(input) {
223
+ const adminPassword = input.adminPassword || "123456";
224
+ const passwordHash = createHash("md5").update(adminPassword, "utf8").digest("hex");
225
+ const adminUid = input.adminUid || "admin";
226
+ const adminRole = input.adminRole || "admin";
227
+ const resetSql = input.resetAdminPassword === true ? `UPDATE _SYS_USER\nSET _PWD = ${sqlLiteral(passwordHash)}\nWHERE _UID = ${sqlLiteral(adminUid)};` : "";
228
+ return {
229
+ SYSID: sqlLiteral(input.sysid || "sys"),
230
+ ADMIN_UID: sqlLiteral(adminUid),
231
+ ADMIN_NAME: sqlLiteral(input.adminName || "管理员"),
232
+ ADMIN_ROLE: sqlLiteral(adminRole),
233
+ ADMIN_ROLE_NAME: sqlLiteral(input.adminRoleName || "管理员"),
234
+ ADMIN_PASSWORD_MD5: sqlLiteral(passwordHash),
235
+ RESET_ADMIN_PASSWORD_SQL: resetSql
236
+ };
237
+ }
238
+ function renderBaseSeedSql(template, values) {
239
+ let rendered = template;
240
+ for (const [key, value] of Object.entries(values)) rendered = rendered.replaceAll(`{{${key}}}`, value);
241
+ const missing = [...rendered.matchAll(/\{\{[A-Z0-9_]+\}\}/g)].map((item) => item[0]);
242
+ if (missing.length > 0) throw new SeiMcpError(`初始化 SQL 存在未替换变量:${[...new Set(missing)].join(", ")}`, "INVALID_PARAMS");
243
+ return rendered;
244
+ }
245
+ function removeSqlCommentLines(sql) {
246
+ return sql.split("\n").filter((line) => !line.trimStart().startsWith("--")).join("\n");
247
+ }
248
+ function splitSqlStatements(sql) {
249
+ const statements = [];
250
+ let current = "";
251
+ let quote = null;
252
+ let escaped = false;
253
+ for (const char of sql) {
254
+ if (escaped) {
255
+ current += char;
256
+ escaped = false;
257
+ continue;
258
+ }
259
+ if (char === "\\" && quote !== null) {
260
+ current += char;
261
+ escaped = true;
262
+ continue;
263
+ }
264
+ if (quote !== null) {
265
+ current += char;
266
+ if (char === quote) quote = null;
267
+ continue;
268
+ }
269
+ if (char === "'" || char === "\"" || char === "`") {
270
+ current += char;
271
+ quote = char;
272
+ continue;
273
+ }
274
+ if (char === ";") {
275
+ const statement = current.trim();
276
+ if (statement) statements.push(statement);
277
+ current = "";
278
+ continue;
279
+ }
280
+ current += char;
281
+ }
282
+ const finalStatement = current.trim();
283
+ if (finalStatement) statements.push(finalStatement);
284
+ return statements;
285
+ }
286
+ function buildDictQueryFilter(typeCode, keyword) {
287
+ const filters = [];
288
+ const normalizedType = normalizeText$1(typeCode);
289
+ const normalizedKeyword = normalizeText$1(keyword);
290
+ if (normalizedType) filters.push({ _TYPE: { "=": normalizedType } });
291
+ if (normalizedKeyword) filters.push({ OR: [
292
+ { _UUID: { "*LIKE*": normalizedKeyword } },
293
+ { _TYPE: { "*LIKE*": normalizedKeyword } },
294
+ { _CODE: { "*LIKE*": normalizedKeyword } },
295
+ { _NAME: { "*LIKE*": normalizedKeyword } }
296
+ ] });
297
+ return filters;
298
+ }
299
+ function dictSortKey(a, b) {
300
+ const sortA = normalizeSort(typeof a._SORT === "number" ? a._SORT : Number(a._SORT ?? 0));
301
+ const sortB = normalizeSort(typeof b._SORT === "number" ? b._SORT : Number(b._SORT ?? 0));
302
+ if (sortA !== sortB) return sortA - sortB;
303
+ return normalizeText$1(a._CODE).localeCompare(normalizeText$1(b._CODE), "zh-CN");
304
+ }
305
+ function normalizeDictFlag(value) {
306
+ return value === 1 ? 1 : 0;
307
+ }
308
+ function normalizeSort(value) {
309
+ return Number.isInteger(value) && Number(value) >= 0 ? Number(value) : 100;
310
+ }
311
+ function requiredText(value, label) {
312
+ const normalized = normalizeText$1(value);
313
+ if (!normalized) throw new SeiMcpError(`${label} 不能为空`, "INVALID_PARAMS");
314
+ return normalized;
315
+ }
316
+ function normalizeText$1(value) {
317
+ return typeof value === "string" ? value.trim() : "";
318
+ }
319
+ function sqlLiteral(value) {
320
+ if (value === null || value === void 0) return "NULL";
321
+ return `'${String(value).replace(/'/g, "''")}'`;
322
+ }
323
+ function isObject$5(value) {
324
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
325
+ }
78
326
  //#endregion
79
327
  //#region src/utils.ts
80
- function isObject$3(value) {
328
+ function isObject$4(value) {
81
329
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
82
330
  }
83
331
  function toTrimmedString$1(value) {
@@ -143,12 +391,12 @@ function createSeiClient(config, logger, fetchImpl = fetch) {
143
391
  function createSeiAuthSettings(config, options = {}) {
144
392
  return {
145
393
  baseUrl: normalizeBaseUrl$1(options.baseUrl ?? config.baseUrl),
146
- token: toText(options.token ?? config.auth.token ?? process.env.SEI_TOKEN),
147
- aiKey: toText(options.aiKey ?? config.auth.aiKey ?? process.env.SEI_AI_LOGIN_KEY ?? "dev-ai-secret"),
394
+ token: firstText(options.token, config.auth.token, process.env.SEI_TOKEN),
395
+ aiKey: firstText(options.aiKey, config.auth.aiKey, process.env.SEI_AI_LOGIN_KEY, DEFAULT_AI_KEY),
148
396
  role: resolveRole(options.role, config.auth.role),
149
- account: toText(options.account ?? config.auth.account ?? process.env.SEI_AI_LOGIN_ACCOUNT),
150
- accountProject: toText(options.accountProject ?? process.env.SEI_AI_LOGIN_ACCOUNT_PROJECT ?? "sys"),
151
- accountCheckcode: toText(options.accountCheckcode ?? process.env.SEI_AI_LOGIN_ACCOUNT_CHECKCODE ?? "abcd") || "abcd",
397
+ account: firstText(options.account, config.auth.account, process.env.SEI_AI_LOGIN_ACCOUNT),
398
+ accountProject: firstText(options.accountProject, process.env.SEI_AI_LOGIN_ACCOUNT_PROJECT, "sys"),
399
+ accountCheckcode: firstText(options.accountCheckcode, process.env.SEI_AI_LOGIN_ACCOUNT_CHECKCODE, ACCOUNT_LOGIN_DEFAULT_CHECKCODE),
152
400
  timeoutMs: normalizeTimeout(options.timeoutMs)
153
401
  };
154
402
  }
@@ -169,6 +417,17 @@ function createCliSeiClient(settings, logger, fetchImpl = fetch) {
169
417
  },
170
418
  async readApiDocs() {
171
419
  return requestJsonRaw(settings, logger, fetchImpl, "GET", OPENAPI_DOCS_PATH, void 0, { skipAuth: true });
420
+ },
421
+ async executeDdlStatements(statements, options = {}) {
422
+ const results = [];
423
+ for (const [index, statement] of statements.entries()) {
424
+ const response = await requestSeiJson(settings, logger, fetchImpl, auth, "POST", DDL_EXECUTE_PATH, { ddl: statement }, options);
425
+ results.push({
426
+ index: index + 1,
427
+ response
428
+ });
429
+ }
430
+ return results;
172
431
  }
173
432
  };
174
433
  }
@@ -333,7 +592,7 @@ async function fetchAccountLoginRow(settings, logger, fetchImpl, adminToken, acc
333
592
  const rows = extractResponseRows(response);
334
593
  if (rows.length === 0) throw new SeiMcpError(`未找到账号:${account}`, "INVALID_REQUEST");
335
594
  const row = rows[0];
336
- if (!isObject$2(row)) throw new SeiMcpError(`账号 ${account} 查询结果不是 JSON 对象`, "INVALID_REQUEST");
595
+ if (!isObject$3(row)) throw new SeiMcpError(`账号 ${account} 查询结果不是 JSON 对象`, "INVALID_REQUEST");
337
596
  return row;
338
597
  }
339
598
  async function requestCookieHeader(settings, logger, fetchImpl, payload) {
@@ -370,19 +629,19 @@ async function resolveRequestToken(settings, auth, options) {
370
629
  }
371
630
  function validateSeiEnvelope(response, context, allowFailure, allowEmpty) {
372
631
  if (response === null && allowEmpty) return;
373
- if (!isObject$2(response)) throw new SeiMcpError(`${context} 响应不是 JSON 对象`, "INVALID_REQUEST");
632
+ if (!isObject$3(response)) throw new SeiMcpError(`${context} 响应不是 JSON 对象`, "INVALID_REQUEST");
374
633
  if (!allowFailure && isSeiFailure(response)) throw new SeiMcpError(`${context} 失败:${String(response.message ?? response.msg ?? response.code ?? "未知错误")}`, "INVALID_REQUEST", { response: safeResponse(response) });
375
634
  }
376
635
  function extractToken(response) {
377
636
  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");
637
+ 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
638
  return response.data.token.trim();
380
639
  }
381
640
  function extractResponseRows(response) {
382
- if (!isObject$2(response)) return [];
641
+ if (!isObject$3(response)) return [];
383
642
  const data = response.data;
384
643
  if (Array.isArray(data)) return data;
385
- if (isObject$2(data)) for (const key of [
644
+ if (isObject$3(data)) for (const key of [
386
645
  "rows",
387
646
  "records",
388
647
  "list",
@@ -456,24 +715,24 @@ function escapeSqlLiteral(value) {
456
715
  return String(value).replace(/'/g, "''");
457
716
  }
458
717
  function isUnauthenticatedResponse(response) {
459
- if (!isObject$2(response)) return false;
718
+ if (!isObject$3(response)) return false;
460
719
  return Number(response.code) === -2;
461
720
  }
462
721
  function isSeiFailure(response) {
463
- if (!isObject$2(response)) return false;
722
+ if (!isObject$3(response)) return false;
464
723
  if (response.success === false) return true;
465
724
  const code = Number(response.code);
466
725
  return Number.isFinite(code) && code < 1;
467
726
  }
468
727
  function safeResponse(response) {
469
- if (!isObject$2(response)) return response;
728
+ if (!isObject$3(response)) return response;
470
729
  return {
471
730
  code: response.code,
472
731
  success: response.success,
473
732
  message: response.message
474
733
  };
475
734
  }
476
- function isObject$2(value) {
735
+ function isObject$3(value) {
477
736
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
478
737
  }
479
738
  function normalizeTimeout(value) {
@@ -484,9 +743,14 @@ function normalizeTimeout(value) {
484
743
  return DEFAULT_TIMEOUT_MS;
485
744
  }
486
745
  function resolveRole(role, fallback) {
487
- const primary = toText(role);
488
- if (primary) return primary.toLowerCase();
489
- return toText(fallback ?? process.env.SEI_AI_LOGIN_ROLE).toLowerCase();
746
+ return firstText(role, fallback, process.env.SEI_AI_LOGIN_ROLE).toLowerCase();
747
+ }
748
+ function firstText(...values) {
749
+ for (const value of values) {
750
+ const text = toText(value);
751
+ if (text) return text;
752
+ }
753
+ return "";
490
754
  }
491
755
  function toText(value) {
492
756
  return typeof value === "string" ? value.trim() : "";
@@ -504,17 +768,17 @@ var OPENAPI_HTTP_METHODS = [
504
768
  "TRACE"
505
769
  ];
506
770
  function buildOpenApiDocSummary(openapiDoc, keyword) {
507
- if (!isObject$1(openapiDoc)) throw new Error("OpenAPI 文档不是 JSON 对象");
508
- const info = isObject$1(openapiDoc.info) ? openapiDoc.info : {};
771
+ if (!isObject$2(openapiDoc)) throw new SeiMcpError("OpenAPI 文档不是 JSON 对象", "INVALID_REQUEST");
772
+ const info = isObject$2(openapiDoc.info) ? openapiDoc.info : {};
509
773
  const rawPaths = openapiDoc.paths;
510
- if (!isObject$1(rawPaths)) throw new Error("OpenAPI 文档缺少 paths 对象");
774
+ if (!isObject$2(rawPaths)) throw new SeiMcpError("OpenAPI 文档缺少 paths 对象", "INVALID_REQUEST");
511
775
  const normalizedKeyword = normalizeText(keyword).toLowerCase();
512
776
  const items = [];
513
777
  for (const [path, rawPathItem] of Object.entries(rawPaths)) {
514
- if (!isObject$1(rawPathItem)) continue;
778
+ if (!isObject$2(rawPathItem)) continue;
515
779
  for (const [method, rawOperation] of Object.entries(rawPathItem)) {
516
780
  const methodUpper = method.toUpperCase();
517
- if (!OPENAPI_HTTP_METHODS.includes(methodUpper) || !isObject$1(rawOperation)) continue;
781
+ if (!OPENAPI_HTTP_METHODS.includes(methodUpper) || !isObject$2(rawOperation)) continue;
518
782
  const tags = Array.isArray(rawOperation.tags) ? rawOperation.tags.map((item) => normalizeText(item)).filter(Boolean) : [];
519
783
  const item = {
520
784
  method: methodUpper,
@@ -559,7 +823,7 @@ function renderOpenApiMarkdown(summary) {
559
823
  let currentTag = "";
560
824
  const items = Array.isArray(summary.items) ? summary.items : [];
561
825
  for (const rawItem of items) {
562
- if (!isObject$1(rawItem)) continue;
826
+ if (!isObject$2(rawItem)) continue;
563
827
  const tag = normalizeText((Array.isArray(rawItem.tags) ? rawItem.tags : [])[0] ?? "未分组") || "未分组";
564
828
  if (tag !== currentTag) {
565
829
  lines.push("", `## ${tag}`);
@@ -578,7 +842,7 @@ function renderOpenApiMarkdown(summary) {
578
842
  if (parameters.length > 0) {
579
843
  lines.push("- 参数:");
580
844
  for (const rawParameter of parameters) {
581
- if (!isObject$1(rawParameter)) continue;
845
+ if (!isObject$2(rawParameter)) continue;
582
846
  const name = normalizeText(rawParameter.name);
583
847
  const location = normalizeText(rawParameter.in);
584
848
  const detail = [
@@ -591,7 +855,7 @@ function renderOpenApiMarkdown(summary) {
591
855
  }
592
856
  const requestBody = Array.isArray(rawItem.requestBody) ? rawItem.requestBody : [];
593
857
  if (requestBody.length > 0) {
594
- const parts = requestBody.filter(isObject$1).map((body) => {
858
+ const parts = requestBody.filter(isObject$2).map((body) => {
595
859
  const contentType = normalizeText(body.contentType);
596
860
  const schema = normalizeText(body.schema);
597
861
  return schema ? `${contentType}(${schema})` : contentType;
@@ -600,7 +864,7 @@ function renderOpenApiMarkdown(summary) {
600
864
  }
601
865
  const responses = Array.isArray(rawItem.responses) ? rawItem.responses : [];
602
866
  if (responses.length > 0) {
603
- const parts = responses.filter(isObject$1).map((response) => `${normalizeText(response.status)} ${normalizeText(response.description)}`.trim()).filter(Boolean);
867
+ const parts = responses.filter(isObject$2).map((response) => `${normalizeText(response.status)} ${normalizeText(response.description)}`.trim()).filter(Boolean);
604
868
  if (parts.length > 0) lines.push(`- 响应:${parts.join(", ")}`);
605
869
  }
606
870
  }
@@ -609,7 +873,7 @@ function renderOpenApiMarkdown(summary) {
609
873
  function extractOpenApiParameters(operation) {
610
874
  const rawParameters = operation.parameters;
611
875
  if (!Array.isArray(rawParameters)) return [];
612
- return rawParameters.filter(isObject$1).map((parameter) => ({
876
+ return rawParameters.filter(isObject$2).map((parameter) => ({
613
877
  name: normalizeText(parameter.name),
614
878
  in: normalizeText(parameter.in),
615
879
  required: Boolean(parameter.required),
@@ -619,22 +883,22 @@ function extractOpenApiParameters(operation) {
619
883
  }
620
884
  function extractOpenApiRequestBody(operation) {
621
885
  const requestBody = operation.requestBody;
622
- if (!isObject$1(requestBody) || !isObject$1(requestBody.content)) return [];
886
+ if (!isObject$2(requestBody) || !isObject$2(requestBody.content)) return [];
623
887
  return Object.entries(requestBody.content).map(([contentType, contentInfo]) => ({
624
888
  contentType: normalizeText(contentType),
625
- schema: summarizeSchema(isObject$1(contentInfo) ? contentInfo.schema : void 0)
889
+ schema: summarizeSchema(isObject$2(contentInfo) ? contentInfo.schema : void 0)
626
890
  })).filter((item) => item.contentType);
627
891
  }
628
892
  function extractOpenApiResponses(operation) {
629
893
  const rawResponses = operation.responses;
630
- if (!isObject$1(rawResponses)) return [];
894
+ if (!isObject$2(rawResponses)) return [];
631
895
  return Object.entries(rawResponses).map(([status, response]) => ({
632
896
  status: normalizeText(status),
633
- description: normalizeText(isObject$1(response) ? response.description : "")
897
+ description: normalizeText(isObject$2(response) ? response.description : "")
634
898
  })).filter((item) => item.status);
635
899
  }
636
900
  function summarizeSchema(schema) {
637
- if (!isObject$1(schema)) return "";
901
+ if (!isObject$2(schema)) return "";
638
902
  const ref = normalizeText(schema.$ref);
639
903
  if (ref) return ref.split("/").pop() ?? ref;
640
904
  const schemaType = normalizeText(schema.type);
@@ -647,63 +911,147 @@ function summarizeSchema(schema) {
647
911
  function normalizeText(value) {
648
912
  return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
649
913
  }
650
- function isObject$1(value) {
914
+ function isObject$2(value) {
651
915
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
652
916
  }
653
917
  //#endregion
654
918
  //#region src/cli.ts
655
919
  async function runCli(argv, options) {
656
- const command = firstPositional(argv);
657
- if (!command || isHelpFlag(command) || hasFlag(argv, "--help") || hasFlag(argv, "-h")) {
658
- process.stdout.write(`${renderCliHelp()}\n`);
659
- return;
660
- }
661
- const client = createCliSeiClient(createSeiAuthSettings(options.config, {
662
- baseUrl: toUndefined(readOption(argv, "--base-url")),
663
- token: toUndefined(readOption(argv, "--token")),
664
- aiKey: toUndefined(readOption(argv, "--ai-key")),
665
- role: toUndefined(readOption(argv, "--role") ?? readOption(argv, "--profile")),
666
- account: toUndefined(readOption(argv, "--account")),
667
- accountProject: toUndefined(readOption(argv, "--account-project")),
668
- accountCheckcode: toUndefined(readOption(argv, "--account-checkcode")),
669
- timeoutMs: readOptionNumber(argv, "--timeout")
670
- }), options.logger, options.fetchImpl ?? fetch);
671
- switch (command) {
672
- case "api-docs": {
673
- const summary = buildOpenApiDocSummary(await client.readApiDocs(), readOption(argv, "--keyword") ?? void 0);
674
- const output = normalizeFormat(readOption(argv, "--format")) === "json" ? `${JSON.stringify(summary, null, 2)}\n` : renderOpenApiMarkdown(summary);
675
- await writeOutput(readOption(argv, "--output"), output);
676
- return;
677
- }
678
- case "call": {
679
- const [method, path] = positionalAfter(argv, command, 2);
680
- if (!method || !path) throw new SeiMcpError("call 需要 METHOD 和 PATH", "INVALID_PARAMS");
681
- const payload = await loadPayload(argv, {});
682
- printJson(await client.call(method, path, payload, {
683
- allowFailure: hasFlag(argv, "--allow-failure"),
684
- allowEmpty: hasFlag(argv, "--allow-empty"),
685
- skipAuth: hasFlag(argv, "--no-token")
686
- }));
687
- return;
688
- }
689
- case "query": {
690
- const payload = await loadPayload(argv, {});
691
- printJson(await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
692
- return;
693
- }
694
- case "save": {
695
- const payload = await loadPayload(argv, {});
696
- printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
920
+ try {
921
+ const command = firstPositional(argv);
922
+ if (!command || isHelpFlag(command) || hasFlag(argv, "--help") || hasFlag(argv, "-h")) {
923
+ process.stdout.write(`${renderCliHelp()}\n`);
697
924
  return;
698
925
  }
699
- case "query-sql":
700
- case "sql": {
701
- const [sql] = positionalAfter(argv, command, 1);
702
- if (!sql) throw new SeiMcpError("query-sql 需要 SQL 参数", "INVALID_PARAMS");
703
- printJson(await client.querySql(sql, readOptionNumber(argv, "--page"), readOptionNumber(argv, "--size"), { allowFailure: hasFlag(argv, "--allow-failure") }));
704
- return;
926
+ const settings = createSeiAuthSettings(options.config, {
927
+ baseUrl: toUndefined(readOption(argv, "--base-url")),
928
+ token: toUndefined(readOption(argv, "--token")),
929
+ aiKey: toUndefined(readOption(argv, "--ai-key")),
930
+ role: toUndefined(readOption(argv, "--role") ?? readOption(argv, "--profile")),
931
+ account: toUndefined(readOption(argv, "--account")),
932
+ accountProject: toUndefined(readOption(argv, "--account-project")),
933
+ accountCheckcode: toUndefined(readOption(argv, "--account-checkcode")),
934
+ timeoutMs: readOptionNumber(argv, "--timeout")
935
+ });
936
+ const client = createCliSeiClient(settings, options.logger, options.fetchImpl ?? fetch);
937
+ switch (command) {
938
+ case "api-docs": {
939
+ const summary = buildOpenApiDocSummary(await client.readApiDocs(), readOption(argv, "--keyword") ?? void 0);
940
+ const output = normalizeFormat(readOption(argv, "--format")) === "json" ? `${JSON.stringify(summary, null, 2)}\n` : renderOpenApiMarkdown(summary);
941
+ await writeOutput(readOption(argv, "--output"), output);
942
+ return;
943
+ }
944
+ case "call": {
945
+ const [method, path] = positionalAfter(argv, command, 2);
946
+ if (!method || !path) throw new SeiMcpError("call 需要 METHOD 和 PATH", "INVALID_PARAMS");
947
+ const payload = await loadPayload(argv, {});
948
+ printJson(await client.call(method, path, payload, {
949
+ allowFailure: hasFlag(argv, "--allow-failure"),
950
+ allowEmpty: hasFlag(argv, "--allow-empty"),
951
+ skipAuth: hasFlag(argv, "--no-token")
952
+ }));
953
+ return;
954
+ }
955
+ case "query": {
956
+ const payload = await loadPayload(argv, {});
957
+ printJson(await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
958
+ return;
959
+ }
960
+ case "save": {
961
+ const payload = await loadPayload(argv, {});
962
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
963
+ return;
964
+ }
965
+ case "query-sql":
966
+ case "sql": {
967
+ const [sql] = positionalAfter(argv, command, 1);
968
+ if (!sql) throw new SeiMcpError("query-sql 需要 SQL 参数", "INVALID_PARAMS");
969
+ printJson(await client.querySql(sql, readOptionNumber(argv, "--page"), readOptionNumber(argv, "--size"), { allowFailure: hasFlag(argv, "--allow-failure") }));
970
+ return;
971
+ }
972
+ case "dict-list": {
973
+ const payload = buildDictQueryPayload(readOption(argv, "--type") ?? void 0, readOption(argv, "--keyword") ?? void 0, readOptionNumber(argv, "--size") ?? 1e3);
974
+ const response = await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") });
975
+ if (!hasFlag(argv, "--flat") && isObject$1(response) && isObject$1(response.data) && Array.isArray(response.data.rows)) response.data.tree = buildDictTree(response.data.rows);
976
+ printJson(response);
977
+ return;
978
+ }
979
+ case "dict-add-category": {
980
+ const [typeCode, name] = positionalAfter(argv, command, 2);
981
+ if (!typeCode || !name) throw new SeiMcpError("dict-add-category 需要 type_code 和 name", "INVALID_PARAMS");
982
+ const payload = buildDictSavePayload(buildDictCategoryRow({
983
+ body: readOption(argv, "--body") ?? void 0,
984
+ code: readOption(argv, "--code") ?? void 0,
985
+ ctype: readOption(argv, "--ctype") ?? void 0,
986
+ dictFlag: readOptionInteger(argv, "--dict-flag"),
987
+ ename: readOption(argv, "--ename") ?? void 0,
988
+ memo: readOption(argv, "--memo") ?? void 0,
989
+ name,
990
+ sort: readOptionInteger(argv, "--sort"),
991
+ sysid: readOption(argv, "--sysid") ?? void 0,
992
+ typeCode,
993
+ uuid: readOption(argv, "--uuid") ?? void 0
994
+ }));
995
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
996
+ return;
997
+ }
998
+ case "dict-add-item": {
999
+ const [typeCode, code, name] = positionalAfter(argv, command, 3);
1000
+ if (!typeCode || !code || !name) throw new SeiMcpError("dict-add-item 需要 type_code、code 和 name", "INVALID_PARAMS");
1001
+ const payload = buildDictSavePayload(buildDictItemRow({
1002
+ body: readOption(argv, "--body") ?? void 0,
1003
+ code,
1004
+ ctype: readOption(argv, "--ctype") ?? void 0,
1005
+ dictFlag: readOptionInteger(argv, "--dict-flag"),
1006
+ ename: readOption(argv, "--ename") ?? void 0,
1007
+ memo: readOption(argv, "--memo") ?? void 0,
1008
+ name,
1009
+ parent: readOption(argv, "--parent") ?? void 0,
1010
+ sort: readOptionInteger(argv, "--sort"),
1011
+ sysid: readOption(argv, "--sysid") ?? void 0,
1012
+ typeCode,
1013
+ uuid: readOption(argv, "--uuid") ?? void 0
1014
+ }));
1015
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
1016
+ return;
1017
+ }
1018
+ case "init-base-data": {
1019
+ const sqlFile = resolveBaseSeedSqlPath(readOption(argv, "--sql-file"));
1020
+ const statements = splitSqlStatements(removeSqlCommentLines(renderBaseSeedSql(await readFile(sqlFile, "utf8"), buildBaseSeedSqlVars({
1021
+ adminName: readOption(argv, "--admin-name") ?? void 0,
1022
+ adminPassword: readOption(argv, "--admin-password") ?? void 0,
1023
+ adminRole: readOption(argv, "--admin-role") ?? void 0,
1024
+ adminRoleName: readOption(argv, "--admin-role-name") ?? void 0,
1025
+ adminUid: readOption(argv, "--admin-uid") ?? void 0,
1026
+ resetAdminPassword: hasFlag(argv, "--reset-admin-password"),
1027
+ sysid: readOption(argv, "--sysid") ?? void 0
1028
+ }))));
1029
+ if (statements.length === 0) throw new SeiMcpError("初始化 SQL 文件没有可执行语句", "INVALID_PARAMS");
1030
+ const executed = await client.executeDdlStatements(statements, { allowFailure: hasFlag(argv, "--allow-failure") });
1031
+ await client.call("POST", "/api/sei/data/removeAllCache", {}, { allowFailure: false });
1032
+ printJson({
1033
+ success: true,
1034
+ code: 1,
1035
+ message: "基础源数据初始化完成",
1036
+ data: {
1037
+ executed: executed.length,
1038
+ sqlFile,
1039
+ admin: readOption(argv, "--admin-uid") ?? "admin",
1040
+ role: readOption(argv, "--admin-role") ?? "admin",
1041
+ baseUrl: settings.baseUrl
1042
+ }
1043
+ });
1044
+ return;
1045
+ }
1046
+ default: throw new SeiMcpError(`未知 CLI 子命令:${command}`, "INVALID_PARAMS");
705
1047
  }
706
- default: throw new SeiMcpError(`未知 CLI 子命令:${command}`, "INVALID_PARAMS");
1048
+ } catch (error) {
1049
+ options.logger.error("CLI command failed", {
1050
+ argv,
1051
+ error: toErrorLogMeta(error)
1052
+ });
1053
+ process.stderr.write(`${formatCliError(error)}\n`);
1054
+ throw error;
707
1055
  }
708
1056
  }
709
1057
  function renderCliHelp() {
@@ -716,6 +1064,10 @@ function renderCliHelp() {
716
1064
  ` ${BIN_NAME} cli save [--json <text> | --file <path> | --stdin]`,
717
1065
  ` ${BIN_NAME} cli query-sql "<sql>" [--page 1] [--size 20]`,
718
1066
  ` ${BIN_NAME} cli api-docs [--keyword <text>] [--format markdown|json] [--output <path>]`,
1067
+ ` ${BIN_NAME} cli dict-list [--type <type>] [--keyword <text>] [--size 1000] [--flat]`,
1068
+ ` ${BIN_NAME} cli dict-add-category <type_code> <name> [--code <code>]`,
1069
+ ` ${BIN_NAME} cli dict-add-item <type_code> <code> <name> [--parent <uuid>]`,
1070
+ ` ${BIN_NAME} cli init-base-data [--sysid sys] [--admin-uid admin]`,
719
1071
  "",
720
1072
  "Common Options:",
721
1073
  " --base-url <url>",
@@ -727,11 +1079,29 @@ function renderCliHelp() {
727
1079
  " --token <token>",
728
1080
  " --timeout <seconds>",
729
1081
  " --allow-failure",
1082
+ " --sysid <sysid>",
1083
+ " --uuid <uuid>",
1084
+ " --ctype <ctype>",
1085
+ " --ename <ename>",
1086
+ " --sort <number>",
1087
+ " --body <json-or-text>",
1088
+ " --memo <text>",
1089
+ " --dict-flag <0|1>",
730
1090
  "",
731
1091
  "call Options:",
732
1092
  " --no-token",
733
1093
  " --allow-empty",
734
1094
  "",
1095
+ "init-base-data Options:",
1096
+ ` --admin-uid <uid> default ${BASE_SEED_DEFAULT_ADMIN_UID}`,
1097
+ " --admin-name <name> default 管理员",
1098
+ ` --admin-role <role> default ${BASE_SEED_DEFAULT_ADMIN_ROLE}`,
1099
+ " --admin-role-name <name> default 管理员",
1100
+ ` --admin-password <pwd> default ${BASE_SEED_DEFAULT_ADMIN_PASSWORD}`,
1101
+ ` --sysid <sysid> default sys`,
1102
+ " --reset-admin-password",
1103
+ " --sql-file <path>",
1104
+ "",
735
1105
  "Payload Options:",
736
1106
  " --json <text>",
737
1107
  " --file <path>",
@@ -797,6 +1167,13 @@ function readOptionNumber(argv, name) {
797
1167
  if (!Number.isFinite(parsed) || parsed <= 0) throw new SeiMcpError(`${name} 必须是正数`, "INVALID_PARAMS");
798
1168
  return parsed;
799
1169
  }
1170
+ function readOptionInteger(argv, name) {
1171
+ const value = readOption(argv, name);
1172
+ if (value === null) return;
1173
+ const parsed = Number.parseInt(value, 10);
1174
+ if (!Number.isFinite(parsed)) throw new SeiMcpError(`${name} 必须是整数`, "INVALID_PARAMS");
1175
+ return parsed;
1176
+ }
800
1177
  function firstPositional(argv) {
801
1178
  for (const item of argv) if (item && !item.startsWith("-")) return item;
802
1179
  return null;
@@ -824,6 +1201,13 @@ function isHelpFlag(value) {
824
1201
  function toUndefined(value) {
825
1202
  return value ?? void 0;
826
1203
  }
1204
+ function resolveBaseSeedSqlPath(path) {
1205
+ if (path) return resolve(path);
1206
+ return resolve(process.cwd(), ".codex/skills/sei-ai/scripts/init-base-data.sql");
1207
+ }
1208
+ function isObject$1(value) {
1209
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1210
+ }
827
1211
  async function readStdin() {
828
1212
  const chunks = [];
829
1213
  for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -836,10 +1220,10 @@ async function loadConfig(argv = process.argv.slice(2), env = process.env) {
836
1220
  return normalizeConfig(configPath ? await readConfigFile(configPath) : {}, env);
837
1221
  }
838
1222
  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 : {};
1223
+ const config = isObject$4(input) ? input : {};
1224
+ const auth = isObject$4(config.auth) ? config.auth : {};
1225
+ const tools = isObject$4(config.tools) ? config.tools : {};
1226
+ const targets = isObject$4(config.targets) ? config.targets : {};
843
1227
  return {
844
1228
  baseUrl: normalizeBaseUrl(config.baseUrl, env.SEI_BASE_URL),
845
1229
  auth: {
@@ -896,8 +1280,27 @@ function stringOrEnv(value, envValue) {
896
1280
  return toTrimmedString$1(envValue);
897
1281
  }
898
1282
  function toTargetMap(value) {
899
- if (!isObject$3(value)) return {};
900
- return Object.fromEntries(Object.entries(value).filter(([, item]) => isObject$3(item)));
1283
+ if (!isObject$4(value)) return {};
1284
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => isObject$4(item)));
1285
+ }
1286
+ //#endregion
1287
+ //#region src/env.ts
1288
+ var DEFAULT_ENV_FILES = [".env.local", ".env"];
1289
+ async function loadWorkingDirEnv(cwd = process.cwd(), env = process.env) {
1290
+ for (const fileName of DEFAULT_ENV_FILES) await loadEnvFile(resolve(cwd, fileName), env);
1291
+ }
1292
+ async function loadEnvFile(path, env) {
1293
+ try {
1294
+ const parsed = parseEnv(await readFile(path, "utf8"));
1295
+ for (const [key, value] of Object.entries(parsed)) if (env[key] === void 0) env[key] = value;
1296
+ } catch (error) {
1297
+ if (isMissingFile(error)) return;
1298
+ throw error;
1299
+ }
1300
+ }
1301
+ function isMissingFile(error) {
1302
+ if (!error || typeof error !== "object") return false;
1303
+ return error.code === "ENOENT";
901
1304
  }
902
1305
  //#endregion
903
1306
  //#region src/logger.ts
@@ -4543,7 +4946,7 @@ var $ZodAsyncError = class extends Error {
4543
4946
  }
4544
4947
  };
4545
4948
  var globalConfig = {};
4546
- function config$1(newConfig) {
4949
+ function config(newConfig) {
4547
4950
  if (newConfig) Object.assign(globalConfig, newConfig);
4548
4951
  return globalConfig;
4549
4952
  }
@@ -4886,7 +5289,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
4886
5289
  }, ctx);
4887
5290
  if (result instanceof Promise) throw new $ZodAsyncError();
4888
5291
  if (result.issues.length) {
4889
- const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())));
5292
+ const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
4890
5293
  captureStackTrace(e, _params?.callee);
4891
5294
  throw e;
4892
5295
  }
@@ -4901,7 +5304,7 @@ var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
4901
5304
  }, ctx);
4902
5305
  if (result instanceof Promise) result = await result;
4903
5306
  if (result.issues.length) {
4904
- const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())));
5307
+ const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
4905
5308
  captureStackTrace(e, params?.callee);
4906
5309
  throw e;
4907
5310
  }
@@ -4920,7 +5323,7 @@ var _safeParse = (_Err) => (schema, value, _ctx) => {
4920
5323
  if (result instanceof Promise) throw new $ZodAsyncError();
4921
5324
  return result.issues.length ? {
4922
5325
  success: false,
4923
- error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())))
5326
+ error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
4924
5327
  } : {
4925
5328
  success: true,
4926
5329
  data: result.value
@@ -4936,7 +5339,7 @@ var _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
4936
5339
  if (result instanceof Promise) result = await result;
4937
5340
  return result.issues.length ? {
4938
5341
  success: false,
4939
- error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())))
5342
+ error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
4940
5343
  } : {
4941
5344
  success: true,
4942
5345
  data: result.value
@@ -5929,7 +6332,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
5929
6332
  return (payload, ctx) => fn(shape, payload, ctx);
5930
6333
  };
5931
6334
  let fastpass;
5932
- const isObject$4 = isObject;
6335
+ const isObject$6 = isObject;
5933
6336
  const jit = !globalConfig.jitless;
5934
6337
  const fastEnabled = jit && allowsEval.value;
5935
6338
  const catchall = def.catchall;
@@ -5937,7 +6340,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
5937
6340
  inst._zod.parse = (payload, ctx) => {
5938
6341
  value ?? (value = _normalized.value);
5939
6342
  const input = payload.value;
5940
- if (!isObject$4(input)) {
6343
+ if (!isObject$6(input)) {
5941
6344
  payload.issues.push({
5942
6345
  expected: "object",
5943
6346
  code: "invalid_type",
@@ -6004,7 +6407,7 @@ function handleUnionResults(results, final, inst, ctx) {
6004
6407
  code: "invalid_union",
6005
6408
  input: final.value,
6006
6409
  inst,
6007
- errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())))
6410
+ errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
6008
6411
  });
6009
6412
  return final;
6010
6413
  }
@@ -6233,7 +6636,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
6233
6636
  payload.issues.push({
6234
6637
  origin: "record",
6235
6638
  code: "invalid_key",
6236
- issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config$1())),
6639
+ issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
6237
6640
  input: key,
6238
6641
  path: [key],
6239
6642
  inst
@@ -6401,7 +6804,7 @@ var $ZodCatch = /* @__PURE__ */ $constructor("$ZodCatch", (inst, def) => {
6401
6804
  if (result.issues.length) {
6402
6805
  payload.value = def.catchValue({
6403
6806
  ...payload,
6404
- error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())) },
6807
+ error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },
6405
6808
  input: payload.value
6406
6809
  });
6407
6810
  payload.issues = [];
@@ -6412,7 +6815,7 @@ var $ZodCatch = /* @__PURE__ */ $constructor("$ZodCatch", (inst, def) => {
6412
6815
  if (result.issues.length) {
6413
6816
  payload.value = def.catchValue({
6414
6817
  ...payload,
6415
- error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())) },
6818
+ error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },
6416
6819
  input: payload.value
6417
6820
  });
6418
6821
  payload.issues = [];
@@ -21222,7 +21625,7 @@ function authorizeQuery(config, input) {
21222
21625
  return {
21223
21626
  head,
21224
21627
  option: {
21225
- ...isObject$3(input.option) ? input.option : {},
21628
+ ...isObject$4(input.option) ? input.option : {},
21226
21629
  fields: effectiveFields.join(","),
21227
21630
  limit: toPositiveInteger(input.option?.limit, 20)
21228
21631
  },
@@ -21244,7 +21647,7 @@ function authorizeSave(config, input) {
21244
21647
  const action = normalizeAction(item?.action);
21245
21648
  if (!allowedActions.includes(action)) throw new SeiMcpError(`save 动作不在白名单内: ${action}`, "INVALID_PARAMS");
21246
21649
  for (const row of Array.isArray(item?.rows) ? item.rows : []) {
21247
- const current = isObject$3(row?.row) ? row.row : {};
21650
+ const current = isObject$4(row?.row) ? row.row : {};
21248
21651
  ensureFieldsAllowed(Object.keys(current).filter((key) => !RESERVED_SAVE_ROW_KEYS.has(key)), allowedFields, "save");
21249
21652
  }
21250
21653
  }
@@ -21272,7 +21675,7 @@ function authorizeQuerySql(config, input) {
21272
21675
  };
21273
21676
  }
21274
21677
  function requireHead(head) {
21275
- if (!isObject$3(head) || Array.isArray(head)) throw new SeiMcpError("head is required", "INVALID_PARAMS");
21678
+ if (!isObject$4(head) || Array.isArray(head)) throw new SeiMcpError("head is required", "INVALID_PARAMS");
21276
21679
  const result = {};
21277
21680
  for (const [key, value] of Object.entries(head)) {
21278
21681
  const trimmed = toTrimmedString$1(value);
@@ -21312,7 +21715,7 @@ function isTargetAllowed(config, tableName) {
21312
21715
  ];
21313
21716
  for (const pool of pools) {
21314
21717
  if (pool[normalized]) return true;
21315
- for (const target of Object.values(pool)) if (isObject$3(target)) {
21718
+ for (const target of Object.values(pool)) if (isObject$4(target)) {
21316
21719
  const mainTable = toTrimmedString$1(target.mainTable);
21317
21720
  if (mainTable && mainTable === normalized) return true;
21318
21721
  }
@@ -21883,6 +22286,7 @@ function toTrimmedString(value) {
21883
22286
  }
21884
22287
  //#endregion
21885
22288
  //#region src/index.ts
22289
+ await loadWorkingDirEnv();
21886
22290
  var logger = createLogger(process.env.SEI_MCP_LOG_LEVEL ?? "info");
21887
22291
  var argv = process.argv.slice(2);
21888
22292
  var runtime = parseRuntimeOptions(argv, process.env);
@@ -21890,23 +22294,31 @@ if (runtime.command === "help") {
21890
22294
  process.stdout.write(`${renderHelpText()}\n`);
21891
22295
  process.exit(0);
21892
22296
  }
21893
- var config = await loadConfig(argv, process.env);
21894
- if (runtime.command === "cli") {
21895
- await runCli(runtime.cliArgs, {
22297
+ try {
22298
+ const config = await loadConfig(argv, process.env);
22299
+ if (runtime.command === "cli") {
22300
+ await runCli(runtime.cliArgs, {
22301
+ config,
22302
+ logger
22303
+ });
22304
+ process.exit(0);
22305
+ }
22306
+ const client = createSeiClient(config, logger);
22307
+ if (runtime.transport === "streamable-http") await startHttpServer(() => createMcpServer({
21896
22308
  config,
22309
+ client,
21897
22310
  logger
21898
- });
21899
- process.exit(0);
22311
+ }), runtime, logger);
22312
+ else await startStdioServer(createMcpServer({
22313
+ config,
22314
+ client,
22315
+ logger
22316
+ }));
22317
+ } catch (error) {
22318
+ if (!isSeiMcpError(error) || runtime.command !== "cli") {
22319
+ logger.error("sei-ai startup failed", toErrorLogMeta(error));
22320
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
22321
+ }
22322
+ process.exit(1);
21900
22323
  }
21901
- var client = createSeiClient(config, logger);
21902
- if (runtime.transport === "streamable-http") await startHttpServer(() => createMcpServer({
21903
- config,
21904
- client,
21905
- logger
21906
- }), runtime, logger);
21907
- else await startStdioServer(createMcpServer({
21908
- config,
21909
- client,
21910
- logger
21911
- }));
21912
22324
  //#endregion
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.3",
4
4
  "description": "SEI MCP server and developer CLI for query, save, query-sql, and api-docs workflows",
5
5
  "keywords": [
6
6
  "sei",