@rei-standard/amsg-server 2.6.0-next.2 → 2.6.0-next.21
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/LICENSE +21 -0
- package/README.md +599 -24
- package/dist/adapters/d1.d.ts +416 -0
- package/dist/adapters/factory.d.ts +39 -0
- package/dist/adapters/interface.d.ts +306 -0
- package/dist/adapters/neon.d.ts +88 -0
- package/dist/adapters/pg-shared.d.ts +161 -0
- package/dist/adapters/pg.d.ts +84 -0
- package/dist/adapters/schema.d.ts +57 -0
- package/dist/adapters/schema.sqlite.d.ts +49 -0
- package/dist/chunk-7NMQFTDJ.cjs +148 -0
- package/dist/chunk-E7OWP3VL.cjs +135 -0
- package/dist/chunk-GN44PST5.mjs +148 -0
- package/dist/chunk-KOJQVOYS.mjs +5847 -0
- package/dist/chunk-QHVID3V2.cjs +5847 -0
- package/dist/chunk-ZGGF4GMA.mjs +135 -0
- package/dist/cloudflare/single-user-worker.d.ts +37 -0
- package/dist/cloudflare.cjs +39 -2
- package/dist/cloudflare.d.ts +10 -2
- package/dist/cloudflare.mjs +40 -3
- package/dist/handlers/cancel-message.d.ts +3 -0
- package/dist/handlers/capabilities.d.ts +4 -0
- package/dist/handlers/client-state.d.ts +7 -0
- package/dist/handlers/get-message.d.ts +3 -0
- package/dist/handlers/get-user-key.d.ts +3 -0
- package/dist/handlers/init-tenant.d.ts +23 -0
- package/dist/handlers/llm-credentials.d.ts +5 -0
- package/dist/handlers/messages.d.ts +3 -0
- package/dist/handlers/outbox.d.ts +7 -0
- package/dist/handlers/push-subscription.d.ts +5 -0
- package/dist/handlers/schedule-message.d.ts +3 -0
- package/dist/handlers/send-notifications.d.ts +3 -0
- package/dist/handlers/single-user-init.d.ts +13 -0
- package/dist/handlers/update-message.d.ts +3 -0
- package/dist/handlers/vapid-public-key.d.ts +19 -0
- package/dist/index.cjs +133 -44
- package/dist/index.d.cts +131 -766
- package/dist/index.d.ts +131 -766
- package/dist/index.mjs +125 -36
- package/dist/lib/agentic-fire.d.ts +108 -0
- package/dist/lib/client-state-store.d.ts +80 -0
- package/dist/lib/constant-time.d.ts +14 -0
- package/dist/lib/db-errors.d.ts +10 -0
- package/dist/lib/encryption.d.ts +48 -0
- package/dist/lib/errors.d.ts +215 -0
- package/dist/lib/llm-credentials-store.d.ts +147 -0
- package/dist/lib/llm.d.ts +1 -0
- package/dist/lib/message-processor.d.ts +73 -0
- package/dist/lib/outbox-store.d.ts +95 -0
- package/dist/lib/push-subscription-store.d.ts +94 -0
- package/dist/lib/recurrence.d.ts +51 -0
- package/dist/lib/request.d.ts +182 -0
- package/dist/lib/result-emitter.d.ts +54 -0
- package/dist/lib/run-tick.d.ts +115 -0
- package/dist/lib/schema-version.d.ts +50 -0
- package/dist/lib/state-accessors.d.ts +70 -0
- package/dist/lib/state-chunks.d.ts +55 -0
- package/dist/lib/task-projection.d.ts +92 -0
- package/dist/lib/validation.d.ts +97 -0
- package/dist/lib/version.d.ts +7 -0
- package/dist/lib/webcrypto-utils.d.ts +1 -0
- package/dist/lib/webpush-webcrypto.d.ts +78 -0
- package/dist/{neon-BFUS25UX.cjs → neon-KP2CPA57.cjs} +110 -10
- package/dist/{neon-CU5N3CSW.mjs → neon-ZIMHALYI.mjs} +106 -6
- package/dist/{pg-QO6NKTGL.mjs → pg-QGC2XHUT.mjs} +94 -5
- package/dist/{pg-IIH3M4OM.cjs → pg-RZPQGXR3.cjs} +98 -9
- package/dist/single-user.d.ts +95 -0
- package/dist/tenant/blob-store.d.ts +30 -0
- package/dist/tenant/context.d.ts +47 -0
- package/dist/tenant/single-user-context.d.ts +29 -0
- package/dist/tenant/token.d.ts +26 -0
- package/package.json +3 -3
- package/dist/chunk-5ENJGVZX.mjs +0 -72
- package/dist/chunk-GN5QTMCL.cjs +0 -1870
- package/dist/chunk-PVNLB6TF.mjs +0 -1870
- package/dist/chunk-RGECD4OH.cjs +0 -72
- package/dist/cloudflare-B-E51VjP.d.cts +0 -2848
- package/dist/cloudflare-B-E51VjP.d.ts +0 -2848
- package/dist/cloudflare.d.cts +0 -2
- package/dist/neon-CJl66EGy.d.cts +0 -251
- package/dist/neon-DYvGnCzx.d.ts +0 -251
- package/dist/pg-07S-u_H4.d.cts +0 -246
- package/dist/pg-Du-pN_UT.d.ts +0 -246
- package/dist/schema-C8OnYk6j.d.cts +0 -74
- package/dist/schema-C8OnYk6j.d.ts +0 -74
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
claimTask,
|
|
3
|
+
deleteLlmCredentials,
|
|
4
|
+
deletePushSubscription,
|
|
5
|
+
getLlmCredentials,
|
|
6
|
+
getPushSubscription,
|
|
7
|
+
getTaskStatusInfo,
|
|
8
|
+
listLlmCredentials,
|
|
9
|
+
renewTaskLease,
|
|
10
|
+
upsertLlmCredentials,
|
|
11
|
+
upsertPushSubscription
|
|
12
|
+
} from "./chunk-ZGGF4GMA.mjs";
|
|
1
13
|
import {
|
|
2
14
|
COLUMNS_SQL,
|
|
3
15
|
INDEXES,
|
|
16
|
+
LLM_CREDENTIALS_TABLE_SQL,
|
|
17
|
+
MIGRATIONS,
|
|
18
|
+
PUSH_SUBSCRIPTION_TABLE_SQL,
|
|
4
19
|
TABLE_SQL,
|
|
20
|
+
TASK_DELIVERY_COLUMNS,
|
|
21
|
+
TASK_DETAIL_COLUMNS,
|
|
22
|
+
UPDATABLE_COLUMNS,
|
|
5
23
|
VERIFY_TABLE_SQL
|
|
6
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-GN44PST5.mjs";
|
|
7
25
|
|
|
8
26
|
// src/server/adapters/pg.js
|
|
9
27
|
import { Pool } from "pg";
|
|
@@ -17,6 +35,12 @@ var PgAdapter = class {
|
|
|
17
35
|
_getPool() {
|
|
18
36
|
if (!this._pool) {
|
|
19
37
|
this._pool = new Pool({ connectionString: this._connectionString });
|
|
38
|
+
this._pool.on("error", (error) => {
|
|
39
|
+
console.error(
|
|
40
|
+
"[amsg-server pg] \u8FDE\u63A5\u6C60\u91CC\u7684\u7A7A\u95F2\u8FDE\u63A5\u51FA\u9519\uFF08\u8BE5\u8FDE\u63A5\u5DF2\u88AB\u6458\u9664\uFF0C\u4E0B\u6B21\u67E5\u8BE2\u4F1A\u91CD\u8FDE\uFF09:",
|
|
41
|
+
error && error.message
|
|
42
|
+
);
|
|
43
|
+
});
|
|
20
44
|
}
|
|
21
45
|
return this._pool;
|
|
22
46
|
}
|
|
@@ -28,6 +52,11 @@ var PgAdapter = class {
|
|
|
28
52
|
}
|
|
29
53
|
async initSchema() {
|
|
30
54
|
await this._query(TABLE_SQL);
|
|
55
|
+
await this._query(PUSH_SUBSCRIPTION_TABLE_SQL);
|
|
56
|
+
await this._query(LLM_CREDENTIALS_TABLE_SQL);
|
|
57
|
+
for (const migration of MIGRATIONS) {
|
|
58
|
+
await this._query(migration.sql);
|
|
59
|
+
}
|
|
31
60
|
const indexResults = [];
|
|
32
61
|
for (const index of INDEXES) {
|
|
33
62
|
try {
|
|
@@ -72,6 +101,8 @@ var PgAdapter = class {
|
|
|
72
101
|
}
|
|
73
102
|
async dropSchema() {
|
|
74
103
|
await this._query("DROP TABLE IF EXISTS scheduled_messages CASCADE");
|
|
104
|
+
await this._query("DROP TABLE IF EXISTS push_subscriptions CASCADE");
|
|
105
|
+
await this._query("DROP TABLE IF EXISTS llm_credentials CASCADE");
|
|
75
106
|
}
|
|
76
107
|
async createTask(params) {
|
|
77
108
|
const rows = await this._query(
|
|
@@ -85,7 +116,7 @@ var PgAdapter = class {
|
|
|
85
116
|
}
|
|
86
117
|
async getTaskByUuid(uuid, userId) {
|
|
87
118
|
const rows = await this._query(
|
|
88
|
-
`SELECT
|
|
119
|
+
`SELECT ${TASK_DETAIL_COLUMNS}
|
|
89
120
|
FROM scheduled_messages
|
|
90
121
|
WHERE uuid = $1 AND user_id = $2 AND status = 'pending'
|
|
91
122
|
LIMIT 1`,
|
|
@@ -95,7 +126,7 @@ var PgAdapter = class {
|
|
|
95
126
|
}
|
|
96
127
|
async getTaskByUuidOnly(uuid) {
|
|
97
128
|
const rows = await this._query(
|
|
98
|
-
`SELECT
|
|
129
|
+
`SELECT ${TASK_DELIVERY_COLUMNS}
|
|
99
130
|
FROM scheduled_messages
|
|
100
131
|
WHERE uuid = $1 AND status = 'pending'
|
|
101
132
|
LIMIT 1`,
|
|
@@ -103,11 +134,28 @@ var PgAdapter = class {
|
|
|
103
134
|
);
|
|
104
135
|
return rows[0] || null;
|
|
105
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* 这条 uuid 现在是什么状态——不限用户,也不限状态(上面那个只看 pending
|
|
139
|
+
* 行)。`runTask` 用它把「这条已经跑完了」和「压根没这条」分开回报。
|
|
140
|
+
*
|
|
141
|
+
* @param {string} uuid
|
|
142
|
+
* @returns {Promise<{ status: string }|null>}
|
|
143
|
+
*/
|
|
144
|
+
async getTaskStatusByUuidOnly(uuid) {
|
|
145
|
+
const rows = await this._query(
|
|
146
|
+
`SELECT status FROM scheduled_messages WHERE uuid = $1 LIMIT 1`,
|
|
147
|
+
[uuid]
|
|
148
|
+
);
|
|
149
|
+
return rows[0] ? { status: rows[0].status } : null;
|
|
150
|
+
}
|
|
106
151
|
async updateTaskById(taskId, updates) {
|
|
107
152
|
const sets = [];
|
|
108
153
|
const values = [];
|
|
109
154
|
let idx = 1;
|
|
110
155
|
for (const [key, value] of Object.entries(updates)) {
|
|
156
|
+
if (!UPDATABLE_COLUMNS.has(key)) {
|
|
157
|
+
throw new Error(`[amsg-server pg] rejected unknown update column: ${key}`);
|
|
158
|
+
}
|
|
111
159
|
sets.push(`${key} = $${idx}`);
|
|
112
160
|
values.push(value);
|
|
113
161
|
idx++;
|
|
@@ -126,6 +174,9 @@ var PgAdapter = class {
|
|
|
126
174
|
let idx = 2;
|
|
127
175
|
if (extraFields) {
|
|
128
176
|
for (const [key, value] of Object.entries(extraFields)) {
|
|
177
|
+
if (!UPDATABLE_COLUMNS.has(key)) {
|
|
178
|
+
throw new Error(`[amsg-server pg] rejected unknown update column: ${key}`);
|
|
179
|
+
}
|
|
129
180
|
sets.push(`${key} = $${idx}`);
|
|
130
181
|
values.push(value);
|
|
131
182
|
idx++;
|
|
@@ -153,14 +204,25 @@ var PgAdapter = class {
|
|
|
153
204
|
}
|
|
154
205
|
async getPendingTasks(limit = 50) {
|
|
155
206
|
return this._query(
|
|
156
|
-
`SELECT
|
|
207
|
+
`SELECT ${TASK_DELIVERY_COLUMNS}
|
|
157
208
|
FROM scheduled_messages
|
|
158
209
|
WHERE status = 'pending' AND next_send_at <= NOW()
|
|
210
|
+
AND (lease_until IS NULL OR lease_until <= NOW())
|
|
211
|
+
AND (retry_after IS NULL OR retry_after <= NOW())
|
|
159
212
|
ORDER BY next_send_at ASC
|
|
160
213
|
LIMIT $1`,
|
|
161
214
|
[limit]
|
|
162
215
|
);
|
|
163
216
|
}
|
|
217
|
+
// 领取一条到点的任务。SQL 与并发语义在 pg-shared.js(pg / neon 共用一份,
|
|
218
|
+
// 语义说明也在那里)。
|
|
219
|
+
async claimTask(taskId, expectedNextSendAt, leaseUntil, serializeGroup = null) {
|
|
220
|
+
return claimTask((text, params) => this._query(text, params), taskId, expectedNextSendAt, leaseUntil, serializeGroup);
|
|
221
|
+
}
|
|
222
|
+
// 投递期间的租约续期(run-tick 心跳;语义见 pg-shared.js)。
|
|
223
|
+
async renewTaskLease(taskId, leaseUntil) {
|
|
224
|
+
return renewTaskLease((text, params) => this._query(text, params), taskId, leaseUntil);
|
|
225
|
+
}
|
|
164
226
|
async listTasks(userId, opts = {}) {
|
|
165
227
|
const { status = "all", limit = 20, offset = 0 } = opts;
|
|
166
228
|
const conditions = ["user_id = $1"];
|
|
@@ -179,7 +241,7 @@ var PgAdapter = class {
|
|
|
179
241
|
const total = parseInt(countRows[0].count, 10);
|
|
180
242
|
const taskParams = [...params, limit, offset];
|
|
181
243
|
const tasks = await this._query(
|
|
182
|
-
`SELECT
|
|
244
|
+
`SELECT ${TASK_DETAIL_COLUMNS}
|
|
183
245
|
FROM scheduled_messages
|
|
184
246
|
WHERE ${where}
|
|
185
247
|
ORDER BY next_send_at ASC
|
|
@@ -206,6 +268,33 @@ var PgAdapter = class {
|
|
|
206
268
|
);
|
|
207
269
|
return rows.length > 0 ? rows[0].status : null;
|
|
208
270
|
}
|
|
271
|
+
// 状态 + 失败摘要(GET /message 对已失败的行透出原因;语义见 pg-shared.js)。
|
|
272
|
+
async getTaskStatusInfo(uuid, userId) {
|
|
273
|
+
return getTaskStatusInfo((text, params) => this._query(text, params), uuid, userId);
|
|
274
|
+
}
|
|
275
|
+
// ── push_subscriptions(实现见 pg-shared.js,pg / neon 共用一份)─────────
|
|
276
|
+
async getPushSubscription(userId) {
|
|
277
|
+
return getPushSubscription((text, params) => this._query(text, params), userId);
|
|
278
|
+
}
|
|
279
|
+
async upsertPushSubscription(userId, encryptedSubscription, updatedAt) {
|
|
280
|
+
return upsertPushSubscription((text, params) => this._query(text, params), userId, encryptedSubscription, updatedAt);
|
|
281
|
+
}
|
|
282
|
+
async deletePushSubscription(userId) {
|
|
283
|
+
return deletePushSubscription((text, params) => this._query(text, params), userId);
|
|
284
|
+
}
|
|
285
|
+
// ── llm_credentials(实现见 pg-shared.js,pg / neon 共用一份)───────────
|
|
286
|
+
async upsertLlmCredentials(userId, entries) {
|
|
287
|
+
return upsertLlmCredentials((text, params) => this._query(text, params), userId, entries, (/* @__PURE__ */ new Date()).toISOString());
|
|
288
|
+
}
|
|
289
|
+
async getLlmCredentials(userId, credIds) {
|
|
290
|
+
return getLlmCredentials((text, params) => this._query(text, params), userId, credIds);
|
|
291
|
+
}
|
|
292
|
+
async listLlmCredentials(userId) {
|
|
293
|
+
return listLlmCredentials((text, params) => this._query(text, params), userId);
|
|
294
|
+
}
|
|
295
|
+
async deleteLlmCredentials(userId, credIds = null) {
|
|
296
|
+
return deleteLlmCredentials((text, params) => this._query(text, params), userId, credIds);
|
|
297
|
+
}
|
|
209
298
|
};
|
|
210
299
|
export {
|
|
211
300
|
PgAdapter
|
|
@@ -3,7 +3,25 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
var _chunkE7OWP3VLcjs = require('./chunk-E7OWP3VL.cjs');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
var _chunk7NMQFTDJcjs = require('./chunk-7NMQFTDJ.cjs');
|
|
7
25
|
|
|
8
26
|
// src/server/adapters/pg.js
|
|
9
27
|
var _pg = require('pg');
|
|
@@ -17,6 +35,12 @@ var PgAdapter = class {
|
|
|
17
35
|
_getPool() {
|
|
18
36
|
if (!this._pool) {
|
|
19
37
|
this._pool = new (0, _pg.Pool)({ connectionString: this._connectionString });
|
|
38
|
+
this._pool.on("error", (error) => {
|
|
39
|
+
console.error(
|
|
40
|
+
"[amsg-server pg] \u8FDE\u63A5\u6C60\u91CC\u7684\u7A7A\u95F2\u8FDE\u63A5\u51FA\u9519\uFF08\u8BE5\u8FDE\u63A5\u5DF2\u88AB\u6458\u9664\uFF0C\u4E0B\u6B21\u67E5\u8BE2\u4F1A\u91CD\u8FDE\uFF09:",
|
|
41
|
+
error && error.message
|
|
42
|
+
);
|
|
43
|
+
});
|
|
20
44
|
}
|
|
21
45
|
return this._pool;
|
|
22
46
|
}
|
|
@@ -27,9 +51,14 @@ var PgAdapter = class {
|
|
|
27
51
|
return result.rows;
|
|
28
52
|
}
|
|
29
53
|
async initSchema() {
|
|
30
|
-
await this._query(
|
|
54
|
+
await this._query(_chunk7NMQFTDJcjs.TABLE_SQL);
|
|
55
|
+
await this._query(_chunk7NMQFTDJcjs.PUSH_SUBSCRIPTION_TABLE_SQL);
|
|
56
|
+
await this._query(_chunk7NMQFTDJcjs.LLM_CREDENTIALS_TABLE_SQL);
|
|
57
|
+
for (const migration of _chunk7NMQFTDJcjs.MIGRATIONS) {
|
|
58
|
+
await this._query(migration.sql);
|
|
59
|
+
}
|
|
31
60
|
const indexResults = [];
|
|
32
|
-
for (const index of
|
|
61
|
+
for (const index of _chunk7NMQFTDJcjs.INDEXES) {
|
|
33
62
|
try {
|
|
34
63
|
await this._query(index.sql);
|
|
35
64
|
indexResults.push({
|
|
@@ -55,11 +84,11 @@ var PgAdapter = class {
|
|
|
55
84
|
`Critical index creation failed (${failedNames}). Please remove duplicate UUID rows and run initSchema again.`
|
|
56
85
|
);
|
|
57
86
|
}
|
|
58
|
-
const tableCheck = await this._query(
|
|
87
|
+
const tableCheck = await this._query(_chunk7NMQFTDJcjs.VERIFY_TABLE_SQL);
|
|
59
88
|
if (tableCheck.length === 0) {
|
|
60
89
|
throw new Error("Table creation verification failed");
|
|
61
90
|
}
|
|
62
|
-
const columns = await this._query(
|
|
91
|
+
const columns = await this._query(_chunk7NMQFTDJcjs.COLUMNS_SQL);
|
|
63
92
|
return {
|
|
64
93
|
columnsCreated: columns.length,
|
|
65
94
|
indexesCreated: indexResults.filter((r) => r.status === "success").length,
|
|
@@ -72,6 +101,8 @@ var PgAdapter = class {
|
|
|
72
101
|
}
|
|
73
102
|
async dropSchema() {
|
|
74
103
|
await this._query("DROP TABLE IF EXISTS scheduled_messages CASCADE");
|
|
104
|
+
await this._query("DROP TABLE IF EXISTS push_subscriptions CASCADE");
|
|
105
|
+
await this._query("DROP TABLE IF EXISTS llm_credentials CASCADE");
|
|
75
106
|
}
|
|
76
107
|
async createTask(params) {
|
|
77
108
|
const rows = await this._query(
|
|
@@ -85,7 +116,7 @@ var PgAdapter = class {
|
|
|
85
116
|
}
|
|
86
117
|
async getTaskByUuid(uuid, userId) {
|
|
87
118
|
const rows = await this._query(
|
|
88
|
-
`SELECT
|
|
119
|
+
`SELECT ${_chunk7NMQFTDJcjs.TASK_DETAIL_COLUMNS}
|
|
89
120
|
FROM scheduled_messages
|
|
90
121
|
WHERE uuid = $1 AND user_id = $2 AND status = 'pending'
|
|
91
122
|
LIMIT 1`,
|
|
@@ -95,7 +126,7 @@ var PgAdapter = class {
|
|
|
95
126
|
}
|
|
96
127
|
async getTaskByUuidOnly(uuid) {
|
|
97
128
|
const rows = await this._query(
|
|
98
|
-
`SELECT
|
|
129
|
+
`SELECT ${_chunk7NMQFTDJcjs.TASK_DELIVERY_COLUMNS}
|
|
99
130
|
FROM scheduled_messages
|
|
100
131
|
WHERE uuid = $1 AND status = 'pending'
|
|
101
132
|
LIMIT 1`,
|
|
@@ -103,11 +134,28 @@ var PgAdapter = class {
|
|
|
103
134
|
);
|
|
104
135
|
return rows[0] || null;
|
|
105
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* 这条 uuid 现在是什么状态——不限用户,也不限状态(上面那个只看 pending
|
|
139
|
+
* 行)。`runTask` 用它把「这条已经跑完了」和「压根没这条」分开回报。
|
|
140
|
+
*
|
|
141
|
+
* @param {string} uuid
|
|
142
|
+
* @returns {Promise<{ status: string }|null>}
|
|
143
|
+
*/
|
|
144
|
+
async getTaskStatusByUuidOnly(uuid) {
|
|
145
|
+
const rows = await this._query(
|
|
146
|
+
`SELECT status FROM scheduled_messages WHERE uuid = $1 LIMIT 1`,
|
|
147
|
+
[uuid]
|
|
148
|
+
);
|
|
149
|
+
return rows[0] ? { status: rows[0].status } : null;
|
|
150
|
+
}
|
|
106
151
|
async updateTaskById(taskId, updates) {
|
|
107
152
|
const sets = [];
|
|
108
153
|
const values = [];
|
|
109
154
|
let idx = 1;
|
|
110
155
|
for (const [key, value] of Object.entries(updates)) {
|
|
156
|
+
if (!_chunk7NMQFTDJcjs.UPDATABLE_COLUMNS.has(key)) {
|
|
157
|
+
throw new Error(`[amsg-server pg] rejected unknown update column: ${key}`);
|
|
158
|
+
}
|
|
111
159
|
sets.push(`${key} = $${idx}`);
|
|
112
160
|
values.push(value);
|
|
113
161
|
idx++;
|
|
@@ -126,6 +174,9 @@ var PgAdapter = class {
|
|
|
126
174
|
let idx = 2;
|
|
127
175
|
if (extraFields) {
|
|
128
176
|
for (const [key, value] of Object.entries(extraFields)) {
|
|
177
|
+
if (!_chunk7NMQFTDJcjs.UPDATABLE_COLUMNS.has(key)) {
|
|
178
|
+
throw new Error(`[amsg-server pg] rejected unknown update column: ${key}`);
|
|
179
|
+
}
|
|
129
180
|
sets.push(`${key} = $${idx}`);
|
|
130
181
|
values.push(value);
|
|
131
182
|
idx++;
|
|
@@ -153,14 +204,25 @@ var PgAdapter = class {
|
|
|
153
204
|
}
|
|
154
205
|
async getPendingTasks(limit = 50) {
|
|
155
206
|
return this._query(
|
|
156
|
-
`SELECT
|
|
207
|
+
`SELECT ${_chunk7NMQFTDJcjs.TASK_DELIVERY_COLUMNS}
|
|
157
208
|
FROM scheduled_messages
|
|
158
209
|
WHERE status = 'pending' AND next_send_at <= NOW()
|
|
210
|
+
AND (lease_until IS NULL OR lease_until <= NOW())
|
|
211
|
+
AND (retry_after IS NULL OR retry_after <= NOW())
|
|
159
212
|
ORDER BY next_send_at ASC
|
|
160
213
|
LIMIT $1`,
|
|
161
214
|
[limit]
|
|
162
215
|
);
|
|
163
216
|
}
|
|
217
|
+
// 领取一条到点的任务。SQL 与并发语义在 pg-shared.js(pg / neon 共用一份,
|
|
218
|
+
// 语义说明也在那里)。
|
|
219
|
+
async claimTask(taskId, expectedNextSendAt, leaseUntil, serializeGroup = null) {
|
|
220
|
+
return _chunkE7OWP3VLcjs.claimTask.call(void 0, (text, params) => this._query(text, params), taskId, expectedNextSendAt, leaseUntil, serializeGroup);
|
|
221
|
+
}
|
|
222
|
+
// 投递期间的租约续期(run-tick 心跳;语义见 pg-shared.js)。
|
|
223
|
+
async renewTaskLease(taskId, leaseUntil) {
|
|
224
|
+
return _chunkE7OWP3VLcjs.renewTaskLease.call(void 0, (text, params) => this._query(text, params), taskId, leaseUntil);
|
|
225
|
+
}
|
|
164
226
|
async listTasks(userId, opts = {}) {
|
|
165
227
|
const { status = "all", limit = 20, offset = 0 } = opts;
|
|
166
228
|
const conditions = ["user_id = $1"];
|
|
@@ -179,7 +241,7 @@ var PgAdapter = class {
|
|
|
179
241
|
const total = parseInt(countRows[0].count, 10);
|
|
180
242
|
const taskParams = [...params, limit, offset];
|
|
181
243
|
const tasks = await this._query(
|
|
182
|
-
`SELECT
|
|
244
|
+
`SELECT ${_chunk7NMQFTDJcjs.TASK_DETAIL_COLUMNS}
|
|
183
245
|
FROM scheduled_messages
|
|
184
246
|
WHERE ${where}
|
|
185
247
|
ORDER BY next_send_at ASC
|
|
@@ -206,6 +268,33 @@ var PgAdapter = class {
|
|
|
206
268
|
);
|
|
207
269
|
return rows.length > 0 ? rows[0].status : null;
|
|
208
270
|
}
|
|
271
|
+
// 状态 + 失败摘要(GET /message 对已失败的行透出原因;语义见 pg-shared.js)。
|
|
272
|
+
async getTaskStatusInfo(uuid, userId) {
|
|
273
|
+
return _chunkE7OWP3VLcjs.getTaskStatusInfo.call(void 0, (text, params) => this._query(text, params), uuid, userId);
|
|
274
|
+
}
|
|
275
|
+
// ── push_subscriptions(实现见 pg-shared.js,pg / neon 共用一份)─────────
|
|
276
|
+
async getPushSubscription(userId) {
|
|
277
|
+
return _chunkE7OWP3VLcjs.getPushSubscription.call(void 0, (text, params) => this._query(text, params), userId);
|
|
278
|
+
}
|
|
279
|
+
async upsertPushSubscription(userId, encryptedSubscription, updatedAt) {
|
|
280
|
+
return _chunkE7OWP3VLcjs.upsertPushSubscription.call(void 0, (text, params) => this._query(text, params), userId, encryptedSubscription, updatedAt);
|
|
281
|
+
}
|
|
282
|
+
async deletePushSubscription(userId) {
|
|
283
|
+
return _chunkE7OWP3VLcjs.deletePushSubscription.call(void 0, (text, params) => this._query(text, params), userId);
|
|
284
|
+
}
|
|
285
|
+
// ── llm_credentials(实现见 pg-shared.js,pg / neon 共用一份)───────────
|
|
286
|
+
async upsertLlmCredentials(userId, entries) {
|
|
287
|
+
return _chunkE7OWP3VLcjs.upsertLlmCredentials.call(void 0, (text, params) => this._query(text, params), userId, entries, (/* @__PURE__ */ new Date()).toISOString());
|
|
288
|
+
}
|
|
289
|
+
async getLlmCredentials(userId, credIds) {
|
|
290
|
+
return _chunkE7OWP3VLcjs.getLlmCredentials.call(void 0, (text, params) => this._query(text, params), userId, credIds);
|
|
291
|
+
}
|
|
292
|
+
async listLlmCredentials(userId) {
|
|
293
|
+
return _chunkE7OWP3VLcjs.listLlmCredentials.call(void 0, (text, params) => this._query(text, params), userId);
|
|
294
|
+
}
|
|
295
|
+
async deleteLlmCredentials(userId, credIds = null) {
|
|
296
|
+
return _chunkE7OWP3VLcjs.deleteLlmCredentials.call(void 0, (text, params) => this._query(text, params), userId, credIds);
|
|
297
|
+
}
|
|
209
298
|
};
|
|
210
299
|
|
|
211
300
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export function createSingleUserServer(config: any): {
|
|
2
|
+
ctx: {
|
|
3
|
+
vapid: {
|
|
4
|
+
email: any;
|
|
5
|
+
publicKey: any;
|
|
6
|
+
privateKey: any;
|
|
7
|
+
};
|
|
8
|
+
webpush: any;
|
|
9
|
+
multipart: any;
|
|
10
|
+
tenantManager: {
|
|
11
|
+
resolveTenant: (headers: any) => Promise<{
|
|
12
|
+
ok: boolean;
|
|
13
|
+
error: {
|
|
14
|
+
status: number;
|
|
15
|
+
body: {
|
|
16
|
+
success: boolean;
|
|
17
|
+
error: {
|
|
18
|
+
code: string;
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
context?: undefined;
|
|
24
|
+
} | {
|
|
25
|
+
ok: boolean;
|
|
26
|
+
context: {
|
|
27
|
+
tenantId: string;
|
|
28
|
+
tokenType: string;
|
|
29
|
+
db: any;
|
|
30
|
+
masterKey: any;
|
|
31
|
+
};
|
|
32
|
+
error?: undefined;
|
|
33
|
+
}>;
|
|
34
|
+
initializeTenant: () => Promise<{
|
|
35
|
+
tenantId: string;
|
|
36
|
+
schema: any;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
hooks: any;
|
|
40
|
+
maxToolIterations: any;
|
|
41
|
+
totalTimeoutMs: any;
|
|
42
|
+
maxStateValueBytes: any;
|
|
43
|
+
onAfterSend: any;
|
|
44
|
+
onFireSettled: any;
|
|
45
|
+
maxScheduledTasksPerFire: any;
|
|
46
|
+
};
|
|
47
|
+
handlers: {
|
|
48
|
+
init: {
|
|
49
|
+
POST: Function;
|
|
50
|
+
};
|
|
51
|
+
getUserKey: {
|
|
52
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
53
|
+
};
|
|
54
|
+
scheduleMessage: {
|
|
55
|
+
POST: (headers: any, body: any) => Promise<any>;
|
|
56
|
+
};
|
|
57
|
+
updateMessage: {
|
|
58
|
+
PUT: (url: any, headers: any, body: any) => Promise<any>;
|
|
59
|
+
};
|
|
60
|
+
cancelMessage: {
|
|
61
|
+
DELETE: (url: any, headers: any) => Promise<any>;
|
|
62
|
+
};
|
|
63
|
+
messages: {
|
|
64
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
65
|
+
};
|
|
66
|
+
getMessage: {
|
|
67
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
68
|
+
};
|
|
69
|
+
vapidPublicKey: {
|
|
70
|
+
GET: Function;
|
|
71
|
+
};
|
|
72
|
+
clientState: {
|
|
73
|
+
PUT: (headers: any, body: any) => Promise<any>;
|
|
74
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
75
|
+
DELETE: (url: any, headers: any) => Promise<any>;
|
|
76
|
+
};
|
|
77
|
+
pushSubscription: {
|
|
78
|
+
PUT: (headers: any, body: any) => Promise<any>;
|
|
79
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
80
|
+
DELETE: (url: any, headers: any) => Promise<any>;
|
|
81
|
+
};
|
|
82
|
+
llmCredentials: {
|
|
83
|
+
PUT: (headers: any, body: any) => Promise<any>;
|
|
84
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
85
|
+
DELETE: (url: any, headers: any, body: any) => Promise<any>;
|
|
86
|
+
};
|
|
87
|
+
capabilities: {
|
|
88
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
89
|
+
};
|
|
90
|
+
outbox: {
|
|
91
|
+
GET: (url: any, headers: any) => Promise<any>;
|
|
92
|
+
POST: (headers: any, body: any) => Promise<any>;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {{
|
|
3
|
+
* tenantId: string,
|
|
4
|
+
* db: { driver: 'neon'|'pg', connectionString: string },
|
|
5
|
+
* masterKey: string,
|
|
6
|
+
* createdAt: string,
|
|
7
|
+
* updatedAt: string
|
|
8
|
+
* }} TenantConfig
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @param {{ namespace?: string, kek: string }} options
|
|
12
|
+
*/
|
|
13
|
+
export function createTenantBlobStore(options: {
|
|
14
|
+
namespace?: string;
|
|
15
|
+
kek: string;
|
|
16
|
+
}): {
|
|
17
|
+
getTenantConfig(tenantId: any): Promise<any>;
|
|
18
|
+
setTenantConfig(config: any): Promise<void>;
|
|
19
|
+
deleteTenantConfig(tenantId: any): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export type TenantConfig = {
|
|
22
|
+
tenantId: string;
|
|
23
|
+
db: {
|
|
24
|
+
driver: "neon" | "pg";
|
|
25
|
+
connectionString: string;
|
|
26
|
+
};
|
|
27
|
+
masterKey: string;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
updatedAt: string;
|
|
30
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {{
|
|
3
|
+
* tenantStore: ReturnType<import('./blob-store.js').createTenantBlobStore>,
|
|
4
|
+
* tokenSigningKey: string,
|
|
5
|
+
* publicBaseUrl?: string
|
|
6
|
+
* }} options
|
|
7
|
+
*/
|
|
8
|
+
export function createTenantContextManager(options: {
|
|
9
|
+
tenantStore: ReturnType<typeof import("./blob-store.js").createTenantBlobStore>;
|
|
10
|
+
tokenSigningKey: string;
|
|
11
|
+
publicBaseUrl?: string;
|
|
12
|
+
}): {
|
|
13
|
+
initializeTenant: ({ driver, connectionString, tenantId: providedTenantId }: {
|
|
14
|
+
driver: any;
|
|
15
|
+
connectionString: any;
|
|
16
|
+
tenantId: any;
|
|
17
|
+
}) => Promise<{
|
|
18
|
+
tenantId: string;
|
|
19
|
+
masterKeyFingerprint: string;
|
|
20
|
+
tenantToken: string;
|
|
21
|
+
cronToken: string;
|
|
22
|
+
cronWebhookUrl: string;
|
|
23
|
+
}>;
|
|
24
|
+
resolveTenant: (headers: any, options?: {}) => Promise<{
|
|
25
|
+
ok: boolean;
|
|
26
|
+
error: {
|
|
27
|
+
status: number;
|
|
28
|
+
body: {
|
|
29
|
+
success: boolean;
|
|
30
|
+
error: {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
context?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
ok: boolean;
|
|
39
|
+
context: {
|
|
40
|
+
tenantId: any;
|
|
41
|
+
tokenType: any;
|
|
42
|
+
db: any;
|
|
43
|
+
masterKey: any;
|
|
44
|
+
};
|
|
45
|
+
error?: undefined;
|
|
46
|
+
}>;
|
|
47
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function createSingleUserContextManager({ db, masterKey, serverToken }?: {}): {
|
|
2
|
+
resolveTenant: (headers: any) => Promise<{
|
|
3
|
+
ok: boolean;
|
|
4
|
+
error: {
|
|
5
|
+
status: number;
|
|
6
|
+
body: {
|
|
7
|
+
success: boolean;
|
|
8
|
+
error: {
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
context?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
context: {
|
|
18
|
+
tenantId: string;
|
|
19
|
+
tokenType: string;
|
|
20
|
+
db: any;
|
|
21
|
+
masterKey: any;
|
|
22
|
+
};
|
|
23
|
+
error?: undefined;
|
|
24
|
+
}>;
|
|
25
|
+
initializeTenant: () => Promise<{
|
|
26
|
+
tenantId: string;
|
|
27
|
+
schema: any;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {'tenant'|'cron'} TokenType
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @param {{ tenantId: string, type: TokenType, expiresInSeconds?: number }} params
|
|
6
|
+
* @param {string} secret
|
|
7
|
+
*/
|
|
8
|
+
export function createTenantToken(params: {
|
|
9
|
+
tenantId: string;
|
|
10
|
+
type: TokenType;
|
|
11
|
+
expiresInSeconds?: number;
|
|
12
|
+
}, secret: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} token
|
|
15
|
+
* @param {string} secret
|
|
16
|
+
* @param {{ expectedTypes?: TokenType[] }} [options]
|
|
17
|
+
*/
|
|
18
|
+
export function verifyTenantToken(token: string, secret: string, options?: {
|
|
19
|
+
expectedTypes?: TokenType[];
|
|
20
|
+
}): any;
|
|
21
|
+
/**
|
|
22
|
+
* @param {Record<string, any>} headers
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
export function extractBearerToken(headers?: Record<string, any>): string;
|
|
26
|
+
export type TokenType = "tenant" | "cron";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rei-standard/amsg-server",
|
|
3
|
-
"version": "2.6.0-next.
|
|
3
|
+
"version": "2.6.0-next.21",
|
|
4
4
|
"description": "ReiStandard Active Messaging server SDK with pluggable database adapters. Three-axis push schema (messageKind / messageType / messageSubtype) from @rei-standard/amsg-shared. Auto-emits ReasoningPush when the LLM response carries reasoning_content.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dist"
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build": "tsup",
|
|
34
|
+
"build": "tsup && tsc -p tsconfig.json && node -e \"require('fs').copyFileSync('dist/index.d.ts', 'dist/index.d.cts')\"",
|
|
35
35
|
"test": "node --test test/*.test.mjs"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@netlify/blobs": "^8.1.0",
|
|
42
|
-
"@rei-standard/amsg-shared": "^0.
|
|
42
|
+
"@rei-standard/amsg-shared": "^0.4.0-next.6",
|
|
43
43
|
"web-push": "^3.6.7"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|