@h-ai/serv 0.1.0-alpha.66 → 0.1.0-alpha.68
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.en.md +75 -37
- package/README.ja.md +76 -38
- package/README.md +77 -39
- package/dist/chunk-BGWE4S6Z.js +37 -0
- package/dist/chunk-BGWE4S6Z.js.map +1 -0
- package/dist/{chunk-R354OMWL.js → chunk-TWHCU6HT.js} +48 -6
- package/dist/chunk-TWHCU6HT.js.map +1 -0
- package/dist/{chunk-27PCPE37.js → chunk-XKCLD42I.js} +2 -2
- package/dist/chunk-XKCLD42I.js.map +1 -0
- package/dist/features/ai.d.ts +139 -57
- package/dist/features/ai.js +57 -46
- package/dist/features/ai.js.map +1 -1
- package/dist/features/iam.d.ts +29 -4
- package/dist/features/iam.js +3 -3
- package/dist/features/iam.js.map +1 -1
- package/dist/features/storage.d.ts +94 -9
- package/dist/features/storage.js +48 -12
- package/dist/features/storage.js.map +1 -1
- package/dist/index.d.ts +587 -187
- package/dist/index.js +89 -111
- package/dist/index.js.map +1 -1
- package/dist/{serv-context-CWzyrsdq.d.ts → serv-context-C48WdBcd.d.ts} +127 -7
- package/dist/serv-router-BX8hgYxo.d.ts +322 -0
- package/package.json +14 -10
- package/dist/chunk-27PCPE37.js.map +0 -1
- package/dist/chunk-R354OMWL.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/features/serv-feature-helpers.ts"],"names":[],"mappings":";;;AAwBO,SAAS,gBAAmB,MAAA,EAAmD;AACpF,EAAA,IAAI,CAAC,MAAA,CAAO,OAAA;AACV,IAAA,OAAO,MAAA;AACT,EAAA,OAAO,EAAA,CAAG,EAAE,KAAA,EAAO,MAAA,CAAO,MAAM,CAAA;AAClC;AAaO,SAAS,YAAA,CAAmB,QAAsB,MAAA,EAAsC;AAC7F,EAAA,IAAI,CAAC,MAAA,CAAO,OAAA;AACV,IAAA,OAAO,MAAA;AACT,EAAA,OAAO,EAAA,CAAG,MAAA,CAAO,MAAA,CAAO,IAAI,CAAC,CAAA;AAC/B","file":"chunk-XKCLD42I.js","sourcesContent":["/**\n * @h-ai/serv — features 公共辅助\n *\n * 把多个 feature procedure 中重复出现的 `HaiResult` 包装逻辑收敛到一处。\n * @module features/serv-feature-helpers\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport { ok } from '@h-ai/core'\n\n/**\n * 把 `HaiResult<T[]>` 转换为 `HaiResult<{ items: T[] }>`。\n *\n * oRPC contract 中 list 类返回常用 `{ items: [...] }` 信封;\n * 业务模块通常直接返回数组,本工具负责套上信封。\n *\n * @example\n * ```ts\n * // 模块内部上下文:示例复用当前声明所在模块的实际绑定。\n * const mapped = wrapItemsResult(ok([{ id: 'report-1' }]))\n * if (!mapped.success) return mapped\n * return mapped.data.items\n * ```\n */\nexport function wrapItemsResult<T>(result: HaiResult<T[]>): HaiResult<{ items: T[] }> {\n if (!result.success)\n return result\n return ok({ items: result.data })\n}\n\n/**\n * 把 `HaiResult<T>` 通过 mapper 映射为 `HaiResult<U>`,错误透传。\n *\n * @example\n * ```ts\n * // 模块内部上下文:示例复用当前声明所在模块的实际绑定。\n * const mapped = mapHaiResult(ok({ id: 'report-1', hidden: true }), value => ({ id: value.id }))\n * if (!mapped.success) return mapped\n * return mapped.data\n * ```\n */\nexport function mapHaiResult<T, U>(result: HaiResult<T>, mapper: (data: T) => U): HaiResult<U> {\n if (!result.success)\n return result\n return ok(mapper(result.data))\n}\n"]}
|
package/dist/features/ai.d.ts
CHANGED
|
@@ -2,29 +2,89 @@ import * as _orpc_server from '@orpc/server';
|
|
|
2
2
|
import * as zod_v4_core from 'zod/v4/core';
|
|
3
3
|
import * as zod from 'zod';
|
|
4
4
|
import { AIFunctions } from '@h-ai/ai';
|
|
5
|
-
import { S as ServContext } from '../serv-context-
|
|
5
|
+
import { S as ServContext } from '../serv-context-C48WdBcd.js';
|
|
6
|
+
import { A as AuthenticatedServContext } from '../serv-router-BX8hgYxo.js';
|
|
6
7
|
import '@h-ai/core';
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* 对不同于当前认证用户的 AI 主体进行授权的请求。
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import type { AiObjectAuthorizationRequest } from '@h-ai/serv/features/ai'
|
|
15
|
+
*
|
|
16
|
+
* const authorize = ({ context, operation }: AiObjectAuthorizationRequest) =>
|
|
17
|
+
* operation === 'history' && context.session.permissions.includes('ai.history.manage')
|
|
18
|
+
* // 租户型应用还应在此核验目标 objectId 与当前租户的真实关系。
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
interface AiObjectAuthorizationRequest {
|
|
22
|
+
/** 本次操作;授权可按读写能力分别限制。 */
|
|
23
|
+
readonly operation: 'completion' | 'message' | 'history' | 'memoryRecall' | 'memoryList' | 'sessions';
|
|
24
|
+
/** 可信认证上下文;不从客户端输入解析身份。 */
|
|
25
|
+
readonly context: AuthenticatedServContext;
|
|
26
|
+
/** 请求访问的主体 ID;必须由策略核验所有者/租户关系。 */
|
|
27
|
+
readonly objectId: string;
|
|
28
|
+
/** 请求指定的会话 ID;始终与 objectId 一起构成作用域。 */
|
|
29
|
+
readonly sessionId?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* AI 默认 procedures 依赖;只要求实际使用的 AI 能力。
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { ai } from '@h-ai/ai'
|
|
37
|
+
* import { createAiProcedures } from '@h-ai/serv/features/ai'
|
|
38
|
+
*
|
|
39
|
+
* // 宿主已初始化 AI 及其共享存储;省略策略时只允许当前 session.userId。
|
|
40
|
+
* const procedures = createAiProcedures({ ai })
|
|
41
|
+
* // 默认 completion 为非流式 JSON;object/session 作用域来自可信认证上下文。
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
9
44
|
interface AiProcedureDeps {
|
|
10
|
-
readonly ai: AIFunctions
|
|
45
|
+
readonly ai: Pick<AIFunctions, 'config'> & {
|
|
46
|
+
readonly llm: Pick<AIFunctions['llm'], 'chat' | 'ask' | 'getHistory' | 'listSessions'>;
|
|
47
|
+
readonly memory: Pick<AIFunctions['memory'], 'recall' | 'listPage'>;
|
|
48
|
+
};
|
|
49
|
+
/** 跨主体资源授权;省略时仅允许当前 session.userId,必须严格返回 true 才放行。 */
|
|
50
|
+
readonly authorizeObject?: (request: AiObjectAuthorizationRequest) => boolean | Promise<boolean>;
|
|
11
51
|
}
|
|
12
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* 创建以认证用户为默认主体的 AI procedures。
|
|
54
|
+
* 补全仅返回非流式 JSON,完整转发 function 工具和角色消息;工具执行由业务方授权处理。
|
|
55
|
+
*
|
|
56
|
+
* @param deps - 已初始化 AI 能力及可选的跨主体授权策略。
|
|
57
|
+
* @returns AI router;跨主体访问未获授权时返回 FORBIDDEN。
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { ai } from '@h-ai/ai'
|
|
62
|
+
* import { createAiProcedures } from '@h-ai/serv/features/ai'
|
|
63
|
+
*
|
|
64
|
+
* // 宿主已初始化 AI 及其共享存储;省略策略时只允许当前 session.userId。
|
|
65
|
+
* const procedures = createAiProcedures({ ai })
|
|
66
|
+
* // 默认 completion 为非流式 JSON;object/session 作用域来自可信认证上下文。
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
13
69
|
declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
14
70
|
chats: {
|
|
15
71
|
createCompletion: _orpc_server.Procedure<ServContext & {}, any, zod.ZodObject<{
|
|
72
|
+
objectId: zod.ZodOptional<zod.ZodString>;
|
|
73
|
+
sessionId: zod.ZodOptional<zod.ZodString>;
|
|
16
74
|
model: zod.ZodOptional<zod.ZodString>;
|
|
17
|
-
messages: zod.ZodArray<zod.ZodObject<{
|
|
18
|
-
role: zod.
|
|
19
|
-
|
|
20
|
-
user: "user";
|
|
21
|
-
assistant: "assistant";
|
|
22
|
-
tool: "tool";
|
|
23
|
-
}>;
|
|
24
|
-
content: zod.ZodOptional<zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodUnion<readonly [zod.ZodObject<{
|
|
75
|
+
messages: zod.ZodArray<zod.ZodDiscriminatedUnion<[zod.ZodObject<{
|
|
76
|
+
role: zod.ZodLiteral<"system">;
|
|
77
|
+
content: zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodObject<{
|
|
25
78
|
type: zod.ZodLiteral<"text">;
|
|
26
79
|
text: zod.ZodString;
|
|
27
|
-
}, zod_v4_core.$
|
|
80
|
+
}, zod_v4_core.$strict>>]>;
|
|
81
|
+
name: zod.ZodOptional<zod.ZodString>;
|
|
82
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
83
|
+
role: zod.ZodLiteral<"user">;
|
|
84
|
+
content: zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodUnion<readonly [zod.ZodObject<{
|
|
85
|
+
type: zod.ZodLiteral<"text">;
|
|
86
|
+
text: zod.ZodString;
|
|
87
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
28
88
|
type: zod.ZodLiteral<"image_url">;
|
|
29
89
|
image_url: zod.ZodObject<{
|
|
30
90
|
url: zod.ZodString;
|
|
@@ -33,8 +93,15 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
33
93
|
low: "low";
|
|
34
94
|
high: "high";
|
|
35
95
|
}>>;
|
|
36
|
-
}, zod_v4_core.$
|
|
37
|
-
}, zod_v4_core.$
|
|
96
|
+
}, zod_v4_core.$strict>;
|
|
97
|
+
}, zod_v4_core.$strict>]>>]>;
|
|
98
|
+
name: zod.ZodOptional<zod.ZodString>;
|
|
99
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
100
|
+
role: zod.ZodLiteral<"assistant">;
|
|
101
|
+
content: zod.ZodOptional<zod.ZodNullable<zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodObject<{
|
|
102
|
+
type: zod.ZodLiteral<"text">;
|
|
103
|
+
text: zod.ZodString;
|
|
104
|
+
}, zod_v4_core.$strict>>]>>>;
|
|
38
105
|
name: zod.ZodOptional<zod.ZodString>;
|
|
39
106
|
tool_calls: zod.ZodOptional<zod.ZodArray<zod.ZodObject<{
|
|
40
107
|
id: zod.ZodString;
|
|
@@ -42,37 +109,35 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
42
109
|
function: zod.ZodObject<{
|
|
43
110
|
name: zod.ZodString;
|
|
44
111
|
arguments: zod.ZodString;
|
|
45
|
-
}, zod_v4_core.$
|
|
46
|
-
}, zod_v4_core.$
|
|
47
|
-
|
|
48
|
-
|
|
112
|
+
}, zod_v4_core.$strict>;
|
|
113
|
+
}, zod_v4_core.$strict>>>;
|
|
114
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
115
|
+
role: zod.ZodLiteral<"tool">;
|
|
116
|
+
content: zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodObject<{
|
|
117
|
+
type: zod.ZodLiteral<"text">;
|
|
118
|
+
text: zod.ZodString;
|
|
119
|
+
}, zod_v4_core.$strict>>]>;
|
|
120
|
+
tool_call_id: zod.ZodString;
|
|
121
|
+
}, zod_v4_core.$strict>], "role">>;
|
|
49
122
|
temperature: zod.ZodOptional<zod.ZodNumber>;
|
|
50
123
|
top_p: zod.ZodOptional<zod.ZodNumber>;
|
|
51
124
|
max_tokens: zod.ZodOptional<zod.ZodNumber>;
|
|
52
|
-
stream: zod.ZodOptional<zod.ZodBoolean>;
|
|
53
125
|
tools: zod.ZodOptional<zod.ZodArray<zod.ZodObject<{
|
|
54
126
|
type: zod.ZodLiteral<"function">;
|
|
55
127
|
function: zod.ZodObject<{
|
|
56
128
|
name: zod.ZodString;
|
|
57
129
|
description: zod.ZodOptional<zod.ZodString>;
|
|
58
|
-
parameters: zod.ZodOptional<zod.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
enum: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
|
|
64
|
-
}, zod_v4_core.$strip>>>;
|
|
65
|
-
required: zod.ZodOptional<zod.ZodArray<zod.ZodString>>;
|
|
66
|
-
}, zod_v4_core.$strip>>;
|
|
67
|
-
}, zod_v4_core.$strip>;
|
|
68
|
-
}, zod_v4_core.$strip>>>;
|
|
69
|
-
tool_choice: zod.ZodOptional<zod.ZodUnion<readonly [zod.ZodLiteral<"auto">, zod.ZodLiteral<"none">, zod.ZodObject<{
|
|
130
|
+
parameters: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodJSONSchema>>;
|
|
131
|
+
strict: zod.ZodOptional<zod.ZodNullable<zod.ZodBoolean>>;
|
|
132
|
+
}, zod_v4_core.$strict>;
|
|
133
|
+
}, zod_v4_core.$strict>>>;
|
|
134
|
+
tool_choice: zod.ZodOptional<zod.ZodUnion<readonly [zod.ZodLiteral<"auto">, zod.ZodLiteral<"none">, zod.ZodLiteral<"required">, zod.ZodObject<{
|
|
70
135
|
type: zod.ZodLiteral<"function">;
|
|
71
136
|
function: zod.ZodObject<{
|
|
72
137
|
name: zod.ZodString;
|
|
73
|
-
}, zod_v4_core.$
|
|
74
|
-
}, zod_v4_core.$
|
|
75
|
-
}, zod_v4_core.$
|
|
138
|
+
}, zod_v4_core.$strict>;
|
|
139
|
+
}, zod_v4_core.$strict>]>>;
|
|
140
|
+
}, zod_v4_core.$strict>, zod.ZodDiscriminatedUnion<[zod.ZodObject<{
|
|
76
141
|
success: zod.ZodLiteral<true>;
|
|
77
142
|
data: zod.ZodObject<{
|
|
78
143
|
id: zod.ZodString;
|
|
@@ -90,8 +155,8 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
90
155
|
function: zod.ZodObject<{
|
|
91
156
|
name: zod.ZodString;
|
|
92
157
|
arguments: zod.ZodString;
|
|
93
|
-
}, zod_v4_core.$
|
|
94
|
-
}, zod_v4_core.$
|
|
158
|
+
}, zod_v4_core.$strict>;
|
|
159
|
+
}, zod_v4_core.$strict>>>;
|
|
95
160
|
}, zod_v4_core.$strip>;
|
|
96
161
|
finish_reason: zod.ZodEnum<{
|
|
97
162
|
length: "length";
|
|
@@ -120,6 +185,8 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
120
185
|
}, zod_v4_core.$strip>;
|
|
121
186
|
}, zod_v4_core.$strip>], "success">, Record<never, never>, Record<never, never>>;
|
|
122
187
|
sendMessage: _orpc_server.Procedure<ServContext & {}, any, zod.ZodObject<{
|
|
188
|
+
objectId: zod.ZodOptional<zod.ZodString>;
|
|
189
|
+
sessionId: zod.ZodOptional<zod.ZodString>;
|
|
123
190
|
message: zod.ZodString;
|
|
124
191
|
systemPrompt: zod.ZodOptional<zod.ZodString>;
|
|
125
192
|
model: zod.ZodOptional<zod.ZodString>;
|
|
@@ -149,7 +216,7 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
149
216
|
}, zod_v4_core.$strip>;
|
|
150
217
|
}, zod_v4_core.$strip>], "success">, Record<never, never>, Record<never, never>>;
|
|
151
218
|
listHistory: _orpc_server.Procedure<ServContext & {}, any, zod.ZodObject<{
|
|
152
|
-
objectId: zod.ZodString
|
|
219
|
+
objectId: zod.ZodOptional<zod.ZodString>;
|
|
153
220
|
sessionId: zod.ZodString;
|
|
154
221
|
limit: zod.ZodOptional<zod.ZodNumber>;
|
|
155
222
|
order: zod.ZodOptional<zod.ZodEnum<{
|
|
@@ -165,17 +232,19 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
165
232
|
sessionId: zod.ZodString;
|
|
166
233
|
request: zod.ZodObject<{
|
|
167
234
|
model: zod.ZodString;
|
|
168
|
-
messages: zod.ZodArray<zod.ZodObject<{
|
|
169
|
-
role: zod.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
235
|
+
messages: zod.ZodArray<zod.ZodDiscriminatedUnion<[zod.ZodObject<{
|
|
236
|
+
role: zod.ZodLiteral<"system">;
|
|
237
|
+
content: zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodObject<{
|
|
238
|
+
type: zod.ZodLiteral<"text">;
|
|
239
|
+
text: zod.ZodString;
|
|
240
|
+
}, zod_v4_core.$strict>>]>;
|
|
241
|
+
name: zod.ZodOptional<zod.ZodString>;
|
|
242
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
243
|
+
role: zod.ZodLiteral<"user">;
|
|
244
|
+
content: zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodUnion<readonly [zod.ZodObject<{
|
|
176
245
|
type: zod.ZodLiteral<"text">;
|
|
177
246
|
text: zod.ZodString;
|
|
178
|
-
}, zod_v4_core.$
|
|
247
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
179
248
|
type: zod.ZodLiteral<"image_url">;
|
|
180
249
|
image_url: zod.ZodObject<{
|
|
181
250
|
url: zod.ZodString;
|
|
@@ -184,8 +253,15 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
184
253
|
low: "low";
|
|
185
254
|
high: "high";
|
|
186
255
|
}>>;
|
|
187
|
-
}, zod_v4_core.$
|
|
188
|
-
}, zod_v4_core.$
|
|
256
|
+
}, zod_v4_core.$strict>;
|
|
257
|
+
}, zod_v4_core.$strict>]>>]>;
|
|
258
|
+
name: zod.ZodOptional<zod.ZodString>;
|
|
259
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
260
|
+
role: zod.ZodLiteral<"assistant">;
|
|
261
|
+
content: zod.ZodOptional<zod.ZodNullable<zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodObject<{
|
|
262
|
+
type: zod.ZodLiteral<"text">;
|
|
263
|
+
text: zod.ZodString;
|
|
264
|
+
}, zod_v4_core.$strict>>]>>>;
|
|
189
265
|
name: zod.ZodOptional<zod.ZodString>;
|
|
190
266
|
tool_calls: zod.ZodOptional<zod.ZodArray<zod.ZodObject<{
|
|
191
267
|
id: zod.ZodString;
|
|
@@ -193,10 +269,16 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
193
269
|
function: zod.ZodObject<{
|
|
194
270
|
name: zod.ZodString;
|
|
195
271
|
arguments: zod.ZodString;
|
|
196
|
-
}, zod_v4_core.$
|
|
197
|
-
}, zod_v4_core.$
|
|
198
|
-
|
|
199
|
-
|
|
272
|
+
}, zod_v4_core.$strict>;
|
|
273
|
+
}, zod_v4_core.$strict>>>;
|
|
274
|
+
}, zod_v4_core.$strict>, zod.ZodObject<{
|
|
275
|
+
role: zod.ZodLiteral<"tool">;
|
|
276
|
+
content: zod.ZodUnion<readonly [zod.ZodString, zod.ZodArray<zod.ZodObject<{
|
|
277
|
+
type: zod.ZodLiteral<"text">;
|
|
278
|
+
text: zod.ZodString;
|
|
279
|
+
}, zod_v4_core.$strict>>]>;
|
|
280
|
+
tool_call_id: zod.ZodString;
|
|
281
|
+
}, zod_v4_core.$strict>], "role">>;
|
|
200
282
|
}, zod_v4_core.$strip>;
|
|
201
283
|
response: zod.ZodObject<{
|
|
202
284
|
content: zod.ZodString;
|
|
@@ -206,8 +288,8 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
206
288
|
function: zod.ZodObject<{
|
|
207
289
|
name: zod.ZodString;
|
|
208
290
|
arguments: zod.ZodString;
|
|
209
|
-
}, zod_v4_core.$
|
|
210
|
-
}, zod_v4_core.$
|
|
291
|
+
}, zod_v4_core.$strict>;
|
|
292
|
+
}, zod_v4_core.$strict>>>;
|
|
211
293
|
finishReason: zod.ZodString;
|
|
212
294
|
usage: zod.ZodObject<{
|
|
213
295
|
prompt_tokens: zod.ZodNumber;
|
|
@@ -329,7 +411,7 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
329
411
|
};
|
|
330
412
|
sessions: {
|
|
331
413
|
list: _orpc_server.Procedure<ServContext & {}, any, zod.ZodObject<{
|
|
332
|
-
objectId: zod.ZodString
|
|
414
|
+
objectId: zod.ZodOptional<zod.ZodString>;
|
|
333
415
|
}, zod_v4_core.$strip>, zod.ZodDiscriminatedUnion<[zod.ZodObject<{
|
|
334
416
|
success: zod.ZodLiteral<true>;
|
|
335
417
|
data: zod.ZodObject<{
|
|
@@ -357,4 +439,4 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
|
|
|
357
439
|
};
|
|
358
440
|
};
|
|
359
441
|
|
|
360
|
-
export { type AiProcedureDeps, createAiProcedures };
|
|
442
|
+
export { type AiObjectAuthorizationRequest, type AiProcedureDeps, createAiProcedures };
|
package/dist/features/ai.js
CHANGED
|
@@ -1,61 +1,70 @@
|
|
|
1
|
-
import { wrapItemsResult } from '../chunk-
|
|
2
|
-
import { implement } from '../chunk-
|
|
1
|
+
import { wrapItemsResult } from '../chunk-XKCLD42I.js';
|
|
2
|
+
import { implement, servM } from '../chunk-TWHCU6HT.js';
|
|
3
3
|
import { apiContract } from '@h-ai/api-contract';
|
|
4
|
-
import { ok } from '@h-ai/core';
|
|
4
|
+
import { ok, err, HaiCommonError } from '@h-ai/core';
|
|
5
5
|
|
|
6
6
|
var aiContract = apiContract.ai;
|
|
7
7
|
function createAiProcedures(deps) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
async function resolveObjectId(operation, context, input) {
|
|
9
|
+
const objectId = input.objectId ?? context.session.userId;
|
|
10
|
+
if (objectId !== context.session.userId && await deps.authorizeObject?.({ context, objectId, sessionId: input.sessionId, operation }) !== true) {
|
|
11
|
+
return err(HaiCommonError.FORBIDDEN, servM("serv_errorForbidden", { locale: context.locale }));
|
|
12
|
+
}
|
|
13
|
+
return ok(objectId);
|
|
14
|
+
}
|
|
15
|
+
return implement(aiContract).context().route("chats.createCompletion").auth().handle(async ({ input, context }) => {
|
|
16
|
+
const objectId = await resolveObjectId("completion", context, input);
|
|
17
|
+
if (!objectId.success)
|
|
18
|
+
return objectId;
|
|
19
|
+
return mapChatCompletionResult(await deps.ai.llm.chat({
|
|
20
|
+
...toChatCompletionRequest(input),
|
|
21
|
+
objectId: objectId.data,
|
|
22
|
+
sessionId: input.sessionId ?? "default",
|
|
23
|
+
signal: context.request.signal
|
|
24
|
+
}));
|
|
25
|
+
}).route("chats.sendMessage").auth().handle(async ({ input, context }) => {
|
|
26
|
+
const objectId = await resolveObjectId("message", context, input);
|
|
27
|
+
if (!objectId.success)
|
|
28
|
+
return objectId;
|
|
29
|
+
return sendMessage(deps, input, { objectId: objectId.data, sessionId: input.sessionId ?? "default", signal: context.request.signal });
|
|
30
|
+
}).route("chats.listHistory").auth().handle(async ({ input, context }) => {
|
|
31
|
+
const objectId = await resolveObjectId("history", context, input);
|
|
32
|
+
if (!objectId.success)
|
|
33
|
+
return objectId;
|
|
34
|
+
const { sessionId, limit, order } = input;
|
|
12
35
|
return mapChatRecordsResult(
|
|
13
|
-
await deps.ai.llm.getHistory({ objectId, sessionId }, { limit, order })
|
|
36
|
+
await deps.ai.llm.getHistory({ objectId: objectId.data, sessionId }, { limit, order })
|
|
14
37
|
);
|
|
15
|
-
}).route("memories.recall").auth().handle(async ({ input }) => {
|
|
38
|
+
}).route("memories.recall").auth().handle(async ({ input, context }) => {
|
|
39
|
+
const objectId = await resolveObjectId("memoryRecall", context, input);
|
|
40
|
+
if (!objectId.success)
|
|
41
|
+
return objectId;
|
|
16
42
|
const { query, ...options } = input;
|
|
17
|
-
return wrapItemsResult(await deps.ai.memory.recall(query, options));
|
|
18
|
-
}).route("memories.list").auth().handle(
|
|
19
|
-
|
|
43
|
+
return wrapItemsResult(await deps.ai.memory.recall(query, { ...options, objectId: objectId.data }));
|
|
44
|
+
}).route("memories.list").auth().handle(async ({ input, context }) => {
|
|
45
|
+
const objectId = await resolveObjectId("memoryList", context, input);
|
|
46
|
+
if (!objectId.success)
|
|
47
|
+
return objectId;
|
|
48
|
+
return deps.ai.memory.listPage({ ...input, objectId: objectId.data });
|
|
49
|
+
}).route("sessions.list").auth().handle(async ({ input, context }) => {
|
|
50
|
+
const objectId = await resolveObjectId("sessions", context, input);
|
|
51
|
+
if (!objectId.success)
|
|
52
|
+
return objectId;
|
|
53
|
+
return wrapItemsResult(await deps.ai.llm.listSessions(objectId.data));
|
|
20
54
|
}).build();
|
|
21
55
|
}
|
|
22
56
|
function toChatCompletionRequest(input) {
|
|
23
57
|
return {
|
|
24
|
-
|
|
58
|
+
// Schema 已按角色校验完整消息,直接传递以保留 name、图片、tool_calls 和工具结果。
|
|
59
|
+
messages: input.messages,
|
|
25
60
|
model: input.model,
|
|
26
61
|
temperature: input.temperature,
|
|
27
62
|
top_p: input.top_p,
|
|
28
|
-
max_tokens: input.max_tokens
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
function toChatMessage(message) {
|
|
32
|
-
if (message.role === "system") {
|
|
33
|
-
return { role: "system", content: toMessageText(message.content) };
|
|
34
|
-
}
|
|
35
|
-
if (message.role === "user") {
|
|
36
|
-
return { role: "user", content: message.content ?? "" };
|
|
37
|
-
}
|
|
38
|
-
if (message.role === "tool") {
|
|
39
|
-
return { role: "tool", content: toMessageText(message.content), tool_call_id: message.tool_call_id ?? "" };
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
role: "assistant",
|
|
43
|
-
content: toAssistantContent(message.content),
|
|
44
|
-
tool_calls: message.tool_calls
|
|
63
|
+
max_tokens: input.max_tokens,
|
|
64
|
+
tools: input.tools,
|
|
65
|
+
tool_choice: input.tool_choice
|
|
45
66
|
};
|
|
46
67
|
}
|
|
47
|
-
function toMessageText(content) {
|
|
48
|
-
if (typeof content === "string") {
|
|
49
|
-
return content;
|
|
50
|
-
}
|
|
51
|
-
return content?.map((part) => part.type === "text" ? part.text : part.image_url.url).join("\n") ?? "";
|
|
52
|
-
}
|
|
53
|
-
function toAssistantContent(content) {
|
|
54
|
-
if (typeof content === "string") {
|
|
55
|
-
return content;
|
|
56
|
-
}
|
|
57
|
-
return content ? toMessageText(content) : null;
|
|
58
|
-
}
|
|
59
68
|
function mapChatCompletionResult(result) {
|
|
60
69
|
if (!result.success) {
|
|
61
70
|
return result;
|
|
@@ -124,13 +133,14 @@ function toApiChatMessage(message) {
|
|
|
124
133
|
return {
|
|
125
134
|
role: "assistant",
|
|
126
135
|
content: toContentText(message.content),
|
|
136
|
+
name: message.name,
|
|
127
137
|
tool_calls: mapFunctionToolCalls(message.tool_calls)
|
|
128
138
|
};
|
|
129
139
|
}
|
|
130
140
|
if (message.role === "user") {
|
|
131
|
-
return { role: "user", content: toContentText(message.content) };
|
|
141
|
+
return { role: "user", content: toContentText(message.content), name: message.name };
|
|
132
142
|
}
|
|
133
|
-
return { role: "system", content: toContentText(message.content) };
|
|
143
|
+
return { role: "system", content: toContentText(message.content), name: message.role === "system" || message.role === "developer" ? message.name : void 0 };
|
|
134
144
|
}
|
|
135
145
|
function toContentText(content) {
|
|
136
146
|
if (typeof content === "string") {
|
|
@@ -169,11 +179,12 @@ function mapFunctionToolCalls(toolCalls) {
|
|
|
169
179
|
}];
|
|
170
180
|
});
|
|
171
181
|
}
|
|
172
|
-
async function sendMessage(deps, input) {
|
|
182
|
+
async function sendMessage(deps, input, scope) {
|
|
173
183
|
const result = await deps.ai.llm.ask(input.message, {
|
|
174
184
|
systemPrompt: input.systemPrompt,
|
|
175
185
|
model: input.model,
|
|
176
|
-
temperature: input.temperature
|
|
186
|
+
temperature: input.temperature,
|
|
187
|
+
...scope
|
|
177
188
|
});
|
|
178
189
|
if (!result.success) {
|
|
179
190
|
return result;
|
package/dist/features/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/features/serv-feature-ai.ts"],"names":[],"mappings":";;;;;AA2BA,IAAM,aAAa,WAAA,CAAY,EAAA;AAQxB,SAAS,mBAAmB,IAAA,EAAuB;AACxD,EAAA,OAAO,SAAA,CAAU,UAAU,CAAA,CACxB,OAAA,GAEA,KAAA,CAAM,wBAAwB,CAAA,CAC9B,IAAA,EAAK,CACL,MAAA,CAAO,OAAO,EAAE,OAAM,KAAM;AAC3B,IAAA,OAAO,uBAAA,CAAwB,MAAM,IAAA,CAAK,EAAA,CAAG,IAAI,IAAA,CAAK,uBAAA,CAAwB,KAAK,CAAC,CAAC,CAAA;AAAA,EACvF,CAAC,CAAA,CAEA,KAAA,CAAM,mBAAmB,CAAA,CACzB,IAAA,EAAK,CACL,MAAA,CAAO,CAAC,EAAE,KAAA,EAAM,KAAM,WAAA,CAAY,MAAM,KAAK,CAAC,CAAA,CAE9C,KAAA,CAAM,mBAAmB,CAAA,CACzB,IAAA,EAAK,CACL,MAAA,CAAO,OAAO,EAAE,KAAA,EAAM,KAAM;AAC3B,IAAA,MAAM,EAAE,QAAA,EAAU,SAAA,EAAW,KAAA,EAAO,OAAM,GAAI,KAAA;AAC9C,IAAA,OAAO,oBAAA;AAAA,MACL,MAAM,IAAA,CAAK,EAAA,CAAG,GAAA,CAAI,UAAA,CAAW,EAAE,QAAA,EAAU,SAAA,EAAU,EAAG,EAAE,KAAA,EAAO,KAAA,EAAO;AAAA,KACxE;AAAA,EACF,CAAC,CAAA,CAEA,KAAA,CAAM,iBAAiB,CAAA,CACvB,IAAA,EAAK,CACL,MAAA,CAAO,OAAO,EAAE,KAAA,EAAM,KAAM;AAC3B,IAAA,MAAM,EAAE,KAAA,EAAO,GAAG,OAAA,EAAQ,GAAI,KAAA;AAC9B,IAAA,OAAO,eAAA,CAAgB,MAAM,IAAA,CAAK,EAAA,CAAG,OAAO,MAAA,CAAO,KAAA,EAAO,OAAO,CAAC,CAAA;AAAA,EACpE,CAAC,CAAA,CAEA,KAAA,CAAM,eAAe,CAAA,CACrB,IAAA,EAAK,CACL,MAAA,CAAO,CAAC,EAAE,KAAA,EAAM,KAAM,IAAA,CAAK,EAAA,CAAG,MAAA,CAAO,QAAA,CAAS,KAAK,CAAC,CAAA,CAEpD,KAAA,CAAM,eAAe,CAAA,CACrB,IAAA,EAAK,CACL,MAAA,CAAO,OAAO,EAAE,KAAA,EAAM,KAAM;AAC3B,IAAA,OAAO,eAAA,CAAgB,MAAM,IAAA,CAAK,EAAA,CAAG,IAAI,YAAA,CAAa,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA,EACvE,CAAC,EAEA,KAAA,EAAM;AACX;AAEA,SAAS,wBAAwB,KAAA,EAAqD;AACpF,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,aAAa,CAAA;AAAA,IAC1C,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,aAAa,KAAA,CAAM,WAAA;AAAA,IACnB,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,YAAY,KAAA,CAAM;AAAA,GACpB;AACF;AAEA,SAAS,cAAc,OAAA,EAAiE;AACtF,EAAA,IAAI,OAAA,CAAQ,SAAS,QAAA,EAAU;AAC7B,IAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,SAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAE;AAAA,EACnE;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,OAAA,CAAQ,WAAW,EAAA,EAAG;AAAA,EACxD;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAG,YAAA,EAAc,OAAA,CAAQ,YAAA,IAAgB,EAAA,EAAG;AAAA,EAC3G;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,OAAA,EAAS,kBAAA,CAAmB,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC3C,YAAY,OAAA,CAAQ;AAAA,GACtB;AACF;AAEA,SAAS,cAAc,OAAA,EAAuE;AAC5F,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAA,EAAS,GAAA,CAAI,CAAA,IAAA,KAAQ,IAAA,CAAK,SAAS,MAAA,GAAS,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,IAAK,EAAA;AACnG;AAEA,SAAS,mBAAmB,OAAA,EAA8E;AACxG,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAA,GAAU,aAAA,CAAc,OAAO,CAAA,GAAI,IAAA;AAC5C;AAEA,SAAS,wBAAwB,MAAA,EAA4E;AAC3G,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAA,CAAG;AAAA,IACR,EAAA,EAAI,OAAO,IAAA,CAAK,EAAA;AAAA,IAChB,MAAA,EAAQ,iBAAA;AAAA,IACR,OAAA,EAAS,OAAO,IAAA,CAAK,OAAA;AAAA,IACrB,KAAA,EAAO,OAAO,IAAA,CAAK,KAAA;AAAA,IACnB,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAA,MAAA,MAAW;AAAA,MAC1C,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAA,EAAS;AAAA,QACP,IAAA,EAAM,WAAA;AAAA,QACN,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,OAAA,IAAW,IAAA;AAAA,QACnC,UAAA,EAAY,oBAAA,CAAqB,MAAA,CAAO,OAAA,CAAQ,UAAU;AAAA,OAC5D;AAAA,MACA,aAAA,EAAe,iBAAA,CAAkB,MAAA,CAAO,aAAa;AAAA,KACvD,CAAE,CAAA;AAAA,IACF,KAAA,EAAO;AAAA,MACL,aAAA,EAAe,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,aAAA,IAAiB,CAAA;AAAA,MACnD,iBAAA,EAAmB,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,iBAAA,IAAqB,CAAA;AAAA,MAC3D,YAAA,EAAc,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,YAAA,IAAgB;AAAA;AACnD,GACD,CAAA;AACH;AAEA,SAAS,kBAAkB,MAAA,EAAiF;AAC1G,EAAA,IAAI,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,YAAA,IAAgB,WAAW,gBAAA,EAAkB;AACjF,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,qBAAqB,MAAA,EAA+F;AAC3H,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAA,CAAG,EAAE,KAAA,EAAO,MAAA,CAAO,KAAK,GAAA,CAAI,eAAe,GAAG,CAAA;AACvD;AAEA,SAAS,gBAAgB,MAAA,EAAqD;AAC5E,EAAA,OAAO;AAAA,IACL,IAAI,MAAA,CAAO,EAAA;AAAA,IACX,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,OAAA,EAAS;AAAA,MACP,KAAA,EAAO,OAAO,OAAA,CAAQ,KAAA;AAAA,MACtB,QAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,IAAI,gBAAgB;AAAA,KACxD;AAAA,IACA,QAAA,EAAU;AAAA,MACR,OAAA,EAAS,OAAO,QAAA,CAAS,OAAA;AAAA,MACzB,SAAA,EAAW,oBAAA,CAAqB,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA;AAAA,MACzD,YAAA,EAAc,OAAO,QAAA,CAAS,YAAA;AAAA,MAC9B,KAAA,EAAO;AAAA,QACL,aAAA,EAAe,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,aAAA;AAAA,QACrC,iBAAA,EAAmB,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,iBAAA;AAAA,QACzC,YAAA,EAAc,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM;AAAA;AACtC,KACF;AAAA,IACA,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,UAAU,MAAA,CAAO;AAAA,GACnB;AACF;AAEA,SAAS,iBAAiB,OAAA,EAAiE;AACzF,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,aAAA,CAAc,QAAQ,OAAO,CAAA,EAAG,YAAA,EAAc,OAAA,CAAQ,YAAA,EAAa;AAAA,EACrG;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,WAAA,EAAa;AAChC,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,WAAA;AAAA,MACN,OAAA,EAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA;AAAA,MACtC,UAAA,EAAY,oBAAA,CAAqB,OAAA,CAAQ,UAAU;AAAA,KACrD;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAE;AAAA,EACjE;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,SAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAE;AACnE;AAEA,SAAS,cAAc,OAAA,EAA0B;AAC/C,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC3B,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,IAAA,KAAS;AAC3B,IAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,EAAM;AAC7C,MAAA,OAAO,EAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAAA,GAAS,IAAA;AACf,IAAA,IAAI,OAAO,MAAA,CAAO,IAAA,KAAS,QAAA,EAAU;AACnC,MAAA,OAAO,MAAA,CAAO,IAAA;AAAA,IAChB;AAEA,IAAA,IAAI,OAAO,MAAA,CAAO,SAAA,KAAc,QAAA,IAAY,MAAA,CAAO,cAAc,IAAA,EAAM;AACrE,MAAA,MAAM,QAAQ,MAAA,CAAO,SAAA;AACrB,MAAA,OAAO,OAAO,KAAA,CAAM,GAAA,KAAQ,QAAA,GAAW,MAAM,GAAA,GAAM,EAAA;AAAA,IACrD;AAEA,IAAA,OAAO,EAAA;AAAA,EACT,CAAC,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,KAAK,IAAI,CAAA;AAC9B;AAEA,SAAS,qBAAqB,SAAA,EAAmC;AAC/D,EAAA,OAAO,SAAA,EAAW,OAAA,CAAQ,CAAC,QAAA,KAAa;AACtC,IAAA,IAAI,QAAA,CAAS,IAAA,KAAS,UAAA,IAAc,EAAE,cAAc,QAAA,CAAA,EAAW;AAC7D,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,OAAO,CAAC;AAAA,MACN,IAAI,QAAA,CAAS,EAAA;AAAA,MACb,IAAA,EAAM,UAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR,IAAA,EAAM,SAAS,QAAA,CAAS,IAAA;AAAA,QACxB,SAAA,EAAW,SAAS,QAAA,CAAS;AAAA;AAC/B,KACD,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAEA,eAAe,WAAA,CACb,MACA,KAAA,EACwD;AACxD,EAAA,MAAM,SAAS,MAAM,IAAA,CAAK,GAAG,GAAA,CAAI,GAAA,CAAI,MAAM,OAAA,EAAS;AAAA,IAClD,cAAc,KAAA,CAAM,YAAA;AAAA,IACpB,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,aAAa,KAAA,CAAM;AAAA,GACpB,CAAA;AAED,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAA,CAAG;AAAA,IACR,SAAS,MAAA,CAAO,IAAA;AAAA,IAChB,OAAO,KAAA,CAAM,KAAA,IAAS,KAAK,EAAA,CAAG,MAAA,EAAQ,IAAI,KAAA,IAAS;AAAA,GACpD,CAAA;AACH","file":"ai.js","sourcesContent":["/**\n * @h-ai/serv — AI 默认 procedures\n *\n * 基于 `@h-ai/ai` 提供开箱即用的 AI procedures 实现:对话补全、消息发送、聊天历史、记忆管理、会话列表。\n * @module features/serv-feature-ai\n */\n\nimport type {\n AIFunctions,\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatMessage,\n ToolCall,\n} from '@h-ai/ai'\nimport type {\n AiChatCompletionData,\n AiChatCompletionInput,\n AiChatRecord,\n AiSendMessageInput,\n} from '@h-ai/api-contract'\nimport type { HaiResult } from '@h-ai/core'\nimport type { ServContext } from '../serv-context.js'\nimport { apiContract } from '@h-ai/api-contract'\nimport { ok } from '@h-ai/core'\nimport { implement } from '../serv-router.js'\nimport { wrapItemsResult } from './serv-feature-helpers.js'\n\nconst aiContract = apiContract.ai\n\n/** AI 默认 procedures 依赖。 */\nexport interface AiProcedureDeps {\n readonly ai: AIFunctions\n}\n\n/** 创建 AI 默认 procedures。 */\nexport function createAiProcedures(deps: AiProcedureDeps) {\n return implement(aiContract)\n .context<ServContext>()\n\n .route('chats.createCompletion')\n .auth()\n .handle(async ({ input }) => {\n return mapChatCompletionResult(await deps.ai.llm.chat(toChatCompletionRequest(input)))\n })\n\n .route('chats.sendMessage')\n .auth()\n .handle(({ input }) => sendMessage(deps, input))\n\n .route('chats.listHistory')\n .auth()\n .handle(async ({ input }) => {\n const { objectId, sessionId, limit, order } = input\n return mapChatRecordsResult(\n await deps.ai.llm.getHistory({ objectId, sessionId }, { limit, order }),\n )\n })\n\n .route('memories.recall')\n .auth()\n .handle(async ({ input }) => {\n const { query, ...options } = input\n return wrapItemsResult(await deps.ai.memory.recall(query, options))\n })\n\n .route('memories.list')\n .auth()\n .handle(({ input }) => deps.ai.memory.listPage(input))\n\n .route('sessions.list')\n .auth()\n .handle(async ({ input }) => {\n return wrapItemsResult(await deps.ai.llm.listSessions(input.objectId))\n })\n\n .build()\n}\n\nfunction toChatCompletionRequest(input: AiChatCompletionInput): ChatCompletionRequest {\n return {\n messages: input.messages.map(toChatMessage),\n model: input.model,\n temperature: input.temperature,\n top_p: input.top_p,\n max_tokens: input.max_tokens,\n }\n}\n\nfunction toChatMessage(message: AiChatCompletionInput['messages'][number]): ChatMessage {\n if (message.role === 'system') {\n return { role: 'system', content: toMessageText(message.content) }\n }\n\n if (message.role === 'user') {\n return { role: 'user', content: message.content ?? '' }\n }\n\n if (message.role === 'tool') {\n return { role: 'tool', content: toMessageText(message.content), tool_call_id: message.tool_call_id ?? '' }\n }\n\n return {\n role: 'assistant',\n content: toAssistantContent(message.content),\n tool_calls: message.tool_calls,\n }\n}\n\nfunction toMessageText(content: AiChatCompletionInput['messages'][number]['content']): string {\n if (typeof content === 'string') {\n return content\n }\n\n return content?.map(part => part.type === 'text' ? part.text : part.image_url.url).join('\\n') ?? ''\n}\n\nfunction toAssistantContent(content: AiChatCompletionInput['messages'][number]['content']): string | null {\n if (typeof content === 'string') {\n return content\n }\n\n return content ? toMessageText(content) : null\n}\n\nfunction mapChatCompletionResult(result: HaiResult<ChatCompletionResponse>): HaiResult<AiChatCompletionData> {\n if (!result.success) {\n return result\n }\n\n return ok({\n id: result.data.id,\n object: 'chat.completion',\n created: result.data.created,\n model: result.data.model,\n choices: result.data.choices.map(choice => ({\n index: choice.index,\n message: {\n role: 'assistant',\n content: choice.message.content ?? null,\n tool_calls: mapFunctionToolCalls(choice.message.tool_calls),\n },\n finish_reason: toApiFinishReason(choice.finish_reason),\n })),\n usage: {\n prompt_tokens: result.data.usage?.prompt_tokens ?? 0,\n completion_tokens: result.data.usage?.completion_tokens ?? 0,\n total_tokens: result.data.usage?.total_tokens ?? 0,\n },\n })\n}\n\nfunction toApiFinishReason(reason: string | null): AiChatCompletionData['choices'][number]['finish_reason'] {\n if (reason === 'length' || reason === 'tool_calls' || reason === 'content_filter') {\n return reason\n }\n\n return 'stop'\n}\n\nfunction mapChatRecordsResult(result: HaiResult<Array<import('@h-ai/ai').ChatRecord>>): HaiResult<{ items: AiChatRecord[] }> {\n if (!result.success) {\n return result\n }\n\n return ok({ items: result.data.map(toApiChatRecord) })\n}\n\nfunction toApiChatRecord(record: import('@h-ai/ai').ChatRecord): AiChatRecord {\n return {\n id: record.id,\n objectId: record.objectId,\n sessionId: record.sessionId,\n request: {\n model: record.request.model,\n messages: record.request.messages.map(toApiChatMessage),\n },\n response: {\n content: record.response.content,\n toolCalls: mapFunctionToolCalls(record.response.toolCalls),\n finishReason: record.response.finishReason,\n usage: {\n prompt_tokens: record.response.usage.prompt_tokens,\n completion_tokens: record.response.usage.completion_tokens,\n total_tokens: record.response.usage.total_tokens,\n },\n },\n createdAt: record.createdAt,\n duration: record.duration,\n }\n}\n\nfunction toApiChatMessage(message: ChatMessage): AiChatCompletionInput['messages'][number] {\n if (message.role === 'tool') {\n return { role: 'tool', content: toContentText(message.content), tool_call_id: message.tool_call_id }\n }\n\n if (message.role === 'assistant') {\n return {\n role: 'assistant',\n content: toContentText(message.content),\n tool_calls: mapFunctionToolCalls(message.tool_calls),\n }\n }\n\n if (message.role === 'user') {\n return { role: 'user', content: toContentText(message.content) }\n }\n\n return { role: 'system', content: toContentText(message.content) }\n}\n\nfunction toContentText(content: unknown): string {\n if (typeof content === 'string') {\n return content\n }\n\n if (!Array.isArray(content)) {\n return ''\n }\n\n return content.map((part) => {\n if (typeof part !== 'object' || part === null) {\n return ''\n }\n\n const record = part as Record<string, unknown>\n if (typeof record.text === 'string') {\n return record.text\n }\n\n if (typeof record.image_url === 'object' && record.image_url !== null) {\n const image = record.image_url as Record<string, unknown>\n return typeof image.url === 'string' ? image.url : ''\n }\n\n return ''\n }).filter(Boolean).join('\\n')\n}\n\nfunction mapFunctionToolCalls(toolCalls: ToolCall[] | undefined) {\n return toolCalls?.flatMap((toolCall) => {\n if (toolCall.type !== 'function' || !('function' in toolCall)) {\n return []\n }\n\n return [{\n id: toolCall.id,\n type: 'function' as const,\n function: {\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n },\n }]\n })\n}\n\nasync function sendMessage(\n deps: AiProcedureDeps,\n input: AiSendMessageInput,\n): Promise<HaiResult<{ content: string, model: string }>> {\n const result = await deps.ai.llm.ask(input.message, {\n systemPrompt: input.systemPrompt,\n model: input.model,\n temperature: input.temperature,\n })\n\n if (!result.success) {\n return result\n }\n\n return ok({\n content: result.data,\n model: input.model ?? deps.ai.config?.llm.model ?? 'default',\n })\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/features/serv-feature-ai.ts"],"names":[],"mappings":"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","file":"ai.js","sourcesContent":["/**\n * @h-ai/serv — AI 默认 procedures\n *\n * 基于 `@h-ai/ai` 提供开箱即用的 AI procedures 实现:对话补全、消息发送、聊天历史、记忆管理、会话列表。\n * 默认将所有读取和写入绑定到认证 session.userId;跨主体访问必须通过显式授权。\n * @module features/serv-feature-ai\n */\n\nimport type {\n AIFunctions,\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatMessage,\n InteractionScope,\n ToolCall,\n} from '@h-ai/ai'\nimport type {\n AiChatCompletionData,\n AiChatCompletionInput,\n AiChatRecord,\n AiSendMessageInput,\n} from '@h-ai/api-contract'\nimport type { HaiResult } from '@h-ai/core'\nimport type { ServContext } from '../serv-context.js'\nimport type { AuthenticatedServContext } from '../serv-router.js'\nimport { apiContract } from '@h-ai/api-contract'\nimport { err, HaiCommonError, ok } from '@h-ai/core'\nimport { servM } from '../serv-i18n.js'\nimport { implement } from '../serv-router.js'\nimport { wrapItemsResult } from './serv-feature-helpers.js'\n\nconst aiContract = apiContract.ai\n\n/**\n * 对不同于当前认证用户的 AI 主体进行授权的请求。\n *\n * @example\n * ```ts\n * import type { AiObjectAuthorizationRequest } from '@h-ai/serv/features/ai'\n *\n * const authorize = ({ context, operation }: AiObjectAuthorizationRequest) =>\n * operation === 'history' && context.session.permissions.includes('ai.history.manage')\n * // 租户型应用还应在此核验目标 objectId 与当前租户的真实关系。\n * ```\n */\nexport interface AiObjectAuthorizationRequest {\n /** 本次操作;授权可按读写能力分别限制。 */\n readonly operation: 'completion' | 'message' | 'history' | 'memoryRecall' | 'memoryList' | 'sessions'\n /** 可信认证上下文;不从客户端输入解析身份。 */\n readonly context: AuthenticatedServContext\n /** 请求访问的主体 ID;必须由策略核验所有者/租户关系。 */\n readonly objectId: string\n /** 请求指定的会话 ID;始终与 objectId 一起构成作用域。 */\n readonly sessionId?: string\n}\n\n/**\n * AI 默认 procedures 依赖;只要求实际使用的 AI 能力。\n *\n * @example\n * ```ts\n * import { ai } from '@h-ai/ai'\n * import { createAiProcedures } from '@h-ai/serv/features/ai'\n *\n * // 宿主已初始化 AI 及其共享存储;省略策略时只允许当前 session.userId。\n * const procedures = createAiProcedures({ ai })\n * // 默认 completion 为非流式 JSON;object/session 作用域来自可信认证上下文。\n * ```\n */\nexport interface AiProcedureDeps {\n readonly ai: Pick<AIFunctions, 'config'> & {\n readonly llm: Pick<AIFunctions['llm'], 'chat' | 'ask' | 'getHistory' | 'listSessions'>\n readonly memory: Pick<AIFunctions['memory'], 'recall' | 'listPage'>\n }\n /** 跨主体资源授权;省略时仅允许当前 session.userId,必须严格返回 true 才放行。 */\n readonly authorizeObject?: (request: AiObjectAuthorizationRequest) => boolean | Promise<boolean>\n}\n\n/**\n * 创建以认证用户为默认主体的 AI procedures。\n * 补全仅返回非流式 JSON,完整转发 function 工具和角色消息;工具执行由业务方授权处理。\n *\n * @param deps - 已初始化 AI 能力及可选的跨主体授权策略。\n * @returns AI router;跨主体访问未获授权时返回 FORBIDDEN。\n *\n * @example\n * ```ts\n * import { ai } from '@h-ai/ai'\n * import { createAiProcedures } from '@h-ai/serv/features/ai'\n *\n * // 宿主已初始化 AI 及其共享存储;省略策略时只允许当前 session.userId。\n * const procedures = createAiProcedures({ ai })\n * // 默认 completion 为非流式 JSON;object/session 作用域来自可信认证上下文。\n * ```\n */\nexport function createAiProcedures(deps: AiProcedureDeps) {\n async function resolveObjectId(\n operation: AiObjectAuthorizationRequest['operation'],\n context: AuthenticatedServContext,\n input: { objectId?: string, sessionId?: string },\n ): Promise<HaiResult<string>> {\n const objectId = input.objectId ?? context.session.userId\n if (objectId !== context.session.userId\n && await deps.authorizeObject?.({ context, objectId, sessionId: input.sessionId, operation }) !== true) {\n return err(HaiCommonError.FORBIDDEN, servM('serv_errorForbidden', { locale: context.locale }))\n }\n return ok(objectId)\n }\n\n return implement(aiContract)\n .context<ServContext>()\n\n .route('chats.createCompletion')\n .auth()\n .handle(async ({ input, context }) => {\n const objectId = await resolveObjectId('completion', context, input)\n if (!objectId.success)\n return objectId\n return mapChatCompletionResult(await deps.ai.llm.chat({\n ...toChatCompletionRequest(input),\n objectId: objectId.data,\n sessionId: input.sessionId ?? 'default',\n signal: context.request.signal,\n }))\n })\n\n .route('chats.sendMessage')\n .auth()\n .handle(async ({ input, context }) => {\n const objectId = await resolveObjectId('message', context, input)\n if (!objectId.success)\n return objectId\n return sendMessage(deps, input, { objectId: objectId.data, sessionId: input.sessionId ?? 'default', signal: context.request.signal })\n })\n\n .route('chats.listHistory')\n .auth()\n .handle(async ({ input, context }) => {\n const objectId = await resolveObjectId('history', context, input)\n if (!objectId.success)\n return objectId\n const { sessionId, limit, order } = input\n return mapChatRecordsResult(\n await deps.ai.llm.getHistory({ objectId: objectId.data, sessionId }, { limit, order }),\n )\n })\n\n .route('memories.recall')\n .auth()\n .handle(async ({ input, context }) => {\n const objectId = await resolveObjectId('memoryRecall', context, input)\n if (!objectId.success)\n return objectId\n const { query, ...options } = input\n return wrapItemsResult(await deps.ai.memory.recall(query, { ...options, objectId: objectId.data }))\n })\n\n .route('memories.list')\n .auth()\n .handle(async ({ input, context }) => {\n const objectId = await resolveObjectId('memoryList', context, input)\n if (!objectId.success)\n return objectId\n return deps.ai.memory.listPage({ ...input, objectId: objectId.data })\n })\n\n .route('sessions.list')\n .auth()\n .handle(async ({ input, context }) => {\n const objectId = await resolveObjectId('sessions', context, input)\n if (!objectId.success)\n return objectId\n return wrapItemsResult(await deps.ai.llm.listSessions(objectId.data))\n })\n\n .build()\n}\n\nfunction toChatCompletionRequest(input: AiChatCompletionInput): ChatCompletionRequest {\n return {\n // Schema 已按角色校验完整消息,直接传递以保留 name、图片、tool_calls 和工具结果。\n messages: input.messages,\n model: input.model,\n temperature: input.temperature,\n top_p: input.top_p,\n max_tokens: input.max_tokens,\n tools: input.tools,\n tool_choice: input.tool_choice,\n }\n}\n\nfunction mapChatCompletionResult(result: HaiResult<ChatCompletionResponse>): HaiResult<AiChatCompletionData> {\n if (!result.success) {\n return result\n }\n\n return ok({\n id: result.data.id,\n object: 'chat.completion',\n created: result.data.created,\n model: result.data.model,\n choices: result.data.choices.map(choice => ({\n index: choice.index,\n message: {\n role: 'assistant',\n content: choice.message.content ?? null,\n tool_calls: mapFunctionToolCalls(choice.message.tool_calls),\n },\n finish_reason: toApiFinishReason(choice.finish_reason),\n })),\n usage: {\n prompt_tokens: result.data.usage?.prompt_tokens ?? 0,\n completion_tokens: result.data.usage?.completion_tokens ?? 0,\n total_tokens: result.data.usage?.total_tokens ?? 0,\n },\n })\n}\n\nfunction toApiFinishReason(reason: string | null): AiChatCompletionData['choices'][number]['finish_reason'] {\n if (reason === 'length' || reason === 'tool_calls' || reason === 'content_filter') {\n return reason\n }\n\n return 'stop'\n}\n\nfunction mapChatRecordsResult(result: HaiResult<Array<import('@h-ai/ai').ChatRecord>>): HaiResult<{ items: AiChatRecord[] }> {\n if (!result.success) {\n return result\n }\n\n return ok({ items: result.data.map(toApiChatRecord) })\n}\n\nfunction toApiChatRecord(record: import('@h-ai/ai').ChatRecord): AiChatRecord {\n return {\n id: record.id,\n objectId: record.objectId,\n sessionId: record.sessionId,\n request: {\n model: record.request.model,\n messages: record.request.messages.map(toApiChatMessage),\n },\n response: {\n content: record.response.content,\n toolCalls: mapFunctionToolCalls(record.response.toolCalls),\n finishReason: record.response.finishReason,\n usage: {\n prompt_tokens: record.response.usage.prompt_tokens,\n completion_tokens: record.response.usage.completion_tokens,\n total_tokens: record.response.usage.total_tokens,\n },\n },\n createdAt: record.createdAt,\n duration: record.duration,\n }\n}\n\nfunction toApiChatMessage(message: ChatMessage): AiChatCompletionInput['messages'][number] {\n if (message.role === 'tool') {\n return { role: 'tool', content: toContentText(message.content), tool_call_id: message.tool_call_id }\n }\n\n if (message.role === 'assistant') {\n return {\n role: 'assistant',\n content: toContentText(message.content),\n name: message.name,\n tool_calls: mapFunctionToolCalls(message.tool_calls),\n }\n }\n\n if (message.role === 'user') {\n return { role: 'user', content: toContentText(message.content), name: message.name }\n }\n\n return { role: 'system', content: toContentText(message.content), name: message.role === 'system' || message.role === 'developer' ? message.name : undefined }\n}\n\nfunction toContentText(content: unknown): string {\n if (typeof content === 'string') {\n return content\n }\n\n if (!Array.isArray(content)) {\n return ''\n }\n\n return content.map((part) => {\n if (typeof part !== 'object' || part === null) {\n return ''\n }\n\n const record = part as Record<string, unknown>\n if (typeof record.text === 'string') {\n return record.text\n }\n\n if (typeof record.image_url === 'object' && record.image_url !== null) {\n const image = record.image_url as Record<string, unknown>\n return typeof image.url === 'string' ? image.url : ''\n }\n\n return ''\n }).filter(Boolean).join('\\n')\n}\n\nfunction mapFunctionToolCalls(toolCalls: ToolCall[] | undefined) {\n return toolCalls?.flatMap((toolCall) => {\n if (toolCall.type !== 'function' || !('function' in toolCall)) {\n return []\n }\n\n return [{\n id: toolCall.id,\n type: 'function' as const,\n function: {\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n },\n }]\n })\n}\n\nasync function sendMessage(\n deps: AiProcedureDeps,\n input: AiSendMessageInput,\n scope: InteractionScope & { signal: AbortSignal },\n): Promise<HaiResult<{ content: string, model: string }>> {\n const result = await deps.ai.llm.ask(input.message, {\n systemPrompt: input.systemPrompt,\n model: input.model,\n temperature: input.temperature,\n ...scope,\n })\n\n if (!result.success) {\n return result\n }\n\n return ok({\n content: result.data,\n model: input.model ?? deps.ai.config?.llm.model ?? 'default',\n })\n}\n"]}
|
package/dist/features/iam.d.ts
CHANGED
|
@@ -4,14 +4,40 @@ import * as zod from 'zod';
|
|
|
4
4
|
import * as _orpc_contract from '@orpc/contract';
|
|
5
5
|
import { IamFunctions } from '@h-ai/iam';
|
|
6
6
|
export { TokenPair } from '@h-ai/iam';
|
|
7
|
-
import { S as ServContext } from '../serv-context-
|
|
7
|
+
import { S as ServContext } from '../serv-context-C48WdBcd.js';
|
|
8
8
|
import '@h-ai/core';
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* IAM 默认 procedures 依赖。
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { iam } from '@h-ai/iam'
|
|
16
|
+
* import { createIamProcedures } from '@h-ai/serv/features/iam'
|
|
17
|
+
*
|
|
18
|
+
* // 宿主已初始化 IAM 及共享依赖。
|
|
19
|
+
* const procedures = createIamProcedures({ iam })
|
|
20
|
+
* // logout 撤销请求持有的 opaque access token;过期 access 也可撤销 refresh,
|
|
21
|
+
* // 不接收 userId,不要求有效 access 的 auth guard,失败仍检查 HaiResult。
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
11
24
|
interface IamProcedureDeps {
|
|
12
25
|
readonly iam: IamFunctions;
|
|
13
26
|
}
|
|
14
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* 创建默认 IAM procedures;按操作声明认证/权限。logout 只撤销请求持有的完整 opaque token,不要求该 access 尚有效,也不能按 userId 撤销。
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { iam } from '@h-ai/iam'
|
|
33
|
+
* import { createIamProcedures } from '@h-ai/serv/features/iam'
|
|
34
|
+
*
|
|
35
|
+
* // 宿主已初始化 IAM 及共享依赖。
|
|
36
|
+
* const procedures = createIamProcedures({ iam })
|
|
37
|
+
* // logout 撤销请求持有的 opaque access token;过期 access 也可撤销 refresh,
|
|
38
|
+
* // 不接收 userId,不要求有效 access 的 auth guard,失败仍检查 HaiResult。
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
15
41
|
declare function createIamProcedures(deps: IamProcedureDeps): {
|
|
16
42
|
auth: {
|
|
17
43
|
publicConfig: _orpc_server.Procedure<ServContext & {}, any, _orpc_contract.Schema<unknown, unknown>, zod.ZodDiscriminatedUnion<[zod.ZodObject<{
|
|
@@ -174,7 +200,6 @@ declare function createIamProcedures(deps: IamProcedureDeps): {
|
|
|
174
200
|
login: _orpc_server.Procedure<ServContext & {}, any, zod.ZodObject<{
|
|
175
201
|
identifier: zod.ZodString;
|
|
176
202
|
password: zod.ZodString;
|
|
177
|
-
rememberMe: zod.ZodOptional<zod.ZodBoolean>;
|
|
178
203
|
}, zod_v4_core.$strip>, zod.ZodDiscriminatedUnion<[zod.ZodObject<{
|
|
179
204
|
success: zod.ZodLiteral<true>;
|
|
180
205
|
data: zod.ZodObject<{
|
package/dist/features/iam.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mapHaiResult } from '../chunk-
|
|
2
|
-
import { implement, servM } from '../chunk-
|
|
1
|
+
import { mapHaiResult } from '../chunk-XKCLD42I.js';
|
|
2
|
+
import { implement, servM } from '../chunk-TWHCU6HT.js';
|
|
3
3
|
import { apiContract } from '@h-ai/api-contract';
|
|
4
4
|
import { core, ok, err, HaiCommonError } from '@h-ai/core';
|
|
5
5
|
|
|
@@ -7,7 +7,7 @@ var iamContract = apiContract.iam;
|
|
|
7
7
|
var iamFeatureLogger = core.logger.child({ module: "serv", scope: "feature-iam" });
|
|
8
8
|
function createIamProcedures(deps) {
|
|
9
9
|
const { iam } = deps;
|
|
10
|
-
return implement(iamContract).context().route("auth.login", ({ input }) => iam.auth.login(input)).route("auth.loginWithOtp", ({ input }) => iam.auth.loginWithOtp(input)).route("auth.loginWithLdap", ({ input }) => iam.auth.loginWithLdap(input)).route("auth.publicConfig", () => ok(iam.publicConfig)).route("auth.requestPasswordReset", ({ input }) => iam.user.requestPasswordReset(input.identifier)).route("auth.confirmPasswordReset", ({ input }) => iam.user.confirmPasswordReset(input.token, input.newPassword)).route("auth.logout"
|
|
10
|
+
return implement(iamContract).context().route("auth.login", ({ input }) => iam.auth.login(input)).route("auth.loginWithOtp", ({ input }) => iam.auth.loginWithOtp(input)).route("auth.loginWithLdap", ({ input }) => iam.auth.loginWithLdap(input)).route("auth.publicConfig", () => ok(iam.publicConfig)).route("auth.requestPasswordReset", ({ input }) => iam.user.requestPasswordReset(input.identifier)).route("auth.confirmPasswordReset", ({ input }) => iam.user.confirmPasswordReset(input.token, input.newPassword)).route("auth.logout", ({ input, context }) => {
|
|
11
11
|
const token = input.accessToken ?? context.accessToken;
|
|
12
12
|
if (!token)
|
|
13
13
|
return err(HaiCommonError.UNAUTHORIZED, servM("serv_errorUnauthorized", { locale: context.locale }));
|