@pactor-app/skills 1.33.0 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/pactor-prd/SKILL.md +20 -23
- package/pactor-prd/references/acceptance-checklist.md +1 -1
- package/pactor-prd/references/data-api.md +35 -0
- package/pactor-prd/references/incremental-wireframe-delta.md +8 -9
- package/pactor-prd/references/page-inventory-and-files.md +2 -2
- package/pactor-prd/references/page-templates.md +59 -12
- package/pactor-prd/references/project-structure.md +1 -1
- package/pactor-prd/template/README.md +4 -4
- package/pactor-prd/template/app/api/d/[...parts]/route.ts +80 -0
- package/pactor-prd/template/app/layout.tsx +1 -3
- package/pactor-prd/template/next-env.d.ts +2 -2
- package/pactor-prd/template/next.config.js +2 -2
- package/pactor-prd/template/package.json +4 -4
- package/pactor-prd/template/pnpm-lock.yaml +97 -97
- package/pactor-prd/template/src/pactor/App.tsx +3 -2
- package/pactor-prd/template/src/pactor/actions.ts +5 -431
- package/pactor-prd/template/src/pactor/config.ts +12 -4
- package/pactor-prd/template/src/server/yaml/common/dashboard.yaml +1 -1
- package/pactor-prd/template/src/server/yaml/common/login.yaml +46 -75
- package/pactor-prd/template/src/server/yaml/models/data-list.yaml +344 -0
- package/pactor-prd/template/src/server/yaml/provider/provider-detail.yaml +2 -2
- package/pactor-prd/template/src/server/yaml/provider/provider-edit.yaml +1 -1
- package/pactor-prd/template/src/server/yaml/provider/provider-list.yaml +1 -1
- package/pactor-prd/template/src/server/yaml/route/route-detail.yaml +1 -1
- package/pactor-prd/template/src/server/yaml/route/route-edit.yaml +1 -1
- package/pactor-prd/template/src/server/yaml/route/route-list.yaml +1 -1
- package/pactor-prd/template/src/server/yaml/sitemap.yaml +23 -6
- package/pactor-prd/references/mock-data-routes.md +0 -66
- package/pactor-prd/template/app/api/data/[...parts]/route.ts +0 -118
- package/pactor-prd/template/app/login.css +0 -122
- package/pactor-prd/template/src/pactor/mocks/audit-mock.ts +0 -403
- package/pactor-prd/template/src/pactor/mocks/call-logs-mock.ts +0 -875
- package/pactor-prd/template/src/pactor/mocks/client-debug-mock.ts +0 -188
- package/pactor-prd/template/src/pactor/mocks/client-templates-mock.ts +0 -497
- package/pactor-prd/template/src/pactor/mocks/clients-mock.ts +0 -483
- package/pactor-prd/template/src/pactor/mocks/costs-mock.ts +0 -406
- package/pactor-prd/template/src/pactor/mocks/dashboard-mock.ts +0 -192
- package/pactor-prd/template/src/pactor/mocks/model-debug-mock.ts +0 -144
- package/pactor-prd/template/src/pactor/mocks/models-mock.ts +0 -799
- package/pactor-prd/template/src/pactor/mocks/ops-usage-mock.ts +0 -806
- package/pactor-prd/template/src/pactor/mocks/providers-mock.ts +0 -424
- package/pactor-prd/template/src/pactor/mocks/routes-mock.ts +0 -604
- package/pactor-prd/template/src/pactor/services.ts +0 -185
|
@@ -1,438 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* DSL 表达式不支持数组操作 / 定时器 / 剪贴板,故对话状态变更、流式模拟、
|
|
6
|
-
* 身份联动选项计算收敛在此处;handler 经 ctx.context.state 直接读写页面
|
|
2
|
+
* 页面级自定义动作注册表,经 SitemapApp 的 `actions` prop 全局注册。
|
|
3
|
+
* 模板默认为空;需要 DSL 表达式做不到的能力(数组操作 / 定时器 / 剪贴板等)
|
|
4
|
+
* 时在此注册自定义动作,handler 可经 ctx.context.state 直接读写页面
|
|
7
5
|
* StateStore,写入即触发 DSL 树重渲染(与内置 setState 动作同一通道)。
|
|
8
6
|
*
|
|
9
|
-
* ActionHandler 类型见 @pactor-app/runtime
|
|
10
|
-
* (handler 只需已求值的 action.params 与 state 读写),赋值给 SitemapApp
|
|
11
|
-
* actions 选项时由 TS 结构化检查兼容性(1.22.0 签名:
|
|
12
|
-
* (action: ActionConfig, ctx: ActionContext) => ActionResult | Promise<…>)。
|
|
7
|
+
* ActionHandler 类型见 @pactor-app/runtime。
|
|
13
8
|
*/
|
|
14
9
|
|
|
15
10
|
import type { ActionHandler } from "@pactor-app/runtime";
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
appKeyOptionsOf,
|
|
19
|
-
routeOptionsOf,
|
|
20
|
-
routeTagOptionsOf,
|
|
21
|
-
simulateClientCall,
|
|
22
|
-
type ClientDebugRound,
|
|
23
|
-
} from "./mocks/client-debug-mock";
|
|
24
|
-
import { getProvider } from "./mocks/providers-mock";
|
|
25
|
-
import {
|
|
26
|
-
capabilitiesOf,
|
|
27
|
-
modelOptionsOf,
|
|
28
|
-
paramDefaultsOf,
|
|
29
|
-
simulateModelCall,
|
|
30
|
-
supportsReasoning,
|
|
31
|
-
testConnection,
|
|
32
|
-
} from "./mocks/model-debug-mock";
|
|
33
|
-
import { streamText, type DebugChatMessage } from "./components/debug-chat";
|
|
34
|
-
|
|
35
|
-
/** SitemapApp actions 选项 handler 的结构化最小契约(ActionHandler 子集) */
|
|
36
|
-
export interface DebugActionContext {
|
|
37
|
-
context: {
|
|
38
|
-
state: {
|
|
39
|
-
get: (path: string) => unknown;
|
|
40
|
-
set: (patch: Record<string, unknown>) => void;
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export type DebugAction = (
|
|
46
|
-
action: { params?: Record<string, unknown> },
|
|
47
|
-
ctx: DebugActionContext,
|
|
48
|
-
) => { ok: boolean };
|
|
49
|
-
|
|
50
|
-
function str(value: unknown): string {
|
|
51
|
-
return typeof value === "string" ? value : "";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function num(value: unknown): number | undefined {
|
|
55
|
-
return typeof value === "number" ? value : undefined;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function createClientDebugActions(): Record<string, DebugAction> {
|
|
59
|
-
/** 流式定时器的停止闭包(停止生成 / 清空对话 / 下一次发送前取消) */
|
|
60
|
-
let stopCurrent: (() => void) | null = null;
|
|
61
|
-
|
|
62
|
-
/** 流式收尾:assistant 消息落定 + 轮次写入 selectedRound(右侧返回报文此时可见) */
|
|
63
|
-
const finalize = (
|
|
64
|
-
ctx: DebugActionContext,
|
|
65
|
-
round: ClientDebugRound,
|
|
66
|
-
assistantId: string,
|
|
67
|
-
finalText: string,
|
|
68
|
-
stopped: boolean,
|
|
69
|
-
) => {
|
|
70
|
-
const messages = (
|
|
71
|
-
(ctx.context.state.get("messages") as DebugChatMessage[]) ?? []
|
|
72
|
-
).map((m) =>
|
|
73
|
-
m.id === assistantId
|
|
74
|
-
? {
|
|
75
|
-
...m,
|
|
76
|
-
content: finalText,
|
|
77
|
-
streaming: false,
|
|
78
|
-
meta: `Token ${round.tokensIn}/${round.tokensOut} · TTFT ${round.ttftMs}ms · 延迟 ${round.latencyMs}ms${stopped ? " · 已停止" : ""}`,
|
|
79
|
-
}
|
|
80
|
-
: m,
|
|
81
|
-
);
|
|
82
|
-
ctx.context.state.set({
|
|
83
|
-
messages,
|
|
84
|
-
selectedRound: round,
|
|
85
|
-
selectedRoundId: round.id,
|
|
86
|
-
streaming: false,
|
|
87
|
-
});
|
|
88
|
-
stopCurrent = null;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
/** 切换客户端:联动 AppKey(取该客户端唯一一把)、授权路由与上下文 */
|
|
93
|
-
"clientDebug.selectClient": (action, ctx) => {
|
|
94
|
-
const clientCode = str(action.params?.clientCode);
|
|
95
|
-
const appKeys = appKeyOptionsOf(clientCode);
|
|
96
|
-
ctx.context.state.set({
|
|
97
|
-
clientCode,
|
|
98
|
-
appKeyOptions: appKeys,
|
|
99
|
-
appKeyId: appKeys[0]?.value ?? "",
|
|
100
|
-
routeOptions: routeOptionsOf(clientCode),
|
|
101
|
-
routeCode: "",
|
|
102
|
-
tagOptions: [],
|
|
103
|
-
routeTag: "",
|
|
104
|
-
});
|
|
105
|
-
return { ok: true };
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
/** 切换授权路由:联动 RouteTag 选项 */
|
|
109
|
-
"clientDebug.selectRoute": (action, ctx) => {
|
|
110
|
-
const routeCode = str(action.params?.routeCode);
|
|
111
|
-
ctx.context.state.set({
|
|
112
|
-
routeCode,
|
|
113
|
-
tagOptions: routeTagOptionsOf(routeCode),
|
|
114
|
-
routeTag: "",
|
|
115
|
-
});
|
|
116
|
-
return { ok: true };
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
/** 发送:模拟完整链路调用,流式渲染 assistant 回复 */
|
|
120
|
-
"clientDebug.send": (action, ctx) => {
|
|
121
|
-
const state = ctx.context.state;
|
|
122
|
-
if (state.get("streaming")) return { ok: false };
|
|
123
|
-
const clientCode = str(state.get("clientCode"));
|
|
124
|
-
const appKeyId = str(state.get("appKeyId"));
|
|
125
|
-
const routeCode = str(state.get("routeCode"));
|
|
126
|
-
const text = str(action.params?.text);
|
|
127
|
-
if (!clientCode || !appKeyId || !routeCode || !text.trim()) {
|
|
128
|
-
return { ok: false };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const roundCount = Number(state.get("roundCount") ?? 0);
|
|
132
|
-
const round = simulateClientCall(
|
|
133
|
-
{
|
|
134
|
-
clientCode,
|
|
135
|
-
appKeyId,
|
|
136
|
-
routeCode,
|
|
137
|
-
channel: str(state.get("channel")),
|
|
138
|
-
routeTag: str(state.get("routeTag")),
|
|
139
|
-
user: str(state.get("user")),
|
|
140
|
-
},
|
|
141
|
-
text,
|
|
142
|
-
roundCount + 1,
|
|
143
|
-
);
|
|
144
|
-
const assistantId = `${round.id}-a`;
|
|
145
|
-
const messages: DebugChatMessage[] = [
|
|
146
|
-
...((state.get("messages") as DebugChatMessage[]) ?? []),
|
|
147
|
-
{ id: `${round.id}-u`, role: "user", content: text, round },
|
|
148
|
-
{
|
|
149
|
-
id: assistantId,
|
|
150
|
-
role: "assistant",
|
|
151
|
-
content: "",
|
|
152
|
-
round,
|
|
153
|
-
streaming: true,
|
|
154
|
-
},
|
|
155
|
-
];
|
|
156
|
-
state.set({
|
|
157
|
-
messages,
|
|
158
|
-
streaming: true,
|
|
159
|
-
roundCount: roundCount + 1,
|
|
160
|
-
// 流结束后再写入 selectedRound(PRD:返回报文在流结束后刷新)
|
|
161
|
-
selectedRound: null,
|
|
162
|
-
selectedRoundId: round.id,
|
|
163
|
-
tab: "request",
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
let latest = "";
|
|
167
|
-
const cancel = streamText(
|
|
168
|
-
round.reply,
|
|
169
|
-
(chunk) => {
|
|
170
|
-
latest = chunk;
|
|
171
|
-
const cur =
|
|
172
|
-
(ctx.context.state.get("messages") as DebugChatMessage[]) ?? [];
|
|
173
|
-
ctx.context.state.set({
|
|
174
|
-
messages: cur.map((m) =>
|
|
175
|
-
m.id === assistantId ? { ...m, content: chunk } : m,
|
|
176
|
-
),
|
|
177
|
-
});
|
|
178
|
-
},
|
|
179
|
-
() => finalize(ctx, round, assistantId, round.reply, false),
|
|
180
|
-
);
|
|
181
|
-
stopCurrent = () => {
|
|
182
|
-
cancel();
|
|
183
|
-
finalize(ctx, round, assistantId, latest || round.reply.slice(0, 12), true);
|
|
184
|
-
};
|
|
185
|
-
return { ok: true };
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
/** 停止生成:取消定时器并按当前片段收尾 */
|
|
189
|
-
"clientDebug.stop": (_action, ctx) => {
|
|
190
|
-
stopCurrent?.();
|
|
191
|
-
if (ctx.context.state.get("streaming")) {
|
|
192
|
-
ctx.context.state.set({ streaming: false });
|
|
193
|
-
}
|
|
194
|
-
return { ok: true };
|
|
195
|
-
},
|
|
196
|
-
|
|
197
|
-
/** 清空对话:清空本地会话(不影响已落库调用日志) */
|
|
198
|
-
"clientDebug.clear": (_action, ctx) => {
|
|
199
|
-
stopCurrent?.();
|
|
200
|
-
stopCurrent = null;
|
|
201
|
-
ctx.context.state.set({
|
|
202
|
-
messages: [],
|
|
203
|
-
selectedRound: null,
|
|
204
|
-
selectedRoundId: "",
|
|
205
|
-
roundCount: 0,
|
|
206
|
-
streaming: false,
|
|
207
|
-
});
|
|
208
|
-
return { ok: true };
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
/** 复制报文到剪贴板(DSL 无剪贴板能力,经动作桥接) */
|
|
212
|
-
"clientDebug.copy": (action) => {
|
|
213
|
-
const text = action.params?.text;
|
|
214
|
-
if (typeof text === "string" && text) {
|
|
215
|
-
void navigator.clipboard?.writeText(text);
|
|
216
|
-
}
|
|
217
|
-
return { ok: true };
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export function createModelDebugActions(): Record<string, DebugAction> {
|
|
223
|
-
let stopCurrent: (() => void) | null = null;
|
|
224
|
-
let seq = 0;
|
|
225
|
-
|
|
226
|
-
/** assistant 消息收尾(流式完成 / 停止生成共用) */
|
|
227
|
-
const finalize = (
|
|
228
|
-
ctx: DebugActionContext,
|
|
229
|
-
id: string,
|
|
230
|
-
patch: Partial<DebugChatMessage>,
|
|
231
|
-
) => {
|
|
232
|
-
const messages = (
|
|
233
|
-
(ctx.context.state.get("messages") as DebugChatMessage[]) ?? []
|
|
234
|
-
).map((m) => (m.id === id ? { ...m, ...patch } : m));
|
|
235
|
-
ctx.context.state.set({ messages, streaming: false });
|
|
236
|
-
stopCurrent = null;
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
/** 切换供应商:联动刷新模型列表并清空已选模型与连接状态 */
|
|
241
|
-
"modelDebug.selectProvider": (action, ctx) => {
|
|
242
|
-
const providerCode = str(action.params?.providerCode);
|
|
243
|
-
ctx.context.state.set({
|
|
244
|
-
providerCode,
|
|
245
|
-
modelOptions: modelOptionsOf(providerCode),
|
|
246
|
-
modelCode: "",
|
|
247
|
-
conn: null,
|
|
248
|
-
showReasoning: false,
|
|
249
|
-
canStream: true,
|
|
250
|
-
canTools: false,
|
|
251
|
-
temperature: undefined,
|
|
252
|
-
topP: undefined,
|
|
253
|
-
maxTokens: undefined,
|
|
254
|
-
streamingOn: true,
|
|
255
|
-
toolsOn: false,
|
|
256
|
-
});
|
|
257
|
-
return { ok: true };
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
/** 切换模型:回填默认参数 + 按 Capability 计算参数显隐/开关可用性 */
|
|
261
|
-
"modelDebug.selectModel": (action, ctx) => {
|
|
262
|
-
const modelCode = str(action.params?.modelCode);
|
|
263
|
-
const caps = capabilitiesOf(modelCode);
|
|
264
|
-
const defaults = paramDefaultsOf(modelCode);
|
|
265
|
-
ctx.context.state.set({
|
|
266
|
-
modelCode,
|
|
267
|
-
conn: null,
|
|
268
|
-
temperature: defaults.temperature,
|
|
269
|
-
topP: defaults.topP,
|
|
270
|
-
maxTokens: defaults.maxTokens,
|
|
271
|
-
reasoningEffort: defaults.reasoningEffort ?? "medium",
|
|
272
|
-
showReasoning: supportsReasoning(modelCode),
|
|
273
|
-
canStream: caps.length === 0 || caps.includes("Streaming"),
|
|
274
|
-
canTools: caps.includes("Tool Calling"),
|
|
275
|
-
streamingOn: caps.includes("Streaming"),
|
|
276
|
-
toolsOn: false,
|
|
277
|
-
});
|
|
278
|
-
return { ok: true };
|
|
279
|
-
},
|
|
280
|
-
|
|
281
|
-
/** 连接测试:不发对话,仅探测凭证与 Endpoint 可达性(模拟 600ms 探测延迟) */
|
|
282
|
-
"modelDebug.testConnection": (_action, ctx) => {
|
|
283
|
-
const state = ctx.context.state;
|
|
284
|
-
const providerCode = str(state.get("providerCode"));
|
|
285
|
-
if (!providerCode || !str(state.get("modelCode")) || state.get("testing")) {
|
|
286
|
-
return { ok: false };
|
|
287
|
-
}
|
|
288
|
-
state.set({ testing: true, conn: null });
|
|
289
|
-
setTimeout(() => {
|
|
290
|
-
ctx.context.state.set({
|
|
291
|
-
conn: testConnection(providerCode),
|
|
292
|
-
testing: false,
|
|
293
|
-
});
|
|
294
|
-
}, 600);
|
|
295
|
-
return { ok: true };
|
|
296
|
-
},
|
|
297
|
-
|
|
298
|
-
/** 发送:直连供应商模型(不经过 AppKey / Route 业务链路) */
|
|
299
|
-
"modelDebug.send": (action, ctx) => {
|
|
300
|
-
const state = ctx.context.state;
|
|
301
|
-
if (state.get("streaming")) return { ok: false };
|
|
302
|
-
const providerCode = str(state.get("providerCode"));
|
|
303
|
-
const modelCode = str(state.get("modelCode"));
|
|
304
|
-
const text = str(action.params?.text);
|
|
305
|
-
if (!providerCode || !modelCode || !text.trim()) return { ok: false };
|
|
306
|
-
|
|
307
|
-
seq += 1;
|
|
308
|
-
const msgId = `m-${seq}`;
|
|
309
|
-
const messages = (state.get("messages") as DebugChatMessage[]) ?? [];
|
|
310
|
-
const userMsg: DebugChatMessage = {
|
|
311
|
-
id: `${msgId}-u`,
|
|
312
|
-
role: "user",
|
|
313
|
-
content: text,
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
// 上游/网关失败(凭证未配置):对话区以错误消息展示摘要(PRD 交互规则 3)
|
|
317
|
-
const provider = getProvider(providerCode);
|
|
318
|
-
if (provider && provider.keyStatus !== "configured") {
|
|
319
|
-
state.set({
|
|
320
|
-
messages: [
|
|
321
|
-
...messages,
|
|
322
|
-
userMsg,
|
|
323
|
-
{
|
|
324
|
-
id: `${msgId}-e`,
|
|
325
|
-
role: "error",
|
|
326
|
-
content: `请求失败:Provider「${provider.displayName}」${
|
|
327
|
-
provider.keyStatus === "pending"
|
|
328
|
-
? "API Key 待配置"
|
|
329
|
-
: "未配置 API Key"
|
|
330
|
-
},请先在 Provider 页完成凭证配置。`,
|
|
331
|
-
},
|
|
332
|
-
],
|
|
333
|
-
});
|
|
334
|
-
return { ok: true };
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const streamingOn =
|
|
338
|
-
state.get("streamingOn") === true && state.get("canStream") !== false;
|
|
339
|
-
const result = simulateModelCall(
|
|
340
|
-
providerCode,
|
|
341
|
-
modelCode,
|
|
342
|
-
str(state.get("systemPrompt")),
|
|
343
|
-
{
|
|
344
|
-
temperature: num(state.get("temperature")),
|
|
345
|
-
topP: num(state.get("topP")),
|
|
346
|
-
maxTokens: num(state.get("maxTokens")),
|
|
347
|
-
reasoningEffort:
|
|
348
|
-
state.get("showReasoning") === true
|
|
349
|
-
? str(state.get("reasoningEffort"))
|
|
350
|
-
: undefined,
|
|
351
|
-
streaming: streamingOn,
|
|
352
|
-
tools:
|
|
353
|
-
state.get("toolsOn") === true && state.get("canTools") === true,
|
|
354
|
-
},
|
|
355
|
-
text,
|
|
356
|
-
);
|
|
357
|
-
const meta = `Token ${result.tokensIn}/${result.tokensOut} · TTFT ${result.ttftMs}ms · 延迟 ${result.latencyMs}ms · 上游 ${result.status}`;
|
|
358
|
-
|
|
359
|
-
// 非流式:直接落定完整回复
|
|
360
|
-
if (!streamingOn) {
|
|
361
|
-
state.set({
|
|
362
|
-
messages: [
|
|
363
|
-
...messages,
|
|
364
|
-
userMsg,
|
|
365
|
-
{
|
|
366
|
-
id: msgId,
|
|
367
|
-
role: "assistant",
|
|
368
|
-
content: result.reply,
|
|
369
|
-
meta,
|
|
370
|
-
},
|
|
371
|
-
],
|
|
372
|
-
});
|
|
373
|
-
return { ok: true };
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// 流式:边收边渲染;可停止生成
|
|
377
|
-
state.set({
|
|
378
|
-
messages: [
|
|
379
|
-
...messages,
|
|
380
|
-
userMsg,
|
|
381
|
-
{ id: msgId, role: "assistant", content: "", streaming: true },
|
|
382
|
-
],
|
|
383
|
-
streaming: true,
|
|
384
|
-
});
|
|
385
|
-
let latest = "";
|
|
386
|
-
const cancel = streamText(
|
|
387
|
-
result.reply,
|
|
388
|
-
(chunk) => {
|
|
389
|
-
latest = chunk;
|
|
390
|
-
const cur =
|
|
391
|
-
(ctx.context.state.get("messages") as DebugChatMessage[]) ?? [];
|
|
392
|
-
ctx.context.state.set({
|
|
393
|
-
messages: cur.map((m) =>
|
|
394
|
-
m.id === msgId ? { ...m, content: chunk } : m,
|
|
395
|
-
),
|
|
396
|
-
});
|
|
397
|
-
},
|
|
398
|
-
() => finalize(ctx, msgId, { streaming: false, meta }),
|
|
399
|
-
);
|
|
400
|
-
stopCurrent = () => {
|
|
401
|
-
cancel();
|
|
402
|
-
finalize(ctx, msgId, {
|
|
403
|
-
content: latest || result.reply.slice(0, 12),
|
|
404
|
-
streaming: false,
|
|
405
|
-
meta: `${meta} · 已停止`,
|
|
406
|
-
});
|
|
407
|
-
};
|
|
408
|
-
return { ok: true };
|
|
409
|
-
},
|
|
410
|
-
|
|
411
|
-
/** 停止生成 */
|
|
412
|
-
"modelDebug.stop": (_action, ctx) => {
|
|
413
|
-
stopCurrent?.();
|
|
414
|
-
if (ctx.context.state.get("streaming")) {
|
|
415
|
-
ctx.context.state.set({ streaming: false });
|
|
416
|
-
}
|
|
417
|
-
return { ok: true };
|
|
418
|
-
},
|
|
419
|
-
|
|
420
|
-
/** 清空对话(不影响服务端) */
|
|
421
|
-
"modelDebug.clear": (_action, ctx) => {
|
|
422
|
-
stopCurrent?.();
|
|
423
|
-
stopCurrent = null;
|
|
424
|
-
ctx.context.state.set({ messages: [], streaming: false });
|
|
425
|
-
return { ok: true };
|
|
426
|
-
},
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* SitemapApp 全局动作注册表(clientDebug.* / modelDebug.*)。
|
|
432
|
-
* 单例:两组动作名不相交;同一时刻 PageOutlet 只挂载一个页面,
|
|
433
|
-
* 各页面的流式停止闭包互不干扰。
|
|
434
|
-
*/
|
|
435
|
-
export const pactorActions: Record<string, ActionHandler> = {
|
|
436
|
-
...createClientDebugActions(),
|
|
437
|
-
...createModelDebugActions(),
|
|
438
|
-
};
|
|
12
|
+
export const pactorActions: Record<string, ActionHandler> = {};
|
|
@@ -19,10 +19,18 @@ export const pageEndpoint = (pageId: string) =>
|
|
|
19
19
|
`/api/public/pages/${pageId}`;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* 客户端接口基准地址(SitemapApp baseUrl)。
|
|
23
|
+
* 数据接口全部同源经转发层(app/api/d/[...parts])调用,故为空字符串
|
|
24
|
+
* (相对路径由浏览器按同源解析)。
|
|
25
|
+
*/
|
|
26
|
+
export const apiBaseUrl = "";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 服务端接口基准地址(转发层的目标地址,仅服务端路由使用)。
|
|
23
30
|
* 由 next.config.js 的 API_BASE_URLS 按构建环境(APP_ENV:
|
|
24
31
|
* build / build:dev / build:test → production / development / test)
|
|
25
|
-
*
|
|
32
|
+
* 解析并内联;缺省回退本地开发占位(不存在 → 转发层回退空数据)。
|
|
33
|
+
* 所有需要服务端接口地址的位置统一从这里导入。
|
|
26
34
|
*/
|
|
27
|
-
export const
|
|
28
|
-
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:9011";
|
|
35
|
+
export const serverBaseUrl =
|
|
36
|
+
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:9011/backend";
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
# 登录页 DSL
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
1
|
+
# 登录页 DSL:type: Login 业务组件(packages/ui components/business/Login/),
|
|
2
|
+
# 布局(layout: split / centered / brand-card / overlap / backdrop)与修饰
|
|
3
|
+
# (brandSide / brandRatio / authVariant / brandPanelBackground / decor)正交组合,
|
|
4
|
+
# 品牌区/文案/辅助入口/页脚全部 props 化,样式走语义令牌(深浅色/换肤自适应),
|
|
5
|
+
# 可配置项权威定义见 openspec/specs/ui-login-component 与组件源码。
|
|
6
|
+
# 提交闭环由 DSL 编排:内置 request(POST /api/auth/login 写会话 cookie)
|
|
7
|
+
# + 结果判空 + 失败/成功 message + app.reload 整页重载,sitemap 端点按已登录
|
|
8
|
+
# 会话重新下发。页面使用 sitemap.yaml 的 login 布局(纯 PageOutlet)。
|
|
5
9
|
dsl: "1.0"
|
|
6
10
|
page:
|
|
7
11
|
id: login
|
|
@@ -10,75 +14,42 @@ page:
|
|
|
10
14
|
description: 登录页
|
|
11
15
|
|
|
12
16
|
body:
|
|
13
|
-
type:
|
|
17
|
+
type: Login
|
|
14
18
|
props:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- action: if
|
|
53
|
-
params:
|
|
54
|
-
condition: ${actions.loginResult != null && actions.loginResult.token != null}
|
|
55
|
-
then:
|
|
56
|
-
- action: message
|
|
57
|
-
params:
|
|
58
|
-
messageType: success
|
|
59
|
-
text: 登录成功,正在跳转…
|
|
60
|
-
- action: app.reload
|
|
61
|
-
params: { to: / }
|
|
62
|
-
children:
|
|
63
|
-
- type: Input
|
|
64
|
-
props:
|
|
65
|
-
name: username
|
|
66
|
-
label: 用户名
|
|
67
|
-
placeholder: 请输入用户名
|
|
68
|
-
rules:
|
|
69
|
-
- { required: true, message: 请输入用户名 }
|
|
70
|
-
- type: Input
|
|
71
|
-
props:
|
|
72
|
-
name: password
|
|
73
|
-
label: 密码
|
|
74
|
-
type: password
|
|
75
|
-
placeholder: 请输入密码
|
|
76
|
-
rules:
|
|
77
|
-
- { required: true, message: 请输入密码 }
|
|
78
|
-
- type: Button
|
|
79
|
-
props:
|
|
80
|
-
text: 登 录
|
|
81
|
-
variant: primary
|
|
82
|
-
submit: true
|
|
83
|
-
size: large
|
|
84
|
-
style: { width: 100%, marginTop: 8 }
|
|
19
|
+
layout: split
|
|
20
|
+
authVariant: card
|
|
21
|
+
brand:
|
|
22
|
+
name: Pactor Console
|
|
23
|
+
title: 管理后台登录
|
|
24
|
+
description: 仅已分配平台审核角色的管理人员可以访问。
|
|
25
|
+
title: 登录
|
|
26
|
+
subtitle: 使用管理员账号登录控制台(演示账号 admin / pactor123)
|
|
27
|
+
submitText: 登 录
|
|
28
|
+
events:
|
|
29
|
+
onSubmit:
|
|
30
|
+
- id: loginResult
|
|
31
|
+
action: request
|
|
32
|
+
params:
|
|
33
|
+
method: POST
|
|
34
|
+
url: http://localhost:9011/api/auth/login
|
|
35
|
+
body:
|
|
36
|
+
username: ${event.username}
|
|
37
|
+
password: ${event.password}
|
|
38
|
+
- action: if
|
|
39
|
+
params:
|
|
40
|
+
condition: ${actions.loginResult == null || actions.loginResult.token == null}
|
|
41
|
+
then:
|
|
42
|
+
- action: message
|
|
43
|
+
params:
|
|
44
|
+
messageType: error
|
|
45
|
+
text: 用户名或密码错误(演示账号 admin / pactor123)
|
|
46
|
+
- action: if
|
|
47
|
+
params:
|
|
48
|
+
condition: ${actions.loginResult != null && actions.loginResult.token != null}
|
|
49
|
+
then:
|
|
50
|
+
- action: message
|
|
51
|
+
params:
|
|
52
|
+
messageType: success
|
|
53
|
+
text: 登录成功,正在跳转…
|
|
54
|
+
- action: app.reload
|
|
55
|
+
params: { to: / }
|