@maiyunnet/kebab 8.0.6 → 8.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "8.0.6",
3
+ "version": "8.2.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.985.0",
26
- "@aws-sdk/lib-storage": "^3.985.0",
25
+ "@aws-sdk/client-s3": "^3.992.0",
26
+ "@aws-sdk/lib-storage": "^3.992.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.9",
32
+ "@zilliz/milvus2-sdk-node": "^2.6.10",
33
33
  "ejs": "^4.0.1",
34
34
  "jszip": "^3.10.1",
35
- "mysql2": "^3.16.3",
35
+ "mysql2": "^3.17.2",
36
36
  "node-cron": "^4.2.1",
37
- "openai": "^6.18.0",
37
+ "openai": "^6.22.0",
38
38
  "pg": "^8.18.0",
39
39
  "ssh2": "^1.17.0",
40
40
  "svg-captcha": "^1.4.0",
41
- "tencentcloud-sdk-nodejs": "^4.1.185"
41
+ "tencentcloud-sdk-nodejs": "^4.1.186"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@litert/eslint-plugin-rules": "^0.3.1",
45
45
  "@types/ejs": "^3.1.5",
46
- "@types/node": "^25.2.2",
46
+ "@types/node": "^25.2.3",
47
47
  "@types/pg": "^8.16.0",
48
- "typedoc": "^0.28.16",
48
+ "typedoc": "^0.28.17",
49
49
  "typedoc-plugin-markdown": "^4.10.0",
50
50
  "typescript": "^5.9.3"
51
51
  }
package/sys/cmd.js CHANGED
@@ -177,6 +177,14 @@ async function run() {
177
177
  config.ai['AZURE3'] ??= {};
178
178
  config.ai['AZURE3'].endpoint ??= '';
179
179
  config.ai['AZURE3'].skey ??= '';
180
+ config.ai['GEMINI'] ??= {};
181
+ config.ai['GEMINI'].skey ??= '';
182
+ config.ai['GROK'] ??= {};
183
+ config.ai['GROK'].skey ??= '';
184
+ config.ai['VOLCN'] ??= {};
185
+ config.ai['VOLCN'].skey ??= '';
186
+ config.ai['VOLAS'] ??= {};
187
+ config.ai['VOLAS'].skey ??= '';
180
188
  // --- config - vector ---
181
189
  config.vector ??= {};
182
190
  config.vector.host ??= '127.0.0.1';
package/sys/monitor.js CHANGED
@@ -82,9 +82,8 @@ export function start(opt) {
82
82
  memThreshold = opt?.mem ?? 0;
83
83
  eloopThreshold = opt?.eloop ?? 500;
84
84
  if (memThreshold === 0) {
85
- // --- 自动计算:系统总内存的 80% / CPU 核心数 ---
86
- const cpuCount = os.cpus().length;
87
- memThreshold = Math.floor((os.totalmem() * 0.8) / cpuCount / 1024 / 1024);
85
+ // --- 自动计算:系统总内存的 80% ---
86
+ memThreshold = Math.floor((os.totalmem() * 0.8) / 1024 / 1024);
88
87
  }
89
88
  lastCpuUsage = process.cpuUsage();
90
89
  lastCpuTime = Date.now();
@@ -102,7 +101,7 @@ export function start(opt) {
102
101
  timer = setInterval(check, INTERVAL);
103
102
  // --- 启动看门狗线程 ---
104
103
  startWatchdog();
105
- lCore.debug(`[MONITOR] Started, CPU: ${cpuThreshold}%, MEM: ${memThreshold}MB, ELOOP: ${eloopThreshold}ms`);
104
+ lCore.debug(`[MONITOR] [PARENT] [${process.pid}] Started, CPU Threshold: ${cpuThreshold}%, MEM Threshold: ${memThreshold}MB, ELOOP Threshold: ${eloopThreshold}ms`);
106
105
  }
107
106
  /**
108
107
  * --- 停止性能监控 ---
@@ -162,7 +161,7 @@ function startWatchdog() {
162
161
  watchdog = null;
163
162
  }
164
163
  });
165
- lCore.debug('[MONITOR] Watchdog thread started');
164
+ lCore.debug(`[MONITOR] [THREAD] [${process.pid}] Watchdog started`);
166
165
  }
167
166
  catch (e) {
168
167
  lCore.debug('[MONITOR] Failed to start watchdog', e);
@@ -127,8 +127,12 @@ export default class extends sCtr.Ctr {
127
127
  `<br><a href="${this._config.const.urlBase}test/mod-update-list">View "test/mod-update-list"</a>`,
128
128
  '<br><br><b>Library test:</b>',
129
129
  `<br><br><b>Ai:</b>`,
130
- `<br><br><a href="${this._config.const.urlBase}test/ai">View "test/ai"</a>`,
131
130
  `<br><a href="${this._config.const.urlBase}test/ai-stream">View "test/ai-stream"</a>`,
131
+ `<br><a href="${this._config.const.urlBase}test/ai?action=chat">View "test/ai?action=chat"</a>`,
132
+ `<br><a href="${this._config.const.urlBase}test/ai?action=text-to-image">View "test/ai?action=text-to-image"</a>`,
133
+ `<br><a href="${this._config.const.urlBase}test/ai?action=image-to-image">View "test/ai?action=image-to-image"</a>`,
134
+ `<br><a href="${this._config.const.urlBase}test/ai?action=text-to-video">View "test/ai?action=text-to-video"</a>`,
135
+ `<br><a href="${this._config.const.urlBase}test/ai?action=image-to-video">View "test/ai?action=image-to-video"</a>`,
132
136
  '<br><br><b>Core:</b>',
133
137
  `<br><br><a href="${this._config.const.urlBase}test/core-random">View "test/core-random"</a>`,
134
138
  `<br><a href="${this._config.const.urlBase}test/core-rand">View "test/core-rand"</a>`,
@@ -1796,7 +1800,7 @@ error: ${JSON.stringify(res.error)}`);
1796
1800
  lNet.get(this._internalUrl + 'test/net-cookie1', {
1797
1801
  'cookie': cookie
1798
1802
  });</pre>
1799
- headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
1803
+ headers: <pre>${lText.htmlescape(JSON.stringify(res.headers, null, 4))}</pre>
1800
1804
  content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
1801
1805
  cookie: <pre>${JSON.stringify(cookie, null, 4)}</pre><hr>`);
1802
1806
  res = await lNet.get(this._internalUrl + 'test/net-cookie2', {
@@ -1868,6 +1872,14 @@ content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>`);
1868
1872
  'ttl': 60,
1869
1873
  'httponly': true
1870
1874
  });
1875
+ // --- 用于测试 BUG 的额外 Cookie ---
1876
+ this._res.setHeader('Set-Cookie', [
1877
+ ...this._res.getHeader('Set-Cookie'),
1878
+ 'test-del=; Max-Age=0; Path=/',
1879
+ 'test-equals=val=with=equals; Path=/',
1880
+ 'test-expires=val; Expires=Wed, 21 Oct 2035 07:28:00 GMT; Path=/',
1881
+ 'test-invalid=%invalid; Path=/'
1882
+ ]);
1871
1883
  return `lCore.setCookie(this, 'test0', 'session');
1872
1884
  lCore.setCookie(this, 'test1', 'normal', {
1873
1885
  'ttl': 10
@@ -3337,31 +3349,177 @@ rtn.push(reader.readBCDString());</pre>${JSON.stringify(rtn)}`);
3337
3349
  return echo.join('') + '<br>' + this._getEnd();
3338
3350
  }
3339
3351
  async ai() {
3340
- const ai = lAi.get(this, {
3341
- 'service': lAi.ESERVICE.ALICN,
3342
- });
3343
3352
  const echo = [`<pre>const ai = lAi.get(this, {
3344
- 'service': lAi.ESERVICE.ALICN,
3353
+ 'service': lAi.ESERVICE.${lText.htmlescape(this._get['service']?.toUpperCase() ?? 'ALICN')},
3345
3354
  });</pre>`];
3346
- const completion = await ai.chat({
3347
- 'model': 'qwen-plus',
3348
- 'messages': [
3349
- { 'role': 'system', 'content': 'You are Kebab, a friendly and knowledgeable assistant based on an open-source Node framework. You do not mention any model names or AI identity. You can chat casually, answer questions, and provide guidance naturally. Respond in a human-like, approachable manner, as if you are a helpful companion rather than a traditional AI assistant.' },
3350
- { 'role': 'user', 'content': '你是谁?' },
3351
- ],
3355
+ const ai = lAi.get(this, {
3356
+ 'service': lAi.ESERVICE[this._get['service']?.toUpperCase() ?? 'ALICN'] ?? lAi.ESERVICE.ALICN,
3352
3357
  });
3353
- echo.push(`<pre>await ai.chat({
3354
- 'model': 'qwen-plus',
3358
+ switch (this._get['action']) {
3359
+ case 'text-to-image': {
3360
+ // --- 文生图 ---
3361
+ let model = 'z-image-turbo';
3362
+ let size = [1280, 720];
3363
+ if (ai.service !== lAi.ESERVICE.ALICN && ai.service !== lAi.ESERVICE.ALIAS) {
3364
+ switch (ai.service) {
3365
+ case lAi.ESERVICE.AZURE:
3366
+ case lAi.ESERVICE.AZURE2:
3367
+ case lAi.ESERVICE.AZURE3: {
3368
+ model = 'FLUX.2-pro';
3369
+ break;
3370
+ }
3371
+ default: {
3372
+ // --- 火山引擎 ---
3373
+ model = 'doubao-seedream-4-5-251128';
3374
+ size = [2560, 1440];
3375
+ }
3376
+ }
3377
+ }
3378
+ const imgResult = await ai.image({
3379
+ 'model': model,
3380
+ 'prompt': 'A cute cat sitting on a windowsill watching the sunset',
3381
+ 'size': size,
3382
+ 'n': 1,
3383
+ });
3384
+ echo.push(`<pre>await ai.image({
3385
+ 'model': '${model}',
3386
+ 'prompt': 'A cute cat sitting on a windowsill watching the sunset',
3387
+ 'size': [${size[0]}, ${size[1]}],
3388
+ 'n': 1,
3389
+ });</pre>`);
3390
+ if (imgResult && imgResult.list?.length) {
3391
+ for (const img of imgResult.list) {
3392
+ echo.push(`<div><img src="${img.url.startsWith('http') ? img.url : 'data:image/png;base64,' + img.url}" style="max-width: 512px;" /></div>${lText.htmlescape(img.text)}`);
3393
+ }
3394
+ echo.push('<br>request: ' + imgResult.request, ', seed: ' + imgResult.seed);
3395
+ }
3396
+ else {
3397
+ echo.push('Failed');
3398
+ }
3399
+ break;
3400
+ }
3401
+ case 'image-to-image': {
3402
+ // --- 图生图 ---
3403
+ let model = 'wan2.6-image';
3404
+ let size = [1280, 720];
3405
+ const prompt = '用图1的绘画风格重绘图2的场景,桌上增加一盘番茄炒蛋';
3406
+ if (ai.service !== lAi.ESERVICE.ALICN && ai.service !== lAi.ESERVICE.ALIAS) {
3407
+ switch (ai.service) {
3408
+ default: {
3409
+ // --- 火山引擎 ---
3410
+ model = 'doubao-seedream-4-5-251128';
3411
+ size = [2560, 1440];
3412
+ }
3413
+ }
3414
+ }
3415
+ const imgResult = await ai.image({
3416
+ 'model': model,
3417
+ 'imgs': [
3418
+ 'https://cdn.wanx.aliyuncs.com/tmp/pressure/umbrella1.png',
3419
+ 'https://img.alicdn.com/imgextra/i3/O1CN01SfG4J41UYn9WNt4X1_!!6000000002530-49-tps-1696-960.webp',
3420
+ ],
3421
+ 'prompt': prompt,
3422
+ 'size': size,
3423
+ 'n': 1,
3424
+ });
3425
+ echo.push(`<pre>await ai.image({
3426
+ 'model': '${model}',
3427
+ 'imgs': [
3428
+ 'https://cdn.wanx.aliyuncs.com/tmp/pressure/umbrella1.png',
3429
+ 'https://img.alicdn.com/imgextra/i3/O1CN01SfG4J41UYn9WNt4X1_!!6000000002530-49-tps-1696-960.webp',
3430
+ ],
3431
+ 'prompt': '${prompt}',
3432
+ 'size': [${size[0]}, ${size[1]}],
3433
+ 'n': 1,
3434
+ });</pre>`);
3435
+ if (imgResult && imgResult.list?.length) {
3436
+ echo.push(`<div>Reference Images:</div><div>
3437
+ <img src="https://cdn.wanx.aliyuncs.com/tmp/pressure/umbrella1.png" style="max-width: 256px; margin-right: 5px;" />
3438
+ <img src="https://img.alicdn.com/imgextra/i3/O1CN01SfG4J41UYn9WNt4X1_!!6000000002530-49-tps-1696-960.webp" style="max-width: 256px;" />
3439
+ </div>`);
3440
+ for (const img of imgResult.list) {
3441
+ echo.push(`<div><img src="${img.url.startsWith('http') ? img.url : 'data:image/png;base64,' + img.url}" style="max-width: 512px;" /></div>${lText.htmlescape(img.text || prompt)}`);
3442
+ }
3443
+ echo.push('<br>request: ' + imgResult.request, ', seed: ' + imgResult.seed);
3444
+ }
3445
+ else {
3446
+ echo.push('Failed');
3447
+ }
3448
+ break;
3449
+ }
3450
+ case 'text-to-video': {
3451
+ // --- 文生视频 ---
3452
+ const model = 'wan2.6-t2v';
3453
+ const vidResult = await ai.video({
3454
+ 'prompt': 'A cat playing with a ball of yarn',
3455
+ 'model': model,
3456
+ });
3457
+ echo.push(`<pre>await aiVid.video({
3458
+ 'prompt': 'A cat playing with a ball of yarn',
3459
+ 'model': '${model}',
3460
+ });</pre>`);
3461
+ if (vidResult) {
3462
+ echo.push(`<pre>${JSON.stringify(vidResult, null, 4)}</pre><a href="${this._config.const.urlBase}test/ai?action=video-poll&task=${vidResult.task}">poll</a>`);
3463
+ }
3464
+ else {
3465
+ echo.push('Failed');
3466
+ }
3467
+ break;
3468
+ }
3469
+ case 'image-to-video': {
3470
+ // --- 图生视频 ---
3471
+ echo.push('Not yet implemented');
3472
+ break;
3473
+ }
3474
+ case 'video-poll': {
3475
+ // --- 视频生成轮询 ---
3476
+ if (!this._get['task']) {
3477
+ echo.push('Task ID not found.');
3478
+ break;
3479
+ }
3480
+ const pollResult = await ai.poll({
3481
+ 'type': 'video',
3482
+ 'task': this._get['task'],
3483
+ });
3484
+ echo.push(`<pre>await ai.poll({
3485
+ 'type': 'video',
3486
+ 'task': '${this._get['task']}',
3487
+ });</pre>`);
3488
+ if (pollResult) {
3489
+ echo.push(`<pre>${JSON.stringify(pollResult, null, 4)}</pre>Result.`);
3490
+ }
3491
+ else {
3492
+ echo.push('Failed');
3493
+ }
3494
+ break;
3495
+ }
3496
+ default: {
3497
+ // --- CHAT ---
3498
+ let model = ai.service === lAi.ESERVICE.ALICN ? 'qwen-plus' : 'doubao-seed-2-0-mini-260215';
3499
+ if (ai.service === lAi.ESERVICE.AZURE) {
3500
+ model = 'gpt-4.1-nano';
3501
+ }
3502
+ const completion = await ai.chat({
3503
+ 'model': model,
3504
+ 'messages': [
3505
+ { 'role': 'system', 'content': 'You are Kebab, a friendly and knowledgeable assistant based on an open-source Node framework. You do not mention any model names or AI identity. You can chat casually, answer questions, and provide guidance naturally. Respond in a human-like, approachable manner, as if you are a helpful companion rather than a traditional AI assistant.' },
3506
+ { 'role': 'user', 'content': '你是谁?' },
3507
+ ],
3508
+ });
3509
+ echo.push(`<pre>await ai.chat({
3510
+ 'model': ${model},
3355
3511
  'messages': [
3356
3512
  { 'role': 'system', 'content': 'You are Kebab, a friendly and knowledgeable assistant based on an open-source Node framework. You do not mention any model names or AI identity. You can chat casually, answer questions, and provide guidance naturally. Respond in a human-like, approachable manner, as if you are a helpful companion rather than a traditional AI assistant.' },
3357
3513
  { 'role': 'user', 'content': '你是谁?' },
3358
3514
  ],
3359
3515
  });</pre>`);
3360
- if (completion) {
3361
- echo.push(JSON.stringify(completion.choices[0].message.content));
3362
- }
3363
- else {
3364
- echo.push('Failed');
3516
+ if (completion) {
3517
+ echo.push(JSON.stringify(completion.choices[0].message.content));
3518
+ }
3519
+ else {
3520
+ echo.push('Failed');
3521
+ }
3522
+ }
3365
3523
  }
3366
3524
  return echo.join('') + '<br><br>' + this._getEnd();
3367
3525
  }