@maiyunnet/kebab 7.7.0 → 7.8.0

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/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * --- 本文件用来定义每个目录实体地址的常量 ---
6
6
  */
7
7
  /** --- 当前系统版本号 --- */
8
- export declare const VER = "7.7.0";
8
+ export declare const VER = "7.8.0";
9
9
  /** --- 框架根目录,以 / 结尾 --- */
10
10
  export declare const ROOT_PATH: string;
11
11
  export declare const LIB_PATH: string;
package/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * --- 本文件用来定义每个目录实体地址的常量 ---
7
7
  */
8
8
  /** --- 当前系统版本号 --- */
9
- export const VER = '7.7.0';
9
+ export const VER = '7.8.0';
10
10
  // --- 服务端用的路径 ---
11
11
  const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
12
12
  /** --- /xxx/xxx --- */
package/lib/crypto.js CHANGED
@@ -31,6 +31,7 @@ export function generateKeyPair(type, options = {}) {
31
31
  };
32
32
  options.privateKeyEncoding.type ??= 'pkcs8';
33
33
  options.privateKeyEncoding.format ??= 'pem';
34
+ /** --- 由于 generateKeyPair 重载过多且 options 为 any,此处参数也采用 any --- */
34
35
  crypto.generateKeyPair(type, options, (err, publicKey, privateKey) => {
35
36
  resolve({
36
37
  'private': privateKey,
package/lib/sql.d.ts CHANGED
@@ -12,16 +12,18 @@ export declare enum ESERVICE {
12
12
  }
13
13
  /** --- JSON 查询操作符 --- */
14
14
  export declare enum EJSON {
15
- /** --- 包含 (MySQL: JSON_CONTAINS, PG: @>) --- */
15
+ /** --- 包含值 (MySQL: JSON_CONTAINS, PG: @>) --- */
16
16
  'CONTAINS' = "json",
17
- /** --- 被包含 (MySQL: JSON_CONTAINS, PG: <@) --- */
17
+ /** --- 被包含值 (MySQL: JSON_CONTAINS, PG: <@) --- */
18
18
  'CONTAINED_BY' = "json_in",
19
- /** --- 存在 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
19
+ /** --- 存在 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
20
20
  'HAS_KEY' = "json_key",
21
- /** --- 存在任意 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
21
+ /** --- 存在任意 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
22
22
  'HAS_ANY_KEYS' = "json_any",
23
- /** --- 存在所有 Key (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
24
- 'HAS_ALL_KEYS' = "json_all"
23
+ /** --- 存在所有 Key 不含值 (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
24
+ 'HAS_ALL_KEYS' = "json_all",
25
+ /** --- 简单数组重叠 (MySQL: JSON_OVERLAPS, PG: &&) --- */
26
+ 'OVERLAPS' = "json_overlaps"
25
27
  }
26
28
  export declare class Sql {
27
29
  /** --- ctr 对象 --- */
package/lib/sql.js CHANGED
@@ -11,16 +11,18 @@ export var ESERVICE;
11
11
  /** --- JSON 查询操作符 --- */
12
12
  export var EJSON;
13
13
  (function (EJSON) {
14
- /** --- 包含 (MySQL: JSON_CONTAINS, PG: @>) --- */
14
+ /** --- 包含值 (MySQL: JSON_CONTAINS, PG: @>) --- */
15
15
  EJSON["CONTAINS"] = "json";
16
- /** --- 被包含 (MySQL: JSON_CONTAINS, PG: <@) --- */
16
+ /** --- 被包含值 (MySQL: JSON_CONTAINS, PG: <@) --- */
17
17
  EJSON["CONTAINED_BY"] = "json_in";
18
- /** --- 存在 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
18
+ /** --- 存在 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
19
19
  EJSON["HAS_KEY"] = "json_key";
20
- /** --- 存在任意 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
20
+ /** --- 存在任意 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
21
21
  EJSON["HAS_ANY_KEYS"] = "json_any";
22
- /** --- 存在所有 Key (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
22
+ /** --- 存在所有 Key 不含值 (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
23
23
  EJSON["HAS_ALL_KEYS"] = "json_all";
24
+ /** --- 简单数组重叠 (MySQL: JSON_OVERLAPS, PG: &&) --- */
25
+ EJSON["OVERLAPS"] = "json_overlaps";
24
26
  })(EJSON || (EJSON = {}));
25
27
  /** --- field 用 token --- */
26
28
  let columnToken = '';
@@ -452,7 +454,7 @@ export class Sql {
452
454
  data.push(...v[1]);
453
455
  }
454
456
  }
455
- else if (typeof v[1] === 'string' && ['json', 'json_in', 'json_key', 'json_any', 'json_all'].includes(v[1].toLowerCase())) {
457
+ else if (typeof v[1] === 'string' && ['json', 'json_in', 'json_key', 'json_any', 'json_all', 'json_overlaps'].includes(v[1].toLowerCase())) {
456
458
  // --- json ---
457
459
  const op = v[1].toLowerCase();
458
460
  const nv = v[2];
@@ -476,6 +478,16 @@ export class Sql {
476
478
  data.push(lText.stringifyJson(nv));
477
479
  }
478
480
  }
481
+ else if (op === 'json_overlaps') {
482
+ if (this._service === ESERVICE.MYSQL) {
483
+ sql += `JSON_OVERLAPS(${this.field(v[0])}, ${this._placeholder()}) AND `;
484
+ data.push(lText.stringifyJson(nv));
485
+ }
486
+ else {
487
+ sql += `${this.field(v[0])} && ${this._placeholder()} AND `;
488
+ data.push(nv);
489
+ }
490
+ }
479
491
  else {
480
492
  // --- json_key, json_any, json_all ---
481
493
  const keys = Array.isArray(nv) ? nv : [nv];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "7.7.0",
3
+ "version": "7.8.0",
4
4
  "description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -22,30 +22,30 @@
22
22
  "#kebab/*": "./*"
23
23
  },
24
24
  "dependencies": {
25
- "@aws-sdk/client-s3": "^3.943.0",
26
- "@aws-sdk/lib-storage": "^3.943.0",
25
+ "@aws-sdk/client-s3": "^3.975.0",
26
+ "@aws-sdk/lib-storage": "^3.975.0",
27
27
  "@litert/http-client": "^1.1.2",
28
28
  "@litert/mime": "^0.1.3",
29
29
  "@litert/redis": "^3.1.0",
30
30
  "@litert/websocket": "^0.2.8",
31
31
  "@types/ssh2": "^1.15.5",
32
- "@zilliz/milvus2-sdk-node": "^2.6.5",
33
- "ejs": "^3.1.10",
32
+ "@zilliz/milvus2-sdk-node": "^2.6.9",
33
+ "ejs": "^4.0.1",
34
34
  "jszip": "^3.10.1",
35
- "mysql2": "^3.15.3",
35
+ "mysql2": "^3.16.1",
36
36
  "node-cron": "^4.2.1",
37
- "openai": "^6.10.0",
38
- "pg": "^8.16.3",
37
+ "openai": "^6.16.0",
38
+ "pg": "^8.17.2",
39
39
  "ssh2": "^1.17.0",
40
40
  "svg-captcha": "^1.4.0",
41
- "tencentcloud-sdk-nodejs": "^4.1.153"
41
+ "tencentcloud-sdk-nodejs": "^4.1.175"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@litert/eslint-plugin-rules": "^0.3.1",
45
45
  "@types/ejs": "^3.1.5",
46
- "@types/node": "^24.10.1",
47
- "@types/pg": "^8.15.6",
48
- "typedoc": "^0.28.15",
46
+ "@types/node": "^25.0.10",
47
+ "@types/pg": "^8.16.0",
48
+ "typedoc": "^0.28.16",
49
49
  "typedoc-plugin-markdown": "^4.9.0",
50
50
  "typescript": "^5.9.3"
51
51
  }
package/sys/master.js CHANGED
@@ -3,8 +3,8 @@
3
3
  * Date: 2019-5-2 21:03:42
4
4
  * Last: 2020-3-7 10:33:17, 2022-07-22 13:40:10, 2022-09-06 22:40:58, 2024-2-7 01:44:59, 2024-7-2 15:17:09, 2025-6-13 13:06:43, 2025-12-5 13:15:03
5
5
  */
6
+ import cluster from 'cluster';
6
7
  import * as os from 'os';
7
- import * as cluster from 'cluster';
8
8
  import * as http from 'http';
9
9
  // --- 库和定义 ---
10
10
  import * as kebab from '#kebab/index.js';
@@ -22,7 +22,7 @@ const workerList = {};
22
22
  */
23
23
  async function run() {
24
24
  // --- 设置 cluster 调度策略为 round-robin(在支持的平台上启用负载均衡) ---
25
- cluster.default.schedulingPolicy = cluster.default.SCHED_RR;
25
+ cluster.schedulingPolicy = cluster.SCHED_RR;
26
26
  // --- 读取配置文件 ---
27
27
  const configContent = await lFs.getContent(kebab.CONF_CWD + 'config.json', 'utf8');
28
28
  if (!configContent) {
@@ -48,7 +48,7 @@ async function run() {
48
48
  for (let i = 0; i < cpuLengthMax; ++i) {
49
49
  await createChildProcess(i);
50
50
  }
51
- cluster.default.on('listening', function (worker, address) {
51
+ cluster.on('listening', function (worker, address) {
52
52
  // --- 子进程开始监听 ---
53
53
  lCore.display(`[master] Listening: worker ${worker.process.pid ?? 'undefined'}, Address: ${address.address}:${address.port}.`);
54
54
  }).on('exit', function (worker, code) {
@@ -419,7 +419,7 @@ async function checkWorkerLost() {
419
419
  * @param cpu CPU ID
420
420
  */
421
421
  async function createChildProcess(cpu) {
422
- const worker = cluster.default.fork();
422
+ const worker = cluster.fork();
423
423
  if (!worker.process.pid) {
424
424
  return;
425
425
  }
package/sys/mod.js CHANGED
@@ -615,8 +615,11 @@ export default class Mod {
615
615
  }
616
616
  // --- 未处理的错误 ---
617
617
  const service = this._db.getService();
618
- lCore.debug('[MOD][create0][' + cstr._$table + ']', service !== null ? lDb.ESERVICE[service] : 'NONE', r);
619
618
  lCore.log(this._ctr ?? {}, '[MOD][create0][' + cstr._$table + '] ' + lText.stringifyJson(r.error?.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
619
+ if (lCore.globalConfig.debug) {
620
+ lCore.debug('[MOD][create0][' + cstr._$table + ']', service !== null ? lDb.ESERVICE[service] : 'NONE', r);
621
+ lCore.debug(this._sql.format());
622
+ }
620
623
  return false;
621
624
  }
622
625
  }
@@ -626,8 +629,11 @@ export default class Mod {
626
629
  r = await this._db.execute(this._sql.getSql(), this._sql.getData());
627
630
  if (r.error) {
628
631
  if (r.error.errno !== 1062) {
629
- lCore.debug('[MOD][create1][' + cstr._$table + ']', r);
630
632
  lCore.log(this._ctr ?? {}, '[MOD][create1][' + cstr._$table + '] ' + lText.stringifyJson(r.error?.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
633
+ if (lCore.globalConfig.debug) {
634
+ lCore.debug('[MOD][create1][' + cstr._$table + ']', r);
635
+ lCore.debug(this._sql.format());
636
+ }
631
637
  return false;
632
638
  }
633
639
  return null;
@@ -0,0 +1,8 @@
1
+ import * as sCtr from '#kebab/sys/ctr.js';
2
+ export default class extends sCtr.Ctr {
3
+ /**
4
+ * --- Agent 示例接口 ---
5
+ * --- 访问:/agent?q=上海天气怎么样? ---
6
+ */
7
+ index(): Promise<string>;
8
+ }
@@ -0,0 +1,68 @@
1
+ import * as sCtr from '#kebab/sys/ctr.js';
2
+ import * as lAi from '#kebab/lib/ai.js';
3
+ import * as lFs from '#kebab/lib/fs.js';
4
+ import * as lText from '#kebab/lib/text.js';
5
+ /** --- Skill 导入 --- */
6
+ import sWeather from '../skill/weather.js';
7
+ export default class extends sCtr.Ctr {
8
+ /**
9
+ * --- Agent 示例接口 ---
10
+ * --- 访问:/agent?q=上海天气怎么样? ---
11
+ */
12
+ async index() {
13
+ // --- 1. 获取用户问题 ---
14
+ const userPrompt = this._get['q'] ?? '你好';
15
+ // --- 2. 加载系统提示词 ---
16
+ const systemPrompt = await lFs.getContent(`${this._config.const.rootPath}www/example/prompt/agent.txt`, 'utf8') ?? '';
17
+ // --- 3. 初始化 AI ---
18
+ const ai = lAi.get(this, {
19
+ 'service': lAi.ESERVICE.ALICN,
20
+ });
21
+ // --- 4. 准备消息 ---
22
+ const messages = [
23
+ { 'role': 'system', 'content': systemPrompt },
24
+ { 'role': 'user', 'content': userPrompt },
25
+ ];
26
+ // --- 5. 第一次对话,传入 Skills 定义 ---
27
+ const res = await ai.chat({
28
+ 'model': 'qwen-plus',
29
+ 'messages': messages,
30
+ 'tools': [sWeather.definition],
31
+ });
32
+ if (!res) {
33
+ return '系统错误';
34
+ }
35
+ const message = res.choices[0].message;
36
+ // --- 6. 如果 AI 决定调用工具 ---
37
+ if (message.tool_calls && message.tool_calls.length > 0) {
38
+ messages.push(message);
39
+ for (const toolCall of message.tool_calls) {
40
+ if (toolCall.function.name === 'get_weather') {
41
+ // --- 解析参数并执行 ---
42
+ const args = lText.parseJson(toolCall.function.arguments);
43
+ if (args === false) {
44
+ continue;
45
+ }
46
+ const result = await sWeather.execute(args);
47
+ // --- 将结果反馈给消息流 ---
48
+ messages.push({
49
+ 'role': 'tool',
50
+ 'tool_call_id': toolCall.id,
51
+ 'name': 'get_weather',
52
+ 'content': result,
53
+ });
54
+ }
55
+ }
56
+ // --- 7. 第二次对话,获取最终文本回复 ---
57
+ const finalRes = await ai.chat({
58
+ 'model': 'qwen-plus',
59
+ 'messages': messages,
60
+ });
61
+ if (finalRes) {
62
+ return finalRes.choices[0].message.content ?? '未能生成回复';
63
+ }
64
+ }
65
+ // --- 如果不需要工具调用,直接返回内容 ---
66
+ return message.content ?? '未能生成回复';
67
+ }
68
+ }
@@ -2601,6 +2601,13 @@ Result:<pre id="result">Nothing.</pre>`);
2601
2601
  echo.push(`<pre>sql.select('*', 'user').where([['info', lSql.EJSON.HAS_ALL_KEYS, ['age', 'name']]]);</pre>
2602
2602
  <b>getSql() :</b> ${s}<br>
2603
2603
  <b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
2604
+ <b>format() :</b> ${sql.format(s, sd)}<hr>`);
2605
+ // --- overlaps ---
2606
+ s = sql.select('*', 'test').where([['tags', lSql.EJSON.OVERLAPS, ['a', 'b']]]).getSql();
2607
+ sd = sql.getData();
2608
+ echo.push(`<pre>sql.select('*', 'test').where([['tags', lSql.EJSON.OVERLAPS, ['a', 'b']]]);</pre>
2609
+ <b>getSql() :</b> ${s}<br>
2610
+ <b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
2604
2611
  <b>format() :</b> ${sql.format(s, sd)}`);
2605
2612
  break;
2606
2613
  }
@@ -0,0 +1,15 @@
1
+ import type * as openai from 'openai';
2
+ /**
3
+ * --- 获取天气预报的 Skill ---
4
+ */
5
+ export default class {
6
+ /** --- Skill 的定义,供 AI 模型识别 --- */
7
+ static definition: openai.default.Chat.ChatCompletionTool;
8
+ /**
9
+ * --- 执行获取天气的逻辑 ---
10
+ * @param args AI 传过来的参数
11
+ */
12
+ static execute(args: {
13
+ 'city': string;
14
+ }): Promise<string>;
15
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * --- 获取天气预报的 Skill ---
3
+ */
4
+ export default class default_1 {
5
+ /** --- Skill 的定义,供 AI 模型识别 --- */
6
+ static definition = {
7
+ 'type': 'function',
8
+ 'function': {
9
+ 'name': 'get_weather',
10
+ 'description': '获取指定城市的天气信息',
11
+ 'parameters': {
12
+ 'type': 'object',
13
+ 'properties': {
14
+ 'city': {
15
+ 'type': 'string',
16
+ 'description': '城市名称,例如“上海”、“北京”',
17
+ },
18
+ },
19
+ 'required': ['city'],
20
+ },
21
+ },
22
+ };
23
+ /**
24
+ * --- 执行获取天气的逻辑 ---
25
+ * @param args AI 传过来的参数
26
+ */
27
+ static async execute(args) {
28
+ // --- 这里是模拟获取天气的逻辑,实际开发中可以调用第三方 API ---
29
+ const weathers = {
30
+ '上海': '晴朗,25℃',
31
+ '北京': '多云,20℃',
32
+ '广州': '阵雨,28℃',
33
+ };
34
+ return weathers[args.city] ?? `抱歉,暂不支持查询 ${args.city} 的天气。`;
35
+ }
36
+ }