@rei-standard/amsg-server 2.0.0-pre1 → 2.0.1

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
@@ -1,8 +1,8 @@
1
1
  # @rei-standard/amsg-server
2
2
 
3
- `@rei-standard/amsg-server` 是 ReiStandard 主动消息标准的服务端 SDK(v2.0.0-pre1),提供 Blob 租户配置、租户 token 鉴权和标准路由处理器。
3
+ `@rei-standard/amsg-server` 是 ReiStandard 主动消息标准的服务端 SDK(v2.0.1),提供 Blob 租户配置、租户 token 鉴权和标准路由处理器。
4
4
 
5
- ## v2.0.0-pre1 变更摘要
5
+ ## v2.0.1 变更摘要
6
6
 
7
7
  - 初始化流程合并为 `POST /api/v1/init-tenant`
8
8
  - 移除旧端点:`init-database`、`init-master-key`
@@ -50,6 +50,17 @@ const rei = await createReiServer({
50
50
  // GET /api/v1/messages -> rei.handlers.messages.GET
51
51
  ```
52
52
 
53
+ ## AI 接口 `apiUrl` 约束
54
+
55
+ 当 `messageType` 为 `prompted` / `auto`,或 `instant` 使用 AI 配置时:
56
+
57
+ - `apiUrl` 必须是完整聊天端点(例如:`https://api.openai.com/v1/chat/completions`)。
58
+ - SDK 会自动做最小规范化:去首尾空白、去路径尾部多余 `/`。
59
+ - SDK **不会**自动补全 `/v1`、`/chat/completions` 等路径。
60
+ - `maxTokens` 为可选字段:传了就映射为 `max_tokens`;不传则不指定(由上游模型默认策略决定)。
61
+
62
+ 如果上游返回 `405 Method Not Allowed`,通常表示 `apiUrl` 指向了基础域名而非聊天端点,请优先检查配置值。
63
+
53
64
  ## 一体化初始化流程
54
65
 
55
66
  1. 管理员配置环境变量(VAPID + tenant secrets)
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/server/adapters/factory.js
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/server/adapters/factory.js
2
2
  async function createAdapter(config) {
3
3
  if (!config || !config.driver) {
4
4
  throw new Error(
@@ -66,6 +66,9 @@ function validateScheduleMessagePayload(payload) {
66
66
  if (payload.recurrenceType && !["none", "daily", "weekly"].includes(payload.recurrenceType)) {
67
67
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["recurrenceType"] } };
68
68
  }
69
+ if (payload.maxTokens !== void 0 && payload.maxTokens !== null && (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0)) {
70
+ return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["maxTokens"] } };
71
+ }
69
72
  if (payload.messageType === "fixed") {
70
73
  if (!payload.userMessage) {
71
74
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["userMessage (required for fixed type)"] } };
@@ -505,25 +508,66 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
505
508
  }
506
509
  }
507
510
  async function _callAI(payload) {
508
- const aiResponse = await fetch(payload.apiUrl, {
511
+ const normalizedApiUrl = normalizeAiApiUrl(payload.apiUrl);
512
+ const requestBody = buildAiRequestBody(payload);
513
+ const aiResponse = await fetch(normalizedApiUrl, {
509
514
  method: "POST",
510
515
  headers: {
511
516
  "Content-Type": "application/json",
512
517
  "Authorization": `Bearer ${payload.apiKey}`
513
518
  },
514
- body: JSON.stringify({
515
- model: payload.primaryModel,
516
- messages: [{ role: "user", content: payload.completePrompt }],
517
- max_tokens: 500,
518
- temperature: 0.8
519
- }),
519
+ body: JSON.stringify(requestBody),
520
520
  signal: AbortSignal.timeout(3e5)
521
521
  });
522
522
  if (!aiResponse.ok) {
523
- throw new Error(`AI API error: ${aiResponse.status} ${aiResponse.statusText}`);
523
+ if (aiResponse.status === 405) {
524
+ throw new Error(
525
+ `AI API error: 405 Method Not Allowed. apiUrl must point to a full chat endpoint (for example: /chat/completions). Received: ${normalizedApiUrl}`
526
+ );
527
+ }
528
+ throw new Error(
529
+ `AI API error: ${aiResponse.status} ${aiResponse.statusText || "Unknown Error"}. Request URL: ${normalizedApiUrl}`
530
+ );
524
531
  }
525
532
  const aiData = await aiResponse.json();
526
- return aiData.choices[0].message.content.trim();
533
+ const content = _optionalChain([aiData, 'optionalAccess', _ => _.choices, 'optionalAccess', _2 => _2[0], 'optionalAccess', _3 => _3.message, 'optionalAccess', _4 => _4.content]);
534
+ if (typeof content !== "string" || !content.trim()) {
535
+ throw new Error("AI API error: response missing choices[0].message.content");
536
+ }
537
+ return content.trim();
538
+ }
539
+ function buildAiRequestBody(payload) {
540
+ const requestBody = {
541
+ model: payload.primaryModel,
542
+ messages: [{ role: "user", content: payload.completePrompt }],
543
+ temperature: 0.8
544
+ };
545
+ if (payload.maxTokens === void 0 || payload.maxTokens === null) {
546
+ return requestBody;
547
+ }
548
+ if (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0) {
549
+ throw new Error("Invalid maxTokens: maxTokens must be a positive integer when provided.");
550
+ }
551
+ requestBody.max_tokens = payload.maxTokens;
552
+ return requestBody;
553
+ }
554
+ function normalizeAiApiUrl(apiUrl) {
555
+ if (typeof apiUrl !== "string" || !apiUrl.trim()) {
556
+ throw new Error(
557
+ "Invalid apiUrl: apiUrl is required. Please provide a full chat endpoint URL (for example: https://api.openai.com/v1/chat/completions)."
558
+ );
559
+ }
560
+ const trimmedApiUrl = apiUrl.trim();
561
+ let parsedUrl;
562
+ try {
563
+ parsedUrl = new URL(trimmedApiUrl);
564
+ } catch (e3) {
565
+ throw new Error(
566
+ `Invalid apiUrl: "${apiUrl}". Please provide a valid absolute URL that points to a full chat endpoint.`
567
+ );
568
+ }
569
+ parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, "") || "/";
570
+ return parsedUrl.toString();
527
571
  }
528
572
 
529
573
  // src/server/handlers/schedule-message.js
@@ -591,6 +635,7 @@ function createScheduleMessageHandler(ctx) {
591
635
  apiKey: payload.apiKey || null,
592
636
  primaryModel: payload.primaryModel || null,
593
637
  completePrompt: payload.completePrompt || null,
638
+ maxTokens: _nullishCoalesce(payload.maxTokens, () => ( null)),
594
639
  pushSubscription: payload.pushSubscription,
595
640
  metadata: payload.metadata || {}
596
641
  };
@@ -899,6 +944,9 @@ function createUpdateMessageHandler(ctx) {
899
944
  if (updates.recurrenceType && !["none", "daily", "weekly"].includes(updates.recurrenceType)) {
900
945
  return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["recurrenceType"] } } } };
901
946
  }
947
+ if (Object.prototype.hasOwnProperty.call(updates, "maxTokens") && updates.maxTokens !== null && (!Number.isInteger(updates.maxTokens) || updates.maxTokens <= 0)) {
948
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["maxTokens"] } } } };
949
+ }
902
950
  const existingTask = await db.getTaskByUuid(taskUuid, userId);
903
951
  if (!existingTask) {
904
952
  const taskStatus = await db.getTaskStatus(taskUuid, userId);
@@ -914,7 +962,8 @@ function createUpdateMessageHandler(ctx) {
914
962
  ...updates.userMessage && { userMessage: updates.userMessage },
915
963
  ...updates.recurrenceType && { recurrenceType: updates.recurrenceType },
916
964
  ...updates.avatarUrl && { avatarUrl: updates.avatarUrl },
917
- ...updates.metadata && { metadata: updates.metadata }
965
+ ...updates.metadata && { metadata: updates.metadata },
966
+ ...Object.prototype.hasOwnProperty.call(updates, "maxTokens") && { maxTokens: _nullishCoalesce(updates.maxTokens, () => ( null)) }
918
967
  };
919
968
  const encryptedPayload = encryptForStorage(JSON.stringify(updatedData), userKey);
920
969
  const extraFields = updates.nextSendAt ? { next_send_at: updates.nextSendAt } : void 0;
@@ -1106,7 +1155,7 @@ async function resolveBlobStore(namespace) {
1106
1155
  }
1107
1156
  try {
1108
1157
  const blobModule = await Promise.resolve().then(() => _interopRequireWildcard(require("@netlify/blobs")));
1109
- const getStore = blobModule.getStore || _optionalChain([blobModule, 'access', _ => _.default, 'optionalAccess', _2 => _2.getStore]);
1158
+ const getStore = blobModule.getStore || _optionalChain([blobModule, 'access', _5 => _5.default, 'optionalAccess', _6 => _6.getStore]);
1110
1159
  if (typeof getStore === "function") {
1111
1160
  return getStore({ name: namespace });
1112
1161
  }
@@ -1194,7 +1243,7 @@ function verifyTenantToken(token, secret, options = {}) {
1194
1243
  let payload;
1195
1244
  try {
1196
1245
  payload = JSON.parse(base64UrlDecode2(encodedPayload).toString("utf8"));
1197
- } catch (e3) {
1246
+ } catch (e4) {
1198
1247
  throw new Error("INVALID_TENANT_AUTH");
1199
1248
  }
1200
1249
  if (!payload || typeof payload !== "object" || payload.v !== 1) {
@@ -1251,7 +1300,7 @@ function parseTokenFromUrl(url) {
1251
1300
  try {
1252
1301
  const parsed = new URL(url, "https://dummy.local");
1253
1302
  return String(parsed.searchParams.get("token") || "").trim();
1254
- } catch (e4) {
1303
+ } catch (e5) {
1255
1304
  return "";
1256
1305
  }
1257
1306
  }
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import { createDecipheriv, createHash, randomBytes, createCipheriv, randomUUID,
2
2
 
3
3
  /**
4
4
  * Adapter Factory
5
- * ReiStandard SDK v2.0.0-pre1
5
+ * ReiStandard SDK v2.0.1
6
6
  *
7
7
  * Creates a database adapter instance based on the supplied configuration.
8
8
  *
@@ -35,12 +35,12 @@ async function createAdapter(config) {
35
35
 
36
36
  switch (config.driver) {
37
37
  case 'neon': {
38
- const { NeonAdapter } = await import('./neon-Ch9Abtfp.d.cts');
38
+ const { NeonAdapter } = await import('./neon-CJl66EGy.d.cts');
39
39
  return new NeonAdapter(config.connectionString);
40
40
  }
41
41
 
42
42
  case 'pg': {
43
- const { PgAdapter } = await import('./pg-BpYu3O81.d.cts');
43
+ const { PgAdapter } = await import('./pg-07S-u_H4.d.cts');
44
44
  return new PgAdapter(config.connectionString);
45
45
  }
46
46
 
@@ -54,7 +54,7 @@ async function createAdapter(config) {
54
54
 
55
55
  /**
56
56
  * Validation utility library (SDK version)
57
- * ReiStandard SDK v2.0.0-pre1
57
+ * ReiStandard SDK v2.0.1
58
58
  */
59
59
 
60
60
  /**
@@ -132,6 +132,14 @@ function validateScheduleMessagePayload(payload) {
132
132
  return { valid: false, errorCode: 'INVALID_PARAMETERS', errorMessage: '缺少必需参数或参数格式错误', details: { invalidFields: ['recurrenceType'] } };
133
133
  }
134
134
 
135
+ if (
136
+ payload.maxTokens !== undefined &&
137
+ payload.maxTokens !== null &&
138
+ (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0)
139
+ ) {
140
+ return { valid: false, errorCode: 'INVALID_PARAMETERS', errorMessage: '缺少必需参数或参数格式错误', details: { invalidFields: ['maxTokens'] } };
141
+ }
142
+
135
143
  if (payload.messageType === 'fixed') {
136
144
  if (!payload.userMessage) {
137
145
  return { valid: false, errorCode: 'INVALID_PARAMETERS', errorMessage: '缺少必需参数或参数格式错误', details: { missingFields: ['userMessage (required for fixed type)'] } };
@@ -320,7 +328,7 @@ function getHeader(headers = {}, name) {
320
328
 
321
329
  /**
322
330
  * Handler: init-tenant
323
- * ReiStandard SDK v2.0.0-pre1
331
+ * ReiStandard SDK v2.0.1
324
332
  *
325
333
  * @param {Object} ctx - Server context.
326
334
  * @returns {{ POST: function }}
@@ -456,7 +464,7 @@ function createInitTenantHandler(ctx) {
456
464
 
457
465
  /**
458
466
  * Encryption utility library (SDK version)
459
- * ReiStandard SDK v2.0.0-pre1
467
+ * ReiStandard SDK v2.0.1
460
468
  *
461
469
  * Wraps AES-256-GCM operations for request/response and storage encryption.
462
470
  */
@@ -558,7 +566,7 @@ function decryptFromStorage(encryptedText, encryptionKey) {
558
566
 
559
567
  /**
560
568
  * Handler: get-user-key
561
- * ReiStandard SDK v2.0.0-pre1
569
+ * ReiStandard SDK v2.0.1
562
570
  *
563
571
  * @param {Object} ctx - Server context.
564
572
  * @returns {{ GET: function }}
@@ -627,7 +635,7 @@ function isUniqueViolation(error) {
627
635
 
628
636
  /**
629
637
  * Message Processor (SDK version)
630
- * ReiStandard SDK v2.0.0-pre1
638
+ * ReiStandard SDK v2.0.1
631
639
  *
632
640
  * Handles single message content generation and Web Push delivery.
633
641
  * Receives its dependencies (encryption helpers, webpush, VAPID config)
@@ -824,32 +832,110 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
824
832
  * @private
825
833
  */
826
834
  async function _callAI(payload) {
827
- const aiResponse = await fetch(payload.apiUrl, {
835
+ const normalizedApiUrl = normalizeAiApiUrl(payload.apiUrl);
836
+ const requestBody = buildAiRequestBody(payload);
837
+
838
+ const aiResponse = await fetch(normalizedApiUrl, {
828
839
  method: 'POST',
829
840
  headers: {
830
841
  'Content-Type': 'application/json',
831
842
  'Authorization': `Bearer ${payload.apiKey}`
832
843
  },
833
- body: JSON.stringify({
834
- model: payload.primaryModel,
835
- messages: [{ role: 'user', content: payload.completePrompt }],
836
- max_tokens: 500,
837
- temperature: 0.8
838
- }),
844
+ body: JSON.stringify(requestBody),
839
845
  signal: AbortSignal.timeout(300000)
840
846
  });
841
847
 
842
848
  if (!aiResponse.ok) {
843
- throw new Error(`AI API error: ${aiResponse.status} ${aiResponse.statusText}`);
849
+ if (aiResponse.status === 405) {
850
+ throw new Error(
851
+ `AI API error: 405 Method Not Allowed. ` +
852
+ `apiUrl must point to a full chat endpoint (for example: /chat/completions). ` +
853
+ `Received: ${normalizedApiUrl}`
854
+ );
855
+ }
856
+
857
+ throw new Error(
858
+ `AI API error: ${aiResponse.status} ${aiResponse.statusText || 'Unknown Error'}. ` +
859
+ `Request URL: ${normalizedApiUrl}`
860
+ );
844
861
  }
845
862
 
846
863
  const aiData = await aiResponse.json();
847
- return aiData.choices[0].message.content.trim();
864
+ const content = aiData?.choices?.[0]?.message?.content;
865
+ if (typeof content !== 'string' || !content.trim()) {
866
+ throw new Error('AI API error: response missing choices[0].message.content');
867
+ }
868
+
869
+ return content.trim();
870
+ }
871
+
872
+ /**
873
+ * Build OpenAI-compatible request body.
874
+ *
875
+ * `max_tokens` is optional:
876
+ * - include it only when payload.maxTokens is provided
877
+ * - omit it when payload.maxTokens is undefined / null
878
+ *
879
+ * @param {Object} payload
880
+ * @returns {Object}
881
+ */
882
+ function buildAiRequestBody(payload) {
883
+ const requestBody = {
884
+ model: payload.primaryModel,
885
+ messages: [{ role: 'user', content: payload.completePrompt }],
886
+ temperature: 0.8
887
+ };
888
+
889
+ if (payload.maxTokens === undefined || payload.maxTokens === null) {
890
+ return requestBody;
891
+ }
892
+
893
+ if (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0) {
894
+ throw new Error('Invalid maxTokens: maxTokens must be a positive integer when provided.');
895
+ }
896
+
897
+ requestBody.max_tokens = payload.maxTokens;
898
+ return requestBody;
899
+ }
900
+
901
+ /**
902
+ * Normalize AI API URL with minimal, non-breaking rules:
903
+ * - trim whitespace
904
+ * - remove redundant trailing slashes from pathname
905
+ * - keep query string untouched
906
+ *
907
+ * Note: This function does NOT auto-append /v1 or /chat/completions.
908
+ *
909
+ * @param {string} apiUrl
910
+ * @returns {string}
911
+ */
912
+ function normalizeAiApiUrl(apiUrl) {
913
+ if (typeof apiUrl !== 'string' || !apiUrl.trim()) {
914
+ throw new Error(
915
+ 'Invalid apiUrl: apiUrl is required. ' +
916
+ 'Please provide a full chat endpoint URL (for example: https://api.openai.com/v1/chat/completions).'
917
+ );
918
+ }
919
+
920
+ const trimmedApiUrl = apiUrl.trim();
921
+ let parsedUrl;
922
+
923
+ try {
924
+ parsedUrl = new URL(trimmedApiUrl);
925
+ } catch {
926
+ throw new Error(
927
+ `Invalid apiUrl: "${apiUrl}". ` +
928
+ 'Please provide a valid absolute URL that points to a full chat endpoint.'
929
+ );
930
+ }
931
+
932
+ parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, '') || '/';
933
+ return parsedUrl.toString();
848
934
  }
849
935
 
850
936
  /**
851
937
  * Handler: schedule-message
852
- * ReiStandard SDK v2.0.0-pre1
938
+ * ReiStandard SDK v2.0.1
853
939
  *
854
940
  * @param {Object} ctx - Server context.
855
941
  * @returns {{ POST: function }}
@@ -933,6 +1019,7 @@ function createScheduleMessageHandler(ctx) {
933
1019
  apiKey: payload.apiKey || null,
934
1020
  primaryModel: payload.primaryModel || null,
935
1021
  completePrompt: payload.completePrompt || null,
1022
+ maxTokens: payload.maxTokens ?? null,
936
1023
  pushSubscription: payload.pushSubscription,
937
1024
  metadata: payload.metadata || {}
938
1025
  };
@@ -1046,7 +1133,7 @@ function createScheduleMessageHandler(ctx) {
1046
1133
 
1047
1134
  /**
1048
1135
  * Handler: send-notifications
1049
- * ReiStandard SDK v2.0.0-pre1
1136
+ * ReiStandard SDK v2.0.1
1050
1137
  *
1051
1138
  * @param {Object} ctx - Server context.
1052
1139
  * @returns {{ POST: function }}
@@ -1236,7 +1323,7 @@ function createSendNotificationsHandler(ctx) {
1236
1323
 
1237
1324
  /**
1238
1325
  * Handler: update-message
1239
- * ReiStandard SDK v2.0.0-pre1
1326
+ * ReiStandard SDK v2.0.1
1240
1327
  *
1241
1328
  * @param {Object} ctx - Server context.
1242
1329
  * @returns {{ PUT: function }}
@@ -1306,6 +1393,14 @@ function createUpdateMessageHandler(ctx) {
1306
1393
  return { status: 400, body: { success: false, error: { code: 'INVALID_UPDATE_DATA', message: '更新数据格式错误', details: { invalidFields: ['recurrenceType'] } } } };
1307
1394
  }
1308
1395
 
1396
+ if (
1397
+ Object.prototype.hasOwnProperty.call(updates, 'maxTokens') &&
1398
+ updates.maxTokens !== null &&
1399
+ (!Number.isInteger(updates.maxTokens) || updates.maxTokens <= 0)
1400
+ ) {
1401
+ return { status: 400, body: { success: false, error: { code: 'INVALID_UPDATE_DATA', message: '更新数据格式错误', details: { invalidFields: ['maxTokens'] } } } };
1402
+ }
1403
+
1309
1404
  // Fetch existing task
1310
1405
  const existingTask = await db.getTaskByUuid(taskUuid, userId);
1311
1406
 
@@ -1325,7 +1420,8 @@ function createUpdateMessageHandler(ctx) {
1325
1420
  ...(updates.userMessage && { userMessage: updates.userMessage }),
1326
1421
  ...(updates.recurrenceType && { recurrenceType: updates.recurrenceType }),
1327
1422
  ...(updates.avatarUrl && { avatarUrl: updates.avatarUrl }),
1328
- ...(updates.metadata && { metadata: updates.metadata })
1423
+ ...(updates.metadata && { metadata: updates.metadata }),
1424
+ ...(Object.prototype.hasOwnProperty.call(updates, 'maxTokens') && { maxTokens: updates.maxTokens ?? null })
1329
1425
  };
1330
1426
 
1331
1427
  const encryptedPayload = encryptForStorage(JSON.stringify(updatedData), userKey);
@@ -1355,7 +1451,7 @@ function createUpdateMessageHandler(ctx) {
1355
1451
 
1356
1452
  /**
1357
1453
  * Handler: cancel-message
1358
- * ReiStandard SDK v2.0.0-pre1
1454
+ * ReiStandard SDK v2.0.1
1359
1455
  *
1360
1456
  * @param {Object} ctx - Server context.
1361
1457
  * @returns {{ DELETE: function }}
@@ -1408,7 +1504,7 @@ function createCancelMessageHandler(ctx) {
1408
1504
 
1409
1505
  /**
1410
1506
  * Handler: messages
1411
- * ReiStandard SDK v2.0.0-pre1
1507
+ * ReiStandard SDK v2.0.1
1412
1508
  *
1413
1509
  * @param {Object} ctx - Server context.
1414
1510
  * @returns {{ GET: function }}
@@ -1911,7 +2007,7 @@ function createTenantContextManager(options) {
1911
2007
 
1912
2008
  /**
1913
2009
  * ReiStandard Server SDK Entry Point
1914
- * v2.0.0-pre1
2010
+ * v2.0.1
1915
2011
  *
1916
2012
  * Usage:
1917
2013
  * import { createReiServer } from '@rei-standard/amsg-server';
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { createDecipheriv, createHash, randomBytes, createCipheriv, randomUUID,
2
2
 
3
3
  /**
4
4
  * Adapter Factory
5
- * ReiStandard SDK v2.0.0-pre1
5
+ * ReiStandard SDK v2.0.1
6
6
  *
7
7
  * Creates a database adapter instance based on the supplied configuration.
8
8
  *
@@ -35,12 +35,12 @@ async function createAdapter(config) {
35
35
 
36
36
  switch (config.driver) {
37
37
  case 'neon': {
38
- const { NeonAdapter } = await import('./neon-DyBfYFkz.d.ts');
38
+ const { NeonAdapter } = await import('./neon-DYvGnCzx.d.ts');
39
39
  return new NeonAdapter(config.connectionString);
40
40
  }
41
41
 
42
42
  case 'pg': {
43
- const { PgAdapter } = await import('./pg-DJWS_HZ3.d.ts');
43
+ const { PgAdapter } = await import('./pg-Du-pN_UT.d.ts');
44
44
  return new PgAdapter(config.connectionString);
45
45
  }
46
46
 
@@ -54,7 +54,7 @@ async function createAdapter(config) {
54
54
 
55
55
  /**
56
56
  * Validation utility library (SDK version)
57
- * ReiStandard SDK v2.0.0-pre1
57
+ * ReiStandard SDK v2.0.1
58
58
  */
59
59
 
60
60
  /**
@@ -132,6 +132,14 @@ function validateScheduleMessagePayload(payload) {
132
132
  return { valid: false, errorCode: 'INVALID_PARAMETERS', errorMessage: '缺少必需参数或参数格式错误', details: { invalidFields: ['recurrenceType'] } };
133
133
  }
134
134
 
135
+ if (
136
+ payload.maxTokens !== undefined &&
137
+ payload.maxTokens !== null &&
138
+ (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0)
139
+ ) {
140
+ return { valid: false, errorCode: 'INVALID_PARAMETERS', errorMessage: '缺少必需参数或参数格式错误', details: { invalidFields: ['maxTokens'] } };
141
+ }
142
+
135
143
  if (payload.messageType === 'fixed') {
136
144
  if (!payload.userMessage) {
137
145
  return { valid: false, errorCode: 'INVALID_PARAMETERS', errorMessage: '缺少必需参数或参数格式错误', details: { missingFields: ['userMessage (required for fixed type)'] } };
@@ -320,7 +328,7 @@ function getHeader(headers = {}, name) {
320
328
 
321
329
  /**
322
330
  * Handler: init-tenant
323
- * ReiStandard SDK v2.0.0-pre1
331
+ * ReiStandard SDK v2.0.1
324
332
  *
325
333
  * @param {Object} ctx - Server context.
326
334
  * @returns {{ POST: function }}
@@ -456,7 +464,7 @@ function createInitTenantHandler(ctx) {
456
464
 
457
465
  /**
458
466
  * Encryption utility library (SDK version)
459
- * ReiStandard SDK v2.0.0-pre1
467
+ * ReiStandard SDK v2.0.1
460
468
  *
461
469
  * Wraps AES-256-GCM operations for request/response and storage encryption.
462
470
  */
@@ -558,7 +566,7 @@ function decryptFromStorage(encryptedText, encryptionKey) {
558
566
 
559
567
  /**
560
568
  * Handler: get-user-key
561
- * ReiStandard SDK v2.0.0-pre1
569
+ * ReiStandard SDK v2.0.1
562
570
  *
563
571
  * @param {Object} ctx - Server context.
564
572
  * @returns {{ GET: function }}
@@ -627,7 +635,7 @@ function isUniqueViolation(error) {
627
635
 
628
636
  /**
629
637
  * Message Processor (SDK version)
630
- * ReiStandard SDK v2.0.0-pre1
638
+ * ReiStandard SDK v2.0.1
631
639
  *
632
640
  * Handles single message content generation and Web Push delivery.
633
641
  * Receives its dependencies (encryption helpers, webpush, VAPID config)
@@ -824,32 +832,110 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
824
832
  * @private
825
833
  */
826
834
  async function _callAI(payload) {
827
- const aiResponse = await fetch(payload.apiUrl, {
835
+ const normalizedApiUrl = normalizeAiApiUrl(payload.apiUrl);
836
+ const requestBody = buildAiRequestBody(payload);
837
+
838
+ const aiResponse = await fetch(normalizedApiUrl, {
828
839
  method: 'POST',
829
840
  headers: {
830
841
  'Content-Type': 'application/json',
831
842
  'Authorization': `Bearer ${payload.apiKey}`
832
843
  },
833
- body: JSON.stringify({
834
- model: payload.primaryModel,
835
- messages: [{ role: 'user', content: payload.completePrompt }],
836
- max_tokens: 500,
837
- temperature: 0.8
838
- }),
844
+ body: JSON.stringify(requestBody),
839
845
  signal: AbortSignal.timeout(300000)
840
846
  });
841
847
 
842
848
  if (!aiResponse.ok) {
843
- throw new Error(`AI API error: ${aiResponse.status} ${aiResponse.statusText}`);
849
+ if (aiResponse.status === 405) {
850
+ throw new Error(
851
+ `AI API error: 405 Method Not Allowed. ` +
852
+ `apiUrl must point to a full chat endpoint (for example: /chat/completions). ` +
853
+ `Received: ${normalizedApiUrl}`
854
+ );
855
+ }
856
+
857
+ throw new Error(
858
+ `AI API error: ${aiResponse.status} ${aiResponse.statusText || 'Unknown Error'}. ` +
859
+ `Request URL: ${normalizedApiUrl}`
860
+ );
844
861
  }
845
862
 
846
863
  const aiData = await aiResponse.json();
847
- return aiData.choices[0].message.content.trim();
864
+ const content = aiData?.choices?.[0]?.message?.content;
865
+ if (typeof content !== 'string' || !content.trim()) {
866
+ throw new Error('AI API error: response missing choices[0].message.content');
867
+ }
868
+
869
+ return content.trim();
870
+ }
871
+
872
+ /**
873
+ * Build OpenAI-compatible request body.
874
+ *
875
+ * `max_tokens` is optional:
876
+ * - include it only when payload.maxTokens is provided
877
+ * - omit it when payload.maxTokens is undefined / null
878
+ *
879
+ * @param {Object} payload
880
+ * @returns {Object}
881
+ */
882
+ function buildAiRequestBody(payload) {
883
+ const requestBody = {
884
+ model: payload.primaryModel,
885
+ messages: [{ role: 'user', content: payload.completePrompt }],
886
+ temperature: 0.8
887
+ };
888
+
889
+ if (payload.maxTokens === undefined || payload.maxTokens === null) {
890
+ return requestBody;
891
+ }
892
+
893
+ if (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0) {
894
+ throw new Error('Invalid maxTokens: maxTokens must be a positive integer when provided.');
895
+ }
896
+
897
+ requestBody.max_tokens = payload.maxTokens;
898
+ return requestBody;
899
+ }
900
+
901
+ /**
902
+ * Normalize AI API URL with minimal, non-breaking rules:
903
+ * - trim whitespace
904
+ * - remove redundant trailing slashes from pathname
905
+ * - keep query string untouched
906
+ *
907
+ * Note: This function does NOT auto-append /v1 or /chat/completions.
908
+ *
909
+ * @param {string} apiUrl
910
+ * @returns {string}
911
+ */
912
+ function normalizeAiApiUrl(apiUrl) {
913
+ if (typeof apiUrl !== 'string' || !apiUrl.trim()) {
914
+ throw new Error(
915
+ 'Invalid apiUrl: apiUrl is required. ' +
916
+ 'Please provide a full chat endpoint URL (for example: https://api.openai.com/v1/chat/completions).'
917
+ );
918
+ }
919
+
920
+ const trimmedApiUrl = apiUrl.trim();
921
+ let parsedUrl;
922
+
923
+ try {
924
+ parsedUrl = new URL(trimmedApiUrl);
925
+ } catch {
926
+ throw new Error(
927
+ `Invalid apiUrl: "${apiUrl}". ` +
928
+ 'Please provide a valid absolute URL that points to a full chat endpoint.'
929
+ );
930
+ }
931
+
932
+ parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, '') || '/';
933
+ return parsedUrl.toString();
848
934
  }
849
935
 
850
936
  /**
851
937
  * Handler: schedule-message
852
- * ReiStandard SDK v2.0.0-pre1
938
+ * ReiStandard SDK v2.0.1
853
939
  *
854
940
  * @param {Object} ctx - Server context.
855
941
  * @returns {{ POST: function }}
@@ -933,6 +1019,7 @@ function createScheduleMessageHandler(ctx) {
933
1019
  apiKey: payload.apiKey || null,
934
1020
  primaryModel: payload.primaryModel || null,
935
1021
  completePrompt: payload.completePrompt || null,
1022
+ maxTokens: payload.maxTokens ?? null,
936
1023
  pushSubscription: payload.pushSubscription,
937
1024
  metadata: payload.metadata || {}
938
1025
  };
@@ -1046,7 +1133,7 @@ function createScheduleMessageHandler(ctx) {
1046
1133
 
1047
1134
  /**
1048
1135
  * Handler: send-notifications
1049
- * ReiStandard SDK v2.0.0-pre1
1136
+ * ReiStandard SDK v2.0.1
1050
1137
  *
1051
1138
  * @param {Object} ctx - Server context.
1052
1139
  * @returns {{ POST: function }}
@@ -1236,7 +1323,7 @@ function createSendNotificationsHandler(ctx) {
1236
1323
 
1237
1324
  /**
1238
1325
  * Handler: update-message
1239
- * ReiStandard SDK v2.0.0-pre1
1326
+ * ReiStandard SDK v2.0.1
1240
1327
  *
1241
1328
  * @param {Object} ctx - Server context.
1242
1329
  * @returns {{ PUT: function }}
@@ -1306,6 +1393,14 @@ function createUpdateMessageHandler(ctx) {
1306
1393
  return { status: 400, body: { success: false, error: { code: 'INVALID_UPDATE_DATA', message: '更新数据格式错误', details: { invalidFields: ['recurrenceType'] } } } };
1307
1394
  }
1308
1395
 
1396
+ if (
1397
+ Object.prototype.hasOwnProperty.call(updates, 'maxTokens') &&
1398
+ updates.maxTokens !== null &&
1399
+ (!Number.isInteger(updates.maxTokens) || updates.maxTokens <= 0)
1400
+ ) {
1401
+ return { status: 400, body: { success: false, error: { code: 'INVALID_UPDATE_DATA', message: '更新数据格式错误', details: { invalidFields: ['maxTokens'] } } } };
1402
+ }
1403
+
1309
1404
  // Fetch existing task
1310
1405
  const existingTask = await db.getTaskByUuid(taskUuid, userId);
1311
1406
 
@@ -1325,7 +1420,8 @@ function createUpdateMessageHandler(ctx) {
1325
1420
  ...(updates.userMessage && { userMessage: updates.userMessage }),
1326
1421
  ...(updates.recurrenceType && { recurrenceType: updates.recurrenceType }),
1327
1422
  ...(updates.avatarUrl && { avatarUrl: updates.avatarUrl }),
1328
- ...(updates.metadata && { metadata: updates.metadata })
1423
+ ...(updates.metadata && { metadata: updates.metadata }),
1424
+ ...(Object.prototype.hasOwnProperty.call(updates, 'maxTokens') && { maxTokens: updates.maxTokens ?? null })
1329
1425
  };
1330
1426
 
1331
1427
  const encryptedPayload = encryptForStorage(JSON.stringify(updatedData), userKey);
@@ -1355,7 +1451,7 @@ function createUpdateMessageHandler(ctx) {
1355
1451
 
1356
1452
  /**
1357
1453
  * Handler: cancel-message
1358
- * ReiStandard SDK v2.0.0-pre1
1454
+ * ReiStandard SDK v2.0.1
1359
1455
  *
1360
1456
  * @param {Object} ctx - Server context.
1361
1457
  * @returns {{ DELETE: function }}
@@ -1408,7 +1504,7 @@ function createCancelMessageHandler(ctx) {
1408
1504
 
1409
1505
  /**
1410
1506
  * Handler: messages
1411
- * ReiStandard SDK v2.0.0-pre1
1507
+ * ReiStandard SDK v2.0.1
1412
1508
  *
1413
1509
  * @param {Object} ctx - Server context.
1414
1510
  * @returns {{ GET: function }}
@@ -1911,7 +2007,7 @@ function createTenantContextManager(options) {
1911
2007
 
1912
2008
  /**
1913
2009
  * ReiStandard Server SDK Entry Point
1914
- * v2.0.0-pre1
2010
+ * v2.0.1
1915
2011
  *
1916
2012
  * Usage:
1917
2013
  * import { createReiServer } from '@rei-standard/amsg-server';
package/dist/index.mjs CHANGED
@@ -66,6 +66,9 @@ function validateScheduleMessagePayload(payload) {
66
66
  if (payload.recurrenceType && !["none", "daily", "weekly"].includes(payload.recurrenceType)) {
67
67
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["recurrenceType"] } };
68
68
  }
69
+ if (payload.maxTokens !== void 0 && payload.maxTokens !== null && (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0)) {
70
+ return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["maxTokens"] } };
71
+ }
69
72
  if (payload.messageType === "fixed") {
70
73
  if (!payload.userMessage) {
71
74
  return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["userMessage (required for fixed type)"] } };
@@ -505,25 +508,66 @@ async function processMessagesByUuid(uuid, ctx, maxRetries = 2, userId, provided
505
508
  }
506
509
  }
507
510
  async function _callAI(payload) {
508
- const aiResponse = await fetch(payload.apiUrl, {
511
+ const normalizedApiUrl = normalizeAiApiUrl(payload.apiUrl);
512
+ const requestBody = buildAiRequestBody(payload);
513
+ const aiResponse = await fetch(normalizedApiUrl, {
509
514
  method: "POST",
510
515
  headers: {
511
516
  "Content-Type": "application/json",
512
517
  "Authorization": `Bearer ${payload.apiKey}`
513
518
  },
514
- body: JSON.stringify({
515
- model: payload.primaryModel,
516
- messages: [{ role: "user", content: payload.completePrompt }],
517
- max_tokens: 500,
518
- temperature: 0.8
519
- }),
519
+ body: JSON.stringify(requestBody),
520
520
  signal: AbortSignal.timeout(3e5)
521
521
  });
522
522
  if (!aiResponse.ok) {
523
- throw new Error(`AI API error: ${aiResponse.status} ${aiResponse.statusText}`);
523
+ if (aiResponse.status === 405) {
524
+ throw new Error(
525
+ `AI API error: 405 Method Not Allowed. apiUrl must point to a full chat endpoint (for example: /chat/completions). Received: ${normalizedApiUrl}`
526
+ );
527
+ }
528
+ throw new Error(
529
+ `AI API error: ${aiResponse.status} ${aiResponse.statusText || "Unknown Error"}. Request URL: ${normalizedApiUrl}`
530
+ );
524
531
  }
525
532
  const aiData = await aiResponse.json();
526
- return aiData.choices[0].message.content.trim();
533
+ const content = aiData?.choices?.[0]?.message?.content;
534
+ if (typeof content !== "string" || !content.trim()) {
535
+ throw new Error("AI API error: response missing choices[0].message.content");
536
+ }
537
+ return content.trim();
538
+ }
539
+ function buildAiRequestBody(payload) {
540
+ const requestBody = {
541
+ model: payload.primaryModel,
542
+ messages: [{ role: "user", content: payload.completePrompt }],
543
+ temperature: 0.8
544
+ };
545
+ if (payload.maxTokens === void 0 || payload.maxTokens === null) {
546
+ return requestBody;
547
+ }
548
+ if (!Number.isInteger(payload.maxTokens) || payload.maxTokens <= 0) {
549
+ throw new Error("Invalid maxTokens: maxTokens must be a positive integer when provided.");
550
+ }
551
+ requestBody.max_tokens = payload.maxTokens;
552
+ return requestBody;
553
+ }
554
+ function normalizeAiApiUrl(apiUrl) {
555
+ if (typeof apiUrl !== "string" || !apiUrl.trim()) {
556
+ throw new Error(
557
+ "Invalid apiUrl: apiUrl is required. Please provide a full chat endpoint URL (for example: https://api.openai.com/v1/chat/completions)."
558
+ );
559
+ }
560
+ const trimmedApiUrl = apiUrl.trim();
561
+ let parsedUrl;
562
+ try {
563
+ parsedUrl = new URL(trimmedApiUrl);
564
+ } catch {
565
+ throw new Error(
566
+ `Invalid apiUrl: "${apiUrl}". Please provide a valid absolute URL that points to a full chat endpoint.`
567
+ );
568
+ }
569
+ parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, "") || "/";
570
+ return parsedUrl.toString();
527
571
  }
528
572
 
529
573
  // src/server/handlers/schedule-message.js
@@ -591,6 +635,7 @@ function createScheduleMessageHandler(ctx) {
591
635
  apiKey: payload.apiKey || null,
592
636
  primaryModel: payload.primaryModel || null,
593
637
  completePrompt: payload.completePrompt || null,
638
+ maxTokens: payload.maxTokens ?? null,
594
639
  pushSubscription: payload.pushSubscription,
595
640
  metadata: payload.metadata || {}
596
641
  };
@@ -899,6 +944,9 @@ function createUpdateMessageHandler(ctx) {
899
944
  if (updates.recurrenceType && !["none", "daily", "weekly"].includes(updates.recurrenceType)) {
900
945
  return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["recurrenceType"] } } } };
901
946
  }
947
+ if (Object.prototype.hasOwnProperty.call(updates, "maxTokens") && updates.maxTokens !== null && (!Number.isInteger(updates.maxTokens) || updates.maxTokens <= 0)) {
948
+ return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["maxTokens"] } } } };
949
+ }
902
950
  const existingTask = await db.getTaskByUuid(taskUuid, userId);
903
951
  if (!existingTask) {
904
952
  const taskStatus = await db.getTaskStatus(taskUuid, userId);
@@ -914,7 +962,8 @@ function createUpdateMessageHandler(ctx) {
914
962
  ...updates.userMessage && { userMessage: updates.userMessage },
915
963
  ...updates.recurrenceType && { recurrenceType: updates.recurrenceType },
916
964
  ...updates.avatarUrl && { avatarUrl: updates.avatarUrl },
917
- ...updates.metadata && { metadata: updates.metadata }
965
+ ...updates.metadata && { metadata: updates.metadata },
966
+ ...Object.prototype.hasOwnProperty.call(updates, "maxTokens") && { maxTokens: updates.maxTokens ?? null }
918
967
  };
919
968
  const encryptedPayload = encryptForStorage(JSON.stringify(updatedData), userKey);
920
969
  const extraFields = updates.nextSendAt ? { next_send_at: updates.nextSendAt } : void 0;
@@ -1,9 +1,9 @@
1
1
  import { neon } from '@neondatabase/serverless';
2
- import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-D-ldTuIC.js';
2
+ import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-C8OnYk6j.cjs';
3
3
 
4
4
  /**
5
5
  * Neon Serverless Database Adapter
6
- * ReiStandard SDK v2.0.0-pre1
6
+ * ReiStandard SDK v2.0.1
7
7
  *
8
8
  * @implements {import('./interface.js').DbAdapter}
9
9
  */
@@ -1,9 +1,9 @@
1
1
  import { neon } from '@neondatabase/serverless';
2
- import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-D-ldTuIC.cjs';
2
+ import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-C8OnYk6j.js';
3
3
 
4
4
  /**
5
5
  * Neon Serverless Database Adapter
6
- * ReiStandard SDK v2.0.0-pre1
6
+ * ReiStandard SDK v2.0.1
7
7
  *
8
8
  * @implements {import('./interface.js').DbAdapter}
9
9
  */
@@ -1,9 +1,9 @@
1
1
  import { Pool } from 'pg';
2
- import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-D-ldTuIC.js';
2
+ import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-C8OnYk6j.cjs';
3
3
 
4
4
  /**
5
5
  * PostgreSQL (pg) Database Adapter
6
- * ReiStandard SDK v2.0.0-pre1
6
+ * ReiStandard SDK v2.0.1
7
7
  *
8
8
  * Uses the standard 'pg' npm package.
9
9
  *
@@ -1,9 +1,9 @@
1
1
  import { Pool } from 'pg';
2
- import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-D-ldTuIC.cjs';
2
+ import { T as TABLE_SQL, I as INDEXES, V as VERIFY_TABLE_SQL, C as COLUMNS_SQL } from './schema-C8OnYk6j.js';
3
3
 
4
4
  /**
5
5
  * PostgreSQL (pg) Database Adapter
6
- * ReiStandard SDK v2.0.0-pre1
6
+ * ReiStandard SDK v2.0.1
7
7
  *
8
8
  * Uses the standard 'pg' npm package.
9
9
  *
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Shared SQL schema constants
3
- * ReiStandard SDK v2.0.0-pre1
3
+ * ReiStandard SDK v2.0.1
4
4
  */
5
5
 
6
6
  const TABLE_SQL = `
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Shared SQL schema constants
3
- * ReiStandard SDK v2.0.0-pre1
3
+ * ReiStandard SDK v2.0.1
4
4
  */
5
5
 
6
6
  const TABLE_SQL = `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rei-standard/amsg-server",
3
- "version": "2.0.0-pre1",
3
+ "version": "2.0.1",
4
4
  "description": "ReiStandard Active Messaging server SDK with pluggable database adapters",
5
5
  "repository": {
6
6
  "type": "git",