@rei-standard/amsg-server 2.6.0-next.9 → 3.0.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/LICENSE +21 -0
- package/README.md +822 -41
- package/dist/adapters/d1.d.ts +693 -0
- package/dist/adapters/factory.d.ts +39 -0
- package/dist/adapters/interface.d.ts +384 -0
- package/dist/adapters/neon.d.ts +105 -0
- package/dist/adapters/pg-shared.d.ts +194 -0
- package/dist/adapters/pg.d.ts +101 -0
- package/dist/adapters/schema.d.ts +57 -0
- package/dist/adapters/schema.sqlite.d.ts +80 -0
- package/dist/chunk-7NMQFTDJ.cjs +148 -0
- package/dist/{chunk-GOBCWCJP.mjs → chunk-GN44PST5.mjs} +68 -1
- package/dist/chunk-RL7V7VSS.mjs +8784 -0
- package/dist/chunk-TKDISKCF.cjs +163 -0
- package/dist/chunk-XKUWMYVF.mjs +163 -0
- package/dist/chunk-YBDQJVFQ.cjs +8784 -0
- package/dist/cloudflare/single-user-worker.d.ts +39 -0
- package/dist/cloudflare.cjs +35 -2
- package/dist/cloudflare.d.ts +11 -2
- package/dist/cloudflare.mjs +36 -3
- package/dist/handlers/cancel-message.d.ts +3 -0
- package/dist/handlers/capabilities.d.ts +4 -0
- package/dist/handlers/client-state-namespaces.d.ts +5 -0
- package/dist/handlers/client-state.d.ts +7 -0
- package/dist/handlers/cloud-data.d.ts +4 -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 +9 -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 +145 -766
- package/dist/index.d.ts +145 -766
- package/dist/index.mjs +125 -36
- package/dist/lib/agentic-fire.d.ts +177 -0
- package/dist/lib/client-state-store.d.ts +88 -0
- package/dist/lib/cloud-data-cleanup.d.ts +36 -0
- package/dist/lib/cloud-data-guard.d.ts +14 -0
- package/dist/lib/cloud-data-inventory.d.ts +15 -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 +273 -0
- package/dist/lib/llm-credentials-store.d.ts +147 -0
- package/dist/lib/llm.d.ts +12 -0
- package/dist/lib/message-processor.d.ts +82 -0
- package/dist/lib/outbox-store.d.ts +162 -0
- package/dist/lib/push-policy.d.ts +11 -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 +60 -0
- package/dist/lib/retry-policy.d.ts +11 -0
- package/dist/lib/run-tick.d.ts +121 -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 +65 -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-QJ7KYHEX.mjs → neon-GKLT4UY3.mjs} +116 -44
- package/dist/{neon-V4VOH55N.cjs → neon-XHPJZESV.cjs} +121 -49
- package/dist/{pg-6X74HKMX.cjs → pg-FHXQ6BJP.cjs} +109 -48
- package/dist/{pg-DFJSFOJQ.mjs → pg-J4YXRQP5.mjs} +104 -43
- package/dist/single-user.d.ts +106 -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-3QRVP4JE.cjs +0 -2834
- package/dist/chunk-DCKCAGFV.cjs +0 -81
- package/dist/chunk-S6O3MXQO.mjs +0 -2834
- package/dist/cloudflare-kUY6z-k5.d.cts +0 -4460
- package/dist/cloudflare-kUY6z-k5.d.ts +0 -4460
- package/dist/cloudflare.d.cts +0 -2
- package/dist/neon-D8Z2Nn97.d.cts +0 -295
- package/dist/neon-PqNFJIz2.d.ts +0 -295
- package/dist/pg-C48FwCWW.d.cts +0 -289
- package/dist/pg-C5gtWUfU.d.ts +0 -289
- package/dist/schema-BHuyH_6Z.d.cts +0 -88
- package/dist/schema-BHuyH_6Z.d.ts +0 -88
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a user-specific encryption key from the master key.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} userId - Unique user identifier.
|
|
5
|
+
* @param {string} masterKey - 64-char hex master key.
|
|
6
|
+
* @returns {Promise<string>} 64-char hex key.
|
|
7
|
+
*/
|
|
8
|
+
export function deriveUserEncryptionKey(userId: string, masterKey: string): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Decrypt a client-encrypted request body (AES-256-GCM, base64 encoded).
|
|
11
|
+
*
|
|
12
|
+
* @param {{ iv: string, authTag: string, encryptedData: string }} encryptedPayload
|
|
13
|
+
* @param {string} encryptionKey - 64-char hex key.
|
|
14
|
+
* @returns {Promise<Object>} Decrypted JSON object.
|
|
15
|
+
*/
|
|
16
|
+
export function decryptPayload(encryptedPayload: {
|
|
17
|
+
iv: string;
|
|
18
|
+
authTag: string;
|
|
19
|
+
encryptedData: string;
|
|
20
|
+
}, encryptionKey: string): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Encrypt a JSON payload for API transfer (AES-256-GCM, base64 encoded).
|
|
23
|
+
*
|
|
24
|
+
* @param {string|Object} payload
|
|
25
|
+
* @param {string} encryptionKey - 64-char hex key.
|
|
26
|
+
* @returns {Promise<{ iv: string, authTag: string, encryptedData: string }>}
|
|
27
|
+
*/
|
|
28
|
+
export function encryptPayload(payload: string | any, encryptionKey: string): Promise<{
|
|
29
|
+
iv: string;
|
|
30
|
+
authTag: string;
|
|
31
|
+
encryptedData: string;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Encrypt data for database storage (hex encoded, colon-separated).
|
|
35
|
+
*
|
|
36
|
+
* @param {string} text - Plaintext string.
|
|
37
|
+
* @param {string} encryptionKey - 64-char hex key.
|
|
38
|
+
* @returns {Promise<string>} Format: iv:authTag:encryptedData
|
|
39
|
+
*/
|
|
40
|
+
export function encryptForStorage(text: string, encryptionKey: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Decrypt data from database storage format.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} encryptedText - Format: iv:authTag:encryptedData
|
|
45
|
+
* @param {string} encryptionKey - 64-char hex key.
|
|
46
|
+
* @returns {Promise<string>} Plaintext string.
|
|
47
|
+
*/
|
|
48
|
+
export function decryptFromStorage(encryptedText: string, encryptionKey: string): Promise<string>;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 这个错误是不是「重试也好不了」。认 NonRetryableError,也认任何自带
|
|
3
|
+
* `permanent: true` 的错误对象(跨包/跨版本安全)。
|
|
4
|
+
*
|
|
5
|
+
* @param {unknown} error
|
|
6
|
+
* @returns {boolean}
|
|
7
|
+
*/
|
|
8
|
+
export function isNonRetryableError(error: unknown): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* 把一个已经构造好的错误标成确定性失败(就地改,原样返回),顺带补一个稳定
|
|
11
|
+
* 的 `code`(错误自己带了 code 就不覆盖)。
|
|
12
|
+
*
|
|
13
|
+
* 用在错误由别处构造、又想保住它原本类型的时候——比如契约校验抛的
|
|
14
|
+
* `TypeError`:宿主按 `instanceof TypeError` 分流的代码不该因为库多标了一个
|
|
15
|
+
* 字段就走岔。自己现造错误的地方直接用 {@link NonRetryableError} 更直白。
|
|
16
|
+
*
|
|
17
|
+
* @template T
|
|
18
|
+
* @param {T} error
|
|
19
|
+
* @param {string} [code]
|
|
20
|
+
* @returns {T}
|
|
21
|
+
*/
|
|
22
|
+
export function markPermanent<T>(error: T, code?: string): T;
|
|
23
|
+
/**
|
|
24
|
+
* 这个错误是不是「投递期间任务被取消/顶替」。
|
|
25
|
+
*
|
|
26
|
+
* @param {unknown} error
|
|
27
|
+
* @returns {boolean}
|
|
28
|
+
*/
|
|
29
|
+
export function isTaskCancelledError(error: unknown): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 推送半途失败、而这一批**没有**落进 outbox 时,这次失败还能不能重试(就地标
|
|
32
|
+
* 注,原样返回传入的错误)。
|
|
33
|
+
*
|
|
34
|
+
* 落进了 outbox 的批次重试时只补推送、不重新生成(见 lib/message-processor.js
|
|
35
|
+
* 的 redeliverCommittedBatch)。没落进去的(适配器没有 outbox,或者这一批落行
|
|
36
|
+
* 失败)没有这条退路:重试只能把整条内容重新生成一遍。那样做安不安全,看设备上
|
|
37
|
+
* 已经有没有这次触发的东西:
|
|
38
|
+
*
|
|
39
|
+
* - 一条都没推出去 → 客户端什么都没收到,重新生成的那一份就是它唯一见到的一
|
|
40
|
+
* 份。照旧重试(会多花一次生成——没有收件箱兜着的时候,这是唯一能把消息送
|
|
41
|
+
* 到的办法);
|
|
42
|
+
* - 已经推出去了几条 → 重新生成的内容跟设备上那几条不是同一份,客户端会看到
|
|
43
|
+
* 前半截来自第一次生成、后半截来自第二次。所以就地判终审(标 permanent):
|
|
44
|
+
* 一次性任务标 failed、循环任务作废本次 occurrence,没推出去的那几段就此
|
|
45
|
+
* 放弃。
|
|
46
|
+
*
|
|
47
|
+
* 取消(TASK_CANCELLED)不归这里管,照旧按取消收尾。
|
|
48
|
+
*
|
|
49
|
+
* @template T
|
|
50
|
+
* @param {T} error - 推送那一步抛的错误
|
|
51
|
+
* @param {{ outboxed: boolean, pushedCount: number }} state - 这一批落没落进
|
|
52
|
+
* outbox、已经推出去了几条
|
|
53
|
+
* @returns {T}
|
|
54
|
+
*/
|
|
55
|
+
export function markUnrecoverablePartialDelivery<T>(error: T, { outboxed, pushedCount }: {
|
|
56
|
+
outboxed: boolean;
|
|
57
|
+
pushedCount: number;
|
|
58
|
+
}): T;
|
|
59
|
+
/**
|
|
60
|
+
* 这个错误是不是「LLM 上游明确拒了这次请求、重试也不会好」。判据见
|
|
61
|
+
* {@link PERMANENT_LLM_STATUSES}。
|
|
62
|
+
*
|
|
63
|
+
* @param {unknown} error
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
export function isPermanentLlmFailure(error: unknown): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* 按 {@link isPermanentLlmFailure} 的口径就地给 LLM 错误标上 `permanent: true`
|
|
69
|
+
* (原样返回传入的错误)。
|
|
70
|
+
*
|
|
71
|
+
* 标在错误对象上而不是只在投递侧判:fire-time hook(`onFireSettled` /
|
|
72
|
+
* `onAfterSend`)拿到的就是这个对象,宿主用 `error.permanent === true` 判断
|
|
73
|
+
* 「这是终态、该告诉用户了」。只在投递侧判的话,hook 看到的是一个没有
|
|
74
|
+
* `permanent` 的错误,任务却已经被判死了。
|
|
75
|
+
*
|
|
76
|
+
* 唯一的调用点是 lib/llm.js 的 callLlm(server 侧所有 LLM 调用都走它),别在
|
|
77
|
+
* 别处各自判。
|
|
78
|
+
*
|
|
79
|
+
* @template T
|
|
80
|
+
* @param {T} error
|
|
81
|
+
* @returns {T}
|
|
82
|
+
*/
|
|
83
|
+
export function markPermanentIfLlmRejected<T>(error: T): T;
|
|
84
|
+
/**
|
|
85
|
+
* 这次投递失败要不要判成永久性的(不再重试)。五个来源:
|
|
86
|
+
* - 已知的永久性错误码(见 {@link PERMANENT_ERROR_CODES});
|
|
87
|
+
* - hook 侧抛出的 NonRetryableError(`permanent`,见 {@link NonRetryableError})——
|
|
88
|
+
* fire_pack 缺失、解析失败这类重试必然同败的错,隔两分钟再试三次只是让用户
|
|
89
|
+
* 多白等十二分钟,还把情绪评估之类的计费重跑三遍;
|
|
90
|
+
* - LLM 上游明确拒了这次请求(401 / 403 / 400 …,见
|
|
91
|
+
* {@link PERMANENT_LLM_STATUSES})。这一条同样经由 `permanent` 传进来:错
|
|
92
|
+
* 误在 lib/llm.js 抛出的那一刻就标好了(见 {@link markPermanentIfLlmRejected}),
|
|
93
|
+
* hook 看到的和这里判的是同一个结论;
|
|
94
|
+
* - 推送服务判了这条订阅的死刑(见 {@link TERMINAL_PUSH_STATUSES});
|
|
95
|
+
* - 推送服务说这条 payload 太大(见 {@link PUSH_PAYLOAD_TOO_LARGE_STATUS})。
|
|
96
|
+
*
|
|
97
|
+
* @param {{ permanent?: unknown, errorCode?: unknown, pushStatus?: unknown }} failure
|
|
98
|
+
* @returns {boolean}
|
|
99
|
+
*/
|
|
100
|
+
export function isPermanentDeliveryFailure(failure: {
|
|
101
|
+
permanent?: unknown;
|
|
102
|
+
errorCode?: unknown;
|
|
103
|
+
pushStatus?: unknown;
|
|
104
|
+
}): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* 发一条 push,失败时自动标上「这是发 push 那一步抛的」。
|
|
107
|
+
*
|
|
108
|
+
* 发 push 的地方一律走这里,别各自写 `try { send } catch { throw
|
|
109
|
+
* tagPushStatusCode(e) }`:漏标一处,那条路上的 410 就读不出状态码,一条早就
|
|
110
|
+
* 失效的订阅会被当成普通失败,在退避阶梯上把 LLM 重跑三轮。忘了标是没有任何
|
|
111
|
+
* 提示的。
|
|
112
|
+
*
|
|
113
|
+
* @param {{ sendNotification: (subscription: any, payload: string) => Promise<any> }} webpush
|
|
114
|
+
* @param {any} subscription
|
|
115
|
+
* @param {string} payloadJson
|
|
116
|
+
* @returns {Promise<any>}
|
|
117
|
+
*/
|
|
118
|
+
export function sendTaggedPush(webpush: {
|
|
119
|
+
sendNotification: (subscription: any, payload: string) => Promise<any>;
|
|
120
|
+
}, subscription: any, payloadJson: string): Promise<any>;
|
|
121
|
+
/**
|
|
122
|
+
* 读推送服务回的状态码。没被 {@link tagPushStatusCode} 标过 → null。
|
|
123
|
+
*
|
|
124
|
+
* @param {unknown} error
|
|
125
|
+
* @returns {number|null}
|
|
126
|
+
*/
|
|
127
|
+
export function readPushStatusCode(error: unknown): number | null;
|
|
128
|
+
/**
|
|
129
|
+
* lastError 里的机读标注:`errorCode` 是底层错误的稳定 code(如
|
|
130
|
+
* `PUSH_PAYLOAD_TOO_LARGE`),`pushStatus` 是推送服务回的 HTTP 状态码。两个都
|
|
131
|
+
* 没有 → undefined,不往记录里塞空字段。
|
|
132
|
+
*
|
|
133
|
+
* reason 那句是给用户看的人话、措辞随时会变,判断「该让用户重建订阅还是裁短
|
|
134
|
+
* 内容」得读这两个字段。定时任务(run-tick)与 instant 任务
|
|
135
|
+
* (processMessagesByUuid)共用这一份,两条路记下来的形状一致。
|
|
136
|
+
*
|
|
137
|
+
* code 是标识符不是人话,不用脱敏,但也别让一个来路不明的超长 code 撑大明文
|
|
138
|
+
* 列——截到 64 字符,够放所有约定过的码。
|
|
139
|
+
*
|
|
140
|
+
* @param {string|null|undefined} errorCode
|
|
141
|
+
* @param {number|null|undefined} pushStatus
|
|
142
|
+
* @returns {{ errorCode?: string, pushStatus?: number }|undefined}
|
|
143
|
+
*/
|
|
144
|
+
export function buildErrorExtra(errorCode: string | null | undefined, pushStatus: number | null | undefined): {
|
|
145
|
+
errorCode?: string;
|
|
146
|
+
pushStatus?: number;
|
|
147
|
+
} | undefined;
|
|
148
|
+
/**
|
|
149
|
+
* 把错误原因脱敏成能明文落库(任务行 last_error 列)的摘要。
|
|
150
|
+
*
|
|
151
|
+
* 任务内容一律密文落库,last_error 是唯一一列明文的「为什么没发出去」——
|
|
152
|
+
* 错误消息偶尔会回显请求细节(上游 API 的报错带 URL、header 片段),所以
|
|
153
|
+
* 长得像凭据的 token 一律遮掉,再截断。
|
|
154
|
+
*
|
|
155
|
+
* 遮什么、怎么遮由 @rei-standard/amsg-shared 的 `redactCredentials` 说了算,
|
|
156
|
+
* 这里只负责压平空白和截断。
|
|
157
|
+
*
|
|
158
|
+
* @param {unknown} reason
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
export function sanitizeErrorSummary(reason: unknown): string;
|
|
162
|
+
/**
|
|
163
|
+
* @typedef {Object} ErrorCause
|
|
164
|
+
* @property {'config'|'request'|'tick'} stage - 在哪一段炸的
|
|
165
|
+
* @property {string} name - 错误类型(`error.name`,认不出来时是 'Error')
|
|
166
|
+
* @property {string} [message] - 脱敏后的错误消息。降级 500(配置都没建起来那条
|
|
167
|
+
* 路)回给跨域调用方时不带这个字段——那条路的 CORS 头是回显来访 Origin 的,任
|
|
168
|
+
* 意第三方页面都能读,而构建期异常原文里常有 binding 名、内网域名、环境变量
|
|
169
|
+
* 名。同源 / 无 Origin 的请求照旧带全文,`wrangler tail` 里也一直有。
|
|
170
|
+
* @property {string} [code] - 错误自带的 `code` 字符串(有才带)
|
|
171
|
+
*/
|
|
172
|
+
/**
|
|
173
|
+
* 把一个异常整理成能随响应体一起回给调用方的机读原因。
|
|
174
|
+
*
|
|
175
|
+
* 500 只回一句「服务器内部错误」的话,真因(`D1_ERROR: no such table:
|
|
176
|
+
* message_outbox`、存储层写超时……)就只剩 console.error 里那一行,调用方拿不
|
|
177
|
+
* 到,用户看到的也只是「服务器内部错误」——不知道哪儿坏了,也不知道该点哪里。
|
|
178
|
+
* 这个函数把异常压成几个固定字段,让调用方能机读、能展示。
|
|
179
|
+
*
|
|
180
|
+
* 只带错误类型和消息文本。消息先过 `sanitizeErrorSummary`(遮掉长得像凭据的
|
|
181
|
+
* 串、截断到 500 字符);密钥、用户数据、任务正文都不在 `error.message` 上,
|
|
182
|
+
* 也不往这里放。
|
|
183
|
+
*
|
|
184
|
+
* @param {unknown} error - 捕获到的异常,也收 `{ name, message }` 这样的普通对象
|
|
185
|
+
* @param {'config'|'request'|'tick'} stage - 'config' = 构建配置时抛的(少了
|
|
186
|
+
* binding / 环境变量);'request' = 路由或处理器抛的;'tick' = cron 那一跳抛的
|
|
187
|
+
* @returns {ErrorCause}
|
|
188
|
+
*/
|
|
189
|
+
export function summarizeErrorCause(error: unknown, stage: "config" | "request" | "tick"): ErrorCause;
|
|
190
|
+
/**
|
|
191
|
+
* 确定性失败——重试必然同败,别再排退避阶梯。
|
|
192
|
+
*
|
|
193
|
+
* 典型场景全在 fire-time hook 里:onBeforeFire 发现 fire_pack 缺失、解析失
|
|
194
|
+
* 败、缺必要的段……这类错误不会因为过两分钟再跑一次就好,按普通投递失败重
|
|
195
|
+
* 试三轮只是让用户多白等十几分钟,还把 hook 里的计费调用(情绪评估之类)
|
|
196
|
+
* 重复烧三遍。hook 抛这个类(或任何带 `permanent: true` 的错误),run-tick
|
|
197
|
+
* 收到后跳过退避:一次性任务直接标 failed(原因进 last_error / payload 的
|
|
198
|
+
* lastError),循环任务直接作废本次 occurrence。
|
|
199
|
+
*
|
|
200
|
+
* `code`(可选)会透传到 processSingleMessage 的 errorCode,宿主想按错误类
|
|
201
|
+
* 别分流时用它。
|
|
202
|
+
*
|
|
203
|
+
* 用 `permanent` 属性而不是 instanceof 做判定:宿主的 worker 里可能打包了另
|
|
204
|
+
* 一份本库(版本错开、bundler 双实例),跨 realm 的 instanceof 靠不住。
|
|
205
|
+
*/
|
|
206
|
+
export class NonRetryableError extends Error {
|
|
207
|
+
/**
|
|
208
|
+
* @param {string} message
|
|
209
|
+
* @param {{ code?: string, cause?: unknown }} [options]
|
|
210
|
+
*/
|
|
211
|
+
constructor(message: string, options?: {
|
|
212
|
+
code?: string;
|
|
213
|
+
cause?: unknown;
|
|
214
|
+
});
|
|
215
|
+
permanent: boolean;
|
|
216
|
+
code: string;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 部署级的配置 / 适配器能力错误——坏的不是这条任务,是这个部署。
|
|
220
|
+
*
|
|
221
|
+
* 典型场景:`hooks.onBeforeFire` 配了但 `onLLMOutput` 忘了配、`executeToolCalls`
|
|
222
|
+
* 没配、自定义适配器缺 `createTask` / `deleteTaskByUuid` / `upsertClientState`。
|
|
223
|
+
* 这类错误重试确实好不了,但**不能**判终态:同一个坏部署下每条到点的任务都会
|
|
224
|
+
* 撞同一个错,判终态就等于在那段时间里把每一条一次性任务都永久标 `failed`,
|
|
225
|
+
* 运维改好配置重新部署也捞不回来了(行已不在 pending,`PUT /update-message`
|
|
226
|
+
* 回 409)。所以留在退避阶梯上——配置一修好,还在阶梯上的任务下一跳就正常发
|
|
227
|
+
* 出去。这跟 VAPID 配错回的 400/401/403 是同一个道理,见
|
|
228
|
+
* {@link PUSH_PAYLOAD_TOO_LARGE_STATUS} 那段注释。
|
|
229
|
+
*
|
|
230
|
+
* 带 `code` 但不带 `permanent`:失败原因照旧进 last_error 的 `errorCode`,宿主
|
|
231
|
+
* 想分流照样读得到,只是不再跳过重试。
|
|
232
|
+
*/
|
|
233
|
+
export class DeploymentConfigError extends Error {
|
|
234
|
+
/**
|
|
235
|
+
* @param {string} message
|
|
236
|
+
* @param {{ code?: string, cause?: unknown }} [options]
|
|
237
|
+
*/
|
|
238
|
+
constructor(message: string, options?: {
|
|
239
|
+
code?: string;
|
|
240
|
+
cause?: unknown;
|
|
241
|
+
});
|
|
242
|
+
code: string;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* 投递期间发现「这条任务已经不归本次投递管了」时,推送侧抛的错误码:行被
|
|
246
|
+
* `DELETE /message` 取消、被 `supersedesUuid` 顶替,或被别的执行者收尾了。
|
|
247
|
+
*
|
|
248
|
+
* 发送方(message-processor 的正文投递、agentic-fire 的 hook 投递)靠它区分
|
|
249
|
+
* 「取消」和「发失败」——两者的收尾完全不同:取消要把这一批还没发出去的
|
|
250
|
+
* outbox 行撤掉,发失败则要把它们留着等补收。
|
|
251
|
+
*/
|
|
252
|
+
export const TASK_CANCELLED_CODE: "TASK_CANCELLED";
|
|
253
|
+
export type ErrorCause = {
|
|
254
|
+
/**
|
|
255
|
+
* - 在哪一段炸的
|
|
256
|
+
*/
|
|
257
|
+
stage: "config" | "request" | "tick";
|
|
258
|
+
/**
|
|
259
|
+
* - 错误类型(`error.name`,认不出来时是 'Error')
|
|
260
|
+
*/
|
|
261
|
+
name: string;
|
|
262
|
+
/**
|
|
263
|
+
* - 脱敏后的错误消息。降级 500(配置都没建起来那条
|
|
264
|
+
* 路)回给跨域调用方时不带这个字段——那条路的 CORS 头是回显来访 Origin 的,任
|
|
265
|
+
* 意第三方页面都能读,而构建期异常原文里常有 binding 名、内网域名、环境变量
|
|
266
|
+
* 名。同源 / 无 Origin 的请求照旧带全文,`wrangler tail` 里也一直有。
|
|
267
|
+
*/
|
|
268
|
+
message?: string;
|
|
269
|
+
/**
|
|
270
|
+
* - 错误自带的 `code` 字符串(有才带)
|
|
271
|
+
*/
|
|
272
|
+
code?: string;
|
|
273
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/** 适配器支不支持用户级凭据存储。 */
|
|
2
|
+
export function supportsLlmCredentialsStore(db: any): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* 一个字符串是不是合法 cred_id:非空、≤128 字符、不含控制字符。
|
|
5
|
+
*
|
|
6
|
+
* @param {unknown} credId
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
export function isValidCredId(credId: unknown): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* 一份凭据 value 的形状:{ apiUrl, apiKey, primaryModel } 三字段全必填。
|
|
12
|
+
* 校验口径对齐 update-message 的凭据刷新:只查 truthy(+ 长度上限),不做
|
|
13
|
+
* 格式校验。
|
|
14
|
+
*
|
|
15
|
+
* @param {unknown} value
|
|
16
|
+
* @returns {string|null} 错误描述,合法时 null
|
|
17
|
+
*/
|
|
18
|
+
export function validateCredValue(value: unknown): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* 任务 payload 里 credRefs 字段的形状校验(schedule-message 与 update-message
|
|
21
|
+
* 共用一份口径)。
|
|
22
|
+
*
|
|
23
|
+
* @param {unknown} credRefs
|
|
24
|
+
* @returns {string|null} 错误描述,合法时 null
|
|
25
|
+
*/
|
|
26
|
+
export function validateCredRefs(credRefs: unknown): string | null;
|
|
27
|
+
/**
|
|
28
|
+
* payload 带没带可用的 chat 凭据引用(`credRefs.chat`)。fire 侧的
|
|
29
|
+
* taskNeedsLlm 和校验共用这一个判据。
|
|
30
|
+
*
|
|
31
|
+
* @param {Object|null|undefined} payload
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
*/
|
|
34
|
+
export function hasChatCredRef(payload: any | null | undefined): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* payload 带没带非空的 credRefs 映射(不管里面是什么 purpose)。自排继承要在
|
|
37
|
+
* 「有 chat 引用」和「只有非 chat 引用」之间分支,这是后者用的那半个判据。
|
|
38
|
+
*
|
|
39
|
+
* @param {Object|null|undefined} payload
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
export function hasCredRefs(payload: any | null | undefined): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* 批量写入(覆盖)这个用户的凭据。value 在这里加密。
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} args
|
|
47
|
+
* @param {import('../adapters/interface.js').DbAdapter} args.db
|
|
48
|
+
* @param {string} args.userId
|
|
49
|
+
* @param {string} args.userKey
|
|
50
|
+
* @param {Array<{ credId: string, value: Object }>} args.credentials
|
|
51
|
+
* @returns {Promise<{ upserted: number }>}
|
|
52
|
+
*/
|
|
53
|
+
export function saveLlmCredentials({ db, userId, userKey, credentials }: {
|
|
54
|
+
db: import("../adapters/interface.js").DbAdapter;
|
|
55
|
+
userId: string;
|
|
56
|
+
userKey: string;
|
|
57
|
+
credentials: Array<{
|
|
58
|
+
credId: string;
|
|
59
|
+
value: any;
|
|
60
|
+
}>;
|
|
61
|
+
}): Promise<{
|
|
62
|
+
upserted: number;
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* 排程 / 更新时的存在性检查:credRefs 里引用的 credId 有哪些还不在表里。
|
|
66
|
+
* 空缺不是这里的错误——回给调用方点名,由它组 4xx。
|
|
67
|
+
*
|
|
68
|
+
* @param {Object} args
|
|
69
|
+
* @param {import('../adapters/interface.js').DbAdapter} args.db
|
|
70
|
+
* @param {string} args.userId
|
|
71
|
+
* @param {Object} args.credRefs
|
|
72
|
+
* @returns {Promise<string[]>} 缺失的 credId(去重)
|
|
73
|
+
*/
|
|
74
|
+
export function findMissingCredIds({ db, userId, credRefs }: {
|
|
75
|
+
db: import("../adapters/interface.js").DbAdapter;
|
|
76
|
+
userId: string;
|
|
77
|
+
credRefs: any;
|
|
78
|
+
}): Promise<string[]>;
|
|
79
|
+
/**
|
|
80
|
+
* 按 cred_id 解出一份明文凭据。每次调用返回**新对象**;没有这行 / 解不开
|
|
81
|
+
* (换过 masterKey)→ null。
|
|
82
|
+
*
|
|
83
|
+
* 结果只该被「拿到就用」:合进发给 LLM 的请求对象、或在 hook 里当场发请求。
|
|
84
|
+
* 不要写回 payload / ctx / metadata——那些对象会流向 hook 和 push,凭据跟着
|
|
85
|
+
* 走就把 CREDENTIAL_PAYLOAD_KEYS 那道防线绕空了。
|
|
86
|
+
*
|
|
87
|
+
* @param {Object} args
|
|
88
|
+
* @param {import('../adapters/interface.js').DbAdapter} args.db
|
|
89
|
+
* @param {string} args.userId
|
|
90
|
+
* @param {string} args.userKey
|
|
91
|
+
* @param {string} args.credId
|
|
92
|
+
* @returns {Promise<{ apiUrl: string, apiKey: string, primaryModel: string }|null>}
|
|
93
|
+
*/
|
|
94
|
+
export function resolveLlmCredential({ db, userId, userKey, credId }: {
|
|
95
|
+
db: import("../adapters/interface.js").DbAdapter;
|
|
96
|
+
userId: string;
|
|
97
|
+
userKey: string;
|
|
98
|
+
credId: string;
|
|
99
|
+
}): Promise<{
|
|
100
|
+
apiUrl: string;
|
|
101
|
+
apiKey: string;
|
|
102
|
+
primaryModel: string;
|
|
103
|
+
} | null>;
|
|
104
|
+
/**
|
|
105
|
+
* fire 时的 chat 凭据解析(message-processor 的两条老路径和 agentic 循环共用)。
|
|
106
|
+
*
|
|
107
|
+
* 解析顺序:
|
|
108
|
+
* 1. payload 没带 `credRefs.chat` → 返回 null,调用方按内联三件套的老行为走
|
|
109
|
+
* (存量任务的零开销路径,不多打一次库)。
|
|
110
|
+
* 2. 带了 → 查表取值,查到就用表里那份(表是用户最近一次登记的,比冻结在
|
|
111
|
+
* 行里的新)。
|
|
112
|
+
* 3. 查不到行(被删 / 换过 masterKey / 适配器不支持)→ 退回内联三件套兜底
|
|
113
|
+
* (update-message 给存量任务补 credRefs 时内联还留着,正是给这里用的)。
|
|
114
|
+
* 4. 都没有 → 抛 `code: 'CREDENTIAL_MISSING'`,走任务的常规失败/重试——用户
|
|
115
|
+
* 补传凭据后下一轮自愈。
|
|
116
|
+
*
|
|
117
|
+
* 返回的对象只许合进**发给 LLM 的请求对象**,见 resolveLlmCredential 的红线。
|
|
118
|
+
*
|
|
119
|
+
* @param {Object} args
|
|
120
|
+
* @param {import('../adapters/interface.js').DbAdapter} args.db
|
|
121
|
+
* @param {string} args.userId
|
|
122
|
+
* @param {string} args.userKey
|
|
123
|
+
* @param {Object} args.decryptedPayload
|
|
124
|
+
* @returns {Promise<{ apiUrl: string, apiKey: string, primaryModel: string }|null>}
|
|
125
|
+
*/
|
|
126
|
+
export function resolveFireCredentials({ db, userId, userKey, decryptedPayload }: {
|
|
127
|
+
db: import("../adapters/interface.js").DbAdapter;
|
|
128
|
+
userId: string;
|
|
129
|
+
userKey: string;
|
|
130
|
+
decryptedPayload: any;
|
|
131
|
+
}): Promise<{
|
|
132
|
+
apiUrl: string;
|
|
133
|
+
apiKey: string;
|
|
134
|
+
primaryModel: string;
|
|
135
|
+
} | null>;
|
|
136
|
+
/** cred_id 的长度上限。 */
|
|
137
|
+
export const CRED_ID_MAX_LENGTH: 128;
|
|
138
|
+
/** value 单字段(apiUrl / apiKey / primaryModel)的长度上限。 */
|
|
139
|
+
export const CRED_VALUE_FIELD_MAX_LENGTH: 2048;
|
|
140
|
+
/** PUT 单批最多几条。 */
|
|
141
|
+
export const CRED_PUT_BATCH_MAX: 100;
|
|
142
|
+
/** 单用户最多存几行凭据。 */
|
|
143
|
+
export const CRED_ROWS_PER_USER_MAX: 500;
|
|
144
|
+
/** credRefs 最多几个 purpose 条目。 */
|
|
145
|
+
export const CRED_REFS_MAX_ENTRIES: 16;
|
|
146
|
+
/** credRefs 的 purpose 键长度上限。 */
|
|
147
|
+
export const CRED_REFS_KEY_MAX_LENGTH: 64;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 与 shared 的 callLlm 同签名、同返回值;失败时抛出的错误若是「上游拒了、重试
|
|
3
|
+
* 也不会好」的那一类,会多带 `permanent: true`。
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} payload
|
|
6
|
+
* @param {Object} [options]
|
|
7
|
+
* @returns {Promise<{ response: unknown, content: string }>}
|
|
8
|
+
*/
|
|
9
|
+
export function callLlm(payload: any, options?: any): Promise<{
|
|
10
|
+
response: unknown;
|
|
11
|
+
content: string;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process a single database task row: decrypt → generate content → push.
|
|
3
|
+
*
|
|
4
|
+
* 每次投递都先看这次触发的整批是不是已经落进了 outbox:落进去了就只补推送、
|
|
5
|
+
* 不再生成(见 redeliverCommittedBatch),返回值带 `redelivered: true`。
|
|
6
|
+
*
|
|
7
|
+
* @param {import('../adapters/interface.js').TaskRow} task
|
|
8
|
+
* @param {ProcessorContext} ctx
|
|
9
|
+
* @param {string} [providedMasterKey]
|
|
10
|
+
* @param {{ userKey: string, payload: Object } | null} [predecrypted] - 调用方
|
|
11
|
+
* (run-tick 的预扫描)已经解好的 payload;传了就不再解第二遍。
|
|
12
|
+
* @returns {Promise<{ success: boolean, messagesSent: number, redelivered?: boolean, pushedCount?: number, deferred?: boolean, retryAfter?: string, error?: string, errorCode?: string|null, pushStatusCode?: number|null, permanent?: boolean, willRetry?: boolean, retryLimit?: number, failureStage?: string }>}
|
|
13
|
+
* 失败时 `pushStatusCode` 是推送服务回的 HTTP 状态码(不是推送阶段炸的 → null)。
|
|
14
|
+
* `deferred: true` 表示 onBeforeFire 把这次触发推迟了(`retryAfter` 是唤醒
|
|
15
|
+
* 时刻):`success` 为 false 但不是失败,调用方要先看这个字段。只有 ctx 带
|
|
16
|
+
* `_deferSupported: true`(run-tick 在适配器有 retry_after 列时给)才会出现。
|
|
17
|
+
*/
|
|
18
|
+
export function processSingleMessage(task: import("../adapters/interface.js").TaskRow, ctx: ProcessorContext, providedMasterKey?: string, predecrypted?: {
|
|
19
|
+
userKey: string;
|
|
20
|
+
payload: any;
|
|
21
|
+
} | null): Promise<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
messagesSent: number;
|
|
24
|
+
redelivered?: boolean;
|
|
25
|
+
pushedCount?: number;
|
|
26
|
+
deferred?: boolean;
|
|
27
|
+
retryAfter?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
errorCode?: string | null;
|
|
30
|
+
pushStatusCode?: number | null;
|
|
31
|
+
permanent?: boolean;
|
|
32
|
+
willRetry?: boolean;
|
|
33
|
+
retryLimit?: number;
|
|
34
|
+
failureStage?: string;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Process a single message identified by UUID (used for instant type).
|
|
38
|
+
*
|
|
39
|
+
* @param {string} uuid
|
|
40
|
+
* @param {ProcessorContext} ctx
|
|
41
|
+
* @param {number} [maxRetries=2]
|
|
42
|
+
* @param {string} [userId]
|
|
43
|
+
* @param {string} [providedMasterKey]
|
|
44
|
+
* @returns {Promise<{ success: boolean, messagesSent?: number, retriesUsed?: number, reasoningError?: string, error?: Object }>}
|
|
45
|
+
* `reasoningError` 只在正文都发出去了、思考过程那一条没发成时出现(思考过程是
|
|
46
|
+
* 附赠内容,它发不出去不算这条消息失败)。调用方拿它提示用户这次没有思考过程,
|
|
47
|
+
* 不带这个字段就是整轮都送到了。
|
|
48
|
+
*
|
|
49
|
+
* 这条路是请求里当场投递,不支持 onBeforeFire 的 `{ defer }`:返回它会得到
|
|
50
|
+
* AGENTIC_DEFER_UNSUPPORTED 的配置错误,按这里的失败流程处理。
|
|
51
|
+
*/
|
|
52
|
+
export function processMessagesByUuid(uuid: string, ctx: ProcessorContext, maxRetries?: number, userId?: string, providedMasterKey?: string): Promise<{
|
|
53
|
+
success: boolean;
|
|
54
|
+
messagesSent?: number;
|
|
55
|
+
retriesUsed?: number;
|
|
56
|
+
reasoningError?: string;
|
|
57
|
+
error?: any;
|
|
58
|
+
}>;
|
|
59
|
+
export { normalizeAiApiUrl } from "./llm.js";
|
|
60
|
+
export type ProcessorContext = {
|
|
61
|
+
maxDeliveryRetries?: number;
|
|
62
|
+
maxGenerationRetries?: number | ((task: any) => number | undefined);
|
|
63
|
+
/**
|
|
64
|
+
* - The web-push module instance (already VAPID-configured).
|
|
65
|
+
*/
|
|
66
|
+
webpush: any;
|
|
67
|
+
/**
|
|
68
|
+
* - { email, publicKey, privateKey }
|
|
69
|
+
*/
|
|
70
|
+
vapid: any;
|
|
71
|
+
db: import("../adapters/interface.js").DbAdapter;
|
|
72
|
+
/**
|
|
73
|
+
* 分片传输的限额,宿主传给 `installReiSW` 的那一份原样传过来即可(见
|
|
74
|
+
* resolveMultipartOptions)。不传 = 两边都用默认值。
|
|
75
|
+
*/
|
|
76
|
+
multipart?: {
|
|
77
|
+
maxChunkBytes?: number;
|
|
78
|
+
maxChunks?: number;
|
|
79
|
+
maxTotalBytes?: number;
|
|
80
|
+
ttlMs?: number;
|
|
81
|
+
};
|
|
82
|
+
};
|