@tbox.cn/app-template-agent 0.7.5 → 0.9.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/.gitignore +5 -0
- package/.tbox/app.json +9 -1
- package/AGENTS.md +12 -4
- package/ENV.md +105 -21
- package/README.md +40 -38
- package/apps/client/VISUAL_RESTORE.md +11 -11
- package/apps/client/index.html +3 -1
- package/apps/client/package.json +9 -8
- package/apps/client/src/business/App.tsx +4 -4
- package/apps/client/src/business/README.md +5 -11
- package/apps/client/src/cards/registry.ts +23 -8
- package/apps/client/src/main.tsx +36 -2
- package/apps/client/src/modules.ts +7 -4
- package/apps/client/src/pages/index/index.tsx +18 -162
- package/apps/client/src/platform/AgentChatApp.tsx +7 -6
- package/apps/client/src/platform/agent-runtime.ts +5 -0
- package/apps/client/src/store/chat.ts +47 -185
- package/apps/client/src/test/setup.ts +7 -0
- package/apps/client/vite.config.ts +16 -1
- package/apps/server/package.json +0 -3
- package/apps/server/src/app-tools.ts +2 -1
- package/apps/server/src/app.ts +248 -119
- package/apps/server/src/config/integration.ts +2 -6
- package/apps/server/src/dev.ts +0 -1
- package/apps/server/src/intent-routes.ts +3 -3
- package/apps/server/src/modules.ts +4 -4
- package/apps/server/src/pre-tools.ts +4 -3
- package/apps/server/tests/agent-prompt.test.ts +81 -0
- package/apps/server/tests/cards-registry.test.ts +1 -3
- package/apps/server/tests/deployment-loader.test.ts +35 -56
- package/apps/server/tests/tool-runtime.test.ts +25 -61
- package/package.json +5 -4
- package/packages/agent-ui-kit/LICENSE +21 -0
- package/packages/agent-ui-kit/README.md +17 -0
- package/packages/agent-ui-kit/package.json +58 -0
- package/packages/agent-ui-kit/src/ChatPage.less +62 -0
- package/packages/agent-ui-kit/src/ChatPage.tsx +305 -0
- package/packages/agent-ui-kit/src/chat-config.test.ts +82 -0
- package/packages/agent-ui-kit/src/chat-config.ts +62 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/DebugButton.tsx +2 -2
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.less +19 -16
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.tsx +10 -15
- package/packages/agent-ui-kit/src/components/CardRenderer.tsx +36 -0
- package/packages/agent-ui-kit/src/components/ChatHeader/index.less +148 -0
- package/packages/agent-ui-kit/src/components/ChatHeader/index.test.tsx +74 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatHeader/index.tsx +27 -15
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.less +47 -39
- package/packages/agent-ui-kit/src/components/ChatInput/index.test.tsx +156 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.tsx +13 -4
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/useVoicePress.ts +2 -2
- package/packages/agent-ui-kit/src/components/HistoryEntry/index.less +49 -0
- package/packages/agent-ui-kit/src/components/HistoryEntry/index.tsx +42 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.less +3 -3
- package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.tsx +6 -6
- package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.less +25 -20
- package/packages/agent-ui-kit/src/components/MessageBubble/index.test.tsx +112 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.tsx +22 -17
- package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.less +2 -2
- package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.tsx +2 -2
- package/packages/agent-ui-kit/src/components/QuickCommands/index.less +63 -0
- package/packages/agent-ui-kit/src/components/QuickCommands/index.test.tsx +99 -0
- package/packages/agent-ui-kit/src/components/QuickCommands/index.tsx +52 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.less +1 -1
- package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.test.ts +346 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.ts +204 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.less +312 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.test.tsx +261 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.tsx +212 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/titles.ts +102 -0
- package/packages/agent-ui-kit/src/hooks/use-drag-gesture.test.ts +66 -0
- package/packages/agent-ui-kit/src/hooks/use-drag-gesture.ts +129 -0
- package/packages/agent-ui-kit/src/hooks/use-pull-down-history.ts +241 -0
- package/packages/agent-ui-kit/src/hooks/use-pull-down-wheel-native.test.ts +64 -0
- package/{apps/client → packages/agent-ui-kit}/src/hooks/useMediaUpload.ts +3 -3
- package/{apps/client → packages/agent-ui-kit}/src/hooks/useTTSAutoPlay.ts +7 -5
- package/packages/agent-ui-kit/src/index.ts +69 -0
- package/packages/agent-ui-kit/src/store-context.tsx +92 -0
- package/packages/agent-ui-kit/src/test/setup.ts +7 -0
- package/packages/agent-ui-kit/src/theme/contract.json +72 -0
- package/packages/agent-ui-kit/src/theme/global.less +99 -0
- package/packages/agent-ui-kit/src/theme/motion.less +19 -0
- package/packages/agent-ui-kit/src/theme/theme-contract.test.ts +69 -0
- package/packages/agent-ui-kit/src/theme/theme.ts +17 -0
- package/packages/agent-ui-kit/src/theme/tokens.less +79 -0
- package/packages/agent-ui-kit/src/utils/haptics.test.ts +51 -0
- package/packages/agent-ui-kit/src/utils/haptics.ts +44 -0
- package/packages/agent-ui-kit/src/utils/pull-down-machine.test.ts +188 -0
- package/packages/agent-ui-kit/src/utils/pull-down-machine.ts +157 -0
- package/packages/agent-ui-kit/src/utils/tool-call-display.test.ts +240 -0
- package/packages/agent-ui-kit/src/utils/tool-call-display.ts +146 -0
- package/packages/agent-ui-kit/src/vite-env.d.ts +1 -0
- package/packages/agent-ui-kit/tsconfig.json +8 -0
- package/packages/agent-ui-kit/vitest.config.ts +7 -0
- package/pnpm-lock.template.yaml +177 -111
- package/pnpm-workspace.template.yaml +12 -4
- package/profiles/mall/config/agent.jsonc +33 -0
- package/profiles/mall/config/chat.json +21 -0
- package/profiles/mall/config/knowledge.json +5 -0
- package/profiles/mall/config/llm_provider.json +5 -0
- package/profiles/mall/config/plugins.json +3 -0
- package/profiles/mall.json +16 -0
- package/scripts/check-workspace.mjs +3 -2
- package/apps/client/assets/copy@2x.png +0 -0
- package/apps/client/assets/debug.png +0 -0
- package/apps/client/assets/dislike@2x.png +0 -0
- package/apps/client/assets/like@2x.png +0 -0
- package/apps/client/assets/speaker@2x.png +0 -0
- package/apps/client/src/cards/CardRenderer.tsx +0 -20
- package/apps/client/src/components/ChatHeader/index.less +0 -104
- package/apps/client/src/components/ErrorBoundary.tsx +0 -2
- package/apps/client/src/components/QuickCommands/index.less +0 -48
- package/apps/client/src/components/QuickCommands/index.tsx +0 -29
- package/apps/client/src/components/ToolCallBlock/index.less +0 -131
- package/apps/client/src/components/ToolCallBlock/index.tsx +0 -125
- package/apps/client/src/examples/tourism/TourismLanding.tsx +0 -96
- package/apps/client/src/examples/tourism/index.less +0 -192
- package/apps/client/src/pages/index/index.less +0 -43
- package/apps/client/src/store/chat-config.ts +0 -40
- package/apps/client/src/styles/global.less +0 -60
- package/apps/client/src/styles/tokens.less +0 -60
- package/apps/server/src/config/deployment.ts +0 -47
- package/packages/module-weather/package.json +0 -36
- package/packages/module-weather/skills/weather-guide/SKILL.md +0 -22
- package/packages/module-weather/skills/weather-guide/references/dressing-tips.md +0 -5
- package/packages/module-weather/src/client/cards/weather/index.less +0 -22
- package/packages/module-weather/src/client/cards/weather/index.tsx +0 -14
- package/packages/module-weather/src/client/index.ts +0 -19
- package/packages/module-weather/src/index.ts +0 -2
- package/packages/module-weather/src/server/cards/weather/meta.ts +0 -27
- package/packages/module-weather/src/server/index.ts +0 -45
- package/packages/module-weather/src/server/pre-tools.ts +0 -20
- package/packages/module-weather/src/server/tool.ts +0 -29
- package/packages/module-weather/tbox.module.json +0 -27
- package/packages/module-weather/tsconfig.json +0 -14
- /package/{apps/client → packages/agent-ui-kit}/assets/keyboard-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/more-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/new-chat.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/quick-command-star.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/send-gradient.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/stop@2x.png +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/voice-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoicePressField.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoiceRecordingOverlay.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/machine.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.less +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.tsx +0 -0
- /package/{apps/client/src/types/chat.ts → packages/agent-ui-kit/src/types.ts} +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/copyText.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/imagePreview.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/session-error.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/toast.ts +0 -0
package/apps/server/src/app.ts
CHANGED
|
@@ -4,14 +4,12 @@
|
|
|
4
4
|
* import 本模块即完成装配(与旧单文件入口时序等价:listen 仍在 start() 内最后发生)。
|
|
5
5
|
*/
|
|
6
6
|
import express from 'express';
|
|
7
|
-
import cors from 'cors';
|
|
8
7
|
import { createServer, type IncomingMessage } from 'http';
|
|
9
8
|
import type { Duplex } from 'node:stream';
|
|
10
9
|
import path from 'path';
|
|
11
10
|
import { existsSync } from 'node:fs';
|
|
12
11
|
import { fileURLToPath } from 'url';
|
|
13
12
|
import { registerAll } from './modules.js';
|
|
14
|
-
|
|
15
13
|
// ===== 模板日志(@tbox.cn/app-sdk/server createLogger:本地时间 + LEVEL + [tag] + req/conv 尾缀) =====
|
|
16
14
|
import { createLogger } from '@tbox.cn/app-sdk/server';
|
|
17
15
|
const serverLogger = createLogger('server');
|
|
@@ -24,6 +22,7 @@ import {
|
|
|
24
22
|
createWsServer,
|
|
25
23
|
createServerContext,
|
|
26
24
|
createEngineFromContext,
|
|
25
|
+
createIntegrationsRegistry,
|
|
27
26
|
loadProviderCatalog,
|
|
28
27
|
tboxTraceExporter,
|
|
29
28
|
extractImageUrls,
|
|
@@ -51,26 +50,35 @@ import { installInspector } from '@tbox.cn/app-sdk/platform';
|
|
|
51
50
|
import { registerAppTools } from './app-tools.js';
|
|
52
51
|
import { APP_PRE_TOOLS } from './pre-tools.js';
|
|
53
52
|
import { INTENT_ROUTES, createAppIntentDispatcher } from './intent-routes.js';
|
|
54
|
-
import { deploymentFromIntegrations, buildDefaultMallContext } from './config/deployment.js';
|
|
55
53
|
import { loadIntegrations } from './config/integration.js';
|
|
54
|
+
// W3-1/W4:mall 域语义(部署投影/scope 校验/默认值/instanceIdOf/mall-directory)归 module-mall-core,
|
|
55
|
+
// 经 ctx.domainAssemblies 注册 + 运行期惰性查表——本文件按 TIER1 契约类型(MallScopeAssembly)
|
|
56
|
+
// 泛型断言读取,零 mall-core 包 import(workspace-dag:应用消费模块经注册表 + 契约)
|
|
57
|
+
import { SCOPE_ASSEMBLY_KIND } from '@tbox.cn/app-contracts';
|
|
58
|
+
import type { MallScopeAssembly } from '@app/contracts-mall';
|
|
56
59
|
// 011 S2/S3:mall 装配原语(条件装配——未配置部署文件时零引用)
|
|
57
60
|
import {
|
|
58
61
|
createScopeSessionRegistry,
|
|
59
62
|
createActionGrantService,
|
|
60
63
|
createModuleActionDispatcher,
|
|
61
64
|
createEffectRuntime,
|
|
62
|
-
createIntegrationsRegistry,
|
|
63
65
|
createAuthStrategyRegistry,
|
|
64
66
|
createEnvironmentSecretProvider,
|
|
65
67
|
attachCardActionTokens,
|
|
66
68
|
} from '@tbox.cn/app-sdk/server';
|
|
67
69
|
|
|
68
70
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
+
|
|
72
|
+
// ── 应用级路径唯一锚点区 ──────────────────────────────────────────────
|
|
73
|
+
// entry 即 bundle 实文件:src/dist 平级,dev/prod 恒等。被 tsup 内联的模块禁止
|
|
74
|
+
// 基于自身 import.meta.url 做层级解析(bundle 后层级漂移——config 曾因此解析到
|
|
75
|
+
// 应用根上一级)。同族锚点:../skills、../../client/dist。
|
|
76
|
+
// TBOX_CONFIG_DIR 为 tbox-app dev 的配置面注入锚点(dev 探针常量,guard 守护):
|
|
77
|
+
// 注入时为 real-env 档位目录绝对路径;未注入维持默认相对解析。
|
|
71
78
|
const CONFIG_DIR = process.env.TBOX_CONFIG_DIR
|
|
72
79
|
? path.resolve(process.env.TBOX_CONFIG_DIR)
|
|
73
|
-
: path.resolve(__dirname, '../../../config');
|
|
80
|
+
: path.resolve(__dirname, '../../../config'); // src|dist → server → apps → 应用根
|
|
81
|
+
|
|
74
82
|
const PORT = Number(process.env.PORT) || 8000;
|
|
75
83
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
76
84
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
@@ -81,6 +89,58 @@ const toolDebug = resolveToolDebug(process.env.TBOX_TOOL_DEBUG, process.env.NODE
|
|
|
81
89
|
// 收益依赖 config/llm_provider.json parallelToolCalls 开启;单工具轮行为等价现状
|
|
82
90
|
const earlyToolExecution = resolveEarlyToolExecution(process.env.TBOX_EARLY_TOOL_EXECUTION);
|
|
83
91
|
|
|
92
|
+
// 017:长期记忆(与 base 同源三件套:默认 inmemory;off 逃生舱;knowledge 需 KB_ID)。
|
|
93
|
+
// mall 域措辞段仅装配态拼接(死能力零广告 L1;构造期常量 → 前缀缓存稳定)。
|
|
94
|
+
// 记忆隔离:resolveScope 经域装配 buildScope 单点(mallId 命名空间;W3-1 查表)——
|
|
95
|
+
// 多 mall 单实例下互不可见;未装配控制面/无 mall 上下文 → 用户级全局记忆。
|
|
96
|
+
// 017 M4:KB 校验三态(TBOX_MEMORY_KB_VALIDATE:1=异步降级不阻塞启动 / strict=同步 fail-fast /
|
|
97
|
+
// 未设置不校验)——gated store 统一收口,装配面恒定(INV-D),降级只发生在调用执行时。
|
|
98
|
+
const memoryBase = await resolveMemoryStoreGuarded({
|
|
99
|
+
store: process.env.TBOX_MEMORY_STORE,
|
|
100
|
+
kbId: process.env.TBOX_MEMORY_KB_ID,
|
|
101
|
+
kbDocId: process.env.TBOX_MEMORY_KB_DOC_ID,
|
|
102
|
+
kbBaseUrl: process.env.TBOX_MEMORY_KB_BASE_URL,
|
|
103
|
+
kbCookie: process.env.TBOX_MEMORY_KB_COOKIE,
|
|
104
|
+
validate: process.env.TBOX_MEMORY_KB_VALIDATE,
|
|
105
|
+
validateTimeoutMs: process.env.TBOX_MEMORY_KB_VALIDATE_TIMEOUT_MS,
|
|
106
|
+
});
|
|
107
|
+
// B4:recall 缓存装饰器(同 base 模板——组合序 withRecallCache 包 gated 外层;
|
|
108
|
+
// 单实例双消费共享 wrapped 实例,save 写失效钩子生效)
|
|
109
|
+
const memory = {
|
|
110
|
+
...memoryBase,
|
|
111
|
+
...(memoryBase.store ? { store: withRecallCache(memoryBase.store) } : {}),
|
|
112
|
+
};
|
|
113
|
+
// agent 提示词(config/agent.jsonc;未配置 = SDK 默认兜底;memoryInstructions 装配条件归 SDK,
|
|
114
|
+
// 模板无条件透传——读取一次,改文件需重启生效)
|
|
115
|
+
const AGENT_PROMPT = loadAgentPromptConfig(CONFIG_DIR);
|
|
116
|
+
|
|
117
|
+
// 平台能力装配(003/006 平台轴,已下沉 @tbox.cn/app-sdk/server)
|
|
118
|
+
// 011 L1 + F2/F3 + W3-1:装配序 integrations → authStrategies → auth → catalog → ctx——
|
|
119
|
+
// v4:部署拓扑并入 integrations.json(单文件真源);域投影/校验(deployment 语义)迁
|
|
120
|
+
// module-mall-core(ctx.domainAssemblies 惰性查表——auth 构造先于装配,动态判定经闭包后补)。
|
|
121
|
+
// auth 提前构造(登录换码器由 provider 包 register 期经 ctx.authExchanges.register 自供给;
|
|
122
|
+
// 注册表持引用惰性查找,register 晚于构造仍生效);
|
|
123
|
+
// catalog 无条件装载(integrations 槽位解析唯一真源 = config/integrations.json)
|
|
124
|
+
// 011 B2:可变认证策略注册表——模块 register 期注册(厂商签名策略),transportFactory 同引用
|
|
125
|
+
const authStrategies = createAuthStrategyRegistry();
|
|
126
|
+
const integrationsConfig = loadIntegrations();
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 域装配查表(W3-1):module-mall-core register 期注册;全部消费点运行期惰性读取
|
|
130
|
+
* (auth/registry 构造早于 ctx 与 registerAll——查表时刻 ∈ listen 后请求/会话链路,装配必已就位)。
|
|
131
|
+
* 无装配(无 integrations.json / 模块未装)→ undefined,各消费点按"无控制面"降级(语义对齐迁移前)。
|
|
132
|
+
*/
|
|
133
|
+
function mallAssembly(): MallScopeAssembly | undefined {
|
|
134
|
+
return ctx.domainAssemblies.resolve<MallScopeAssembly>(SCOPE_ASSEMBLY_KIND);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 上下文统一化 Stage 2:validateContext 域校验注入(HTTP body.context 伪冒防线:
|
|
138
|
+
// mallId 形状校验(v4 恒动态)→ 403;只在全局面生效——login 走 login-routes 独立归一化)。
|
|
139
|
+
// 与 WS validateScope 同核(isValidMallShape);无装配 → true(非 mall 部署放行)。
|
|
140
|
+
const auth = createAuthRuntime(undefined, {
|
|
141
|
+
validateContext: (attributes) => mallAssembly()?.validateScope(attributes) ?? true,
|
|
142
|
+
});
|
|
143
|
+
|
|
84
144
|
/** 应用根探测(apps/server cwd → 逐层向上找 .tbox/app.json;dev/bundle 双态收敛) */
|
|
85
145
|
function appRoot(): string {
|
|
86
146
|
let dir = path.resolve(process.cwd());
|
|
@@ -93,45 +153,38 @@ function appRoot(): string {
|
|
|
93
153
|
return path.resolve(process.cwd());
|
|
94
154
|
}
|
|
95
155
|
|
|
96
|
-
const
|
|
156
|
+
const providerCatalog = loadProviderCatalog(appRoot());
|
|
97
157
|
|
|
98
|
-
//
|
|
99
|
-
|
|
158
|
+
// provider 拆包 F2/F3:integrations 槽位解析轴(config/integrations.json 存在即生效;
|
|
159
|
+
// 缺席 → undefined,模块槽解析与换码链 fail-fast 显性暴露——旧 ProviderRegistry 链已拆除)
|
|
160
|
+
// instanceIdOf 注入 mall 语义(W3-1:沿域装配查表——mallId 派生归 module-mall-core)
|
|
161
|
+
const integrations = integrationsConfig
|
|
162
|
+
? createIntegrationsRegistry({
|
|
163
|
+
integrations: integrationsConfig.config,
|
|
164
|
+
// catalog 真源 = 应用根 .tbox/app.json(上方无条件装载)
|
|
165
|
+
catalog: providerCatalog,
|
|
166
|
+
authStrategies,
|
|
167
|
+
secretProvider: createEnvironmentSecretProvider({
|
|
168
|
+
source: process.env as Record<string, string | undefined>,
|
|
169
|
+
// 文件通道(v2):config/credentials/ 凭据文件优先(ref = 文件名 stem 直引),env 兜底。
|
|
170
|
+
files: path.resolve(CONFIG_DIR, 'credentials'),
|
|
171
|
+
allowedHeaderNames: ['content-type', 'sign', 'timestamp', 'publickey', 'x-api-key'],
|
|
172
|
+
production: isProduction,
|
|
173
|
+
}),
|
|
174
|
+
instanceIdOf: (scope) => mallAssembly()?.instanceIdOf(scope) ?? '',
|
|
175
|
+
// F5 观测轴:provider factory 收到的 transport 统一包外部调用日志(→/← path durMs)
|
|
176
|
+
logger: createLogger('provider'),
|
|
177
|
+
})
|
|
178
|
+
: undefined;
|
|
100
179
|
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// body.context 伪冒防线:mallId 形状校验 → 403)与 scopeDefaults 同源。
|
|
106
|
-
const integrationsConfig = loadIntegrations();
|
|
107
|
-
const deployment = integrationsConfig ? deploymentFromIntegrations(integrationsConfig) : undefined;
|
|
108
|
-
const auth = createAuthRuntime(undefined, {
|
|
109
|
-
// validateContext 只在全局面生效(login/dev-login 的 body.context 走 login-routes 独立归一化)
|
|
110
|
-
...(deployment ? { validateContext: deployment.assembly.validateScope } : {}),
|
|
180
|
+
// LLM 装配(模型名单点:configPath 入列 CONFIG_DIR 注入家族——dev 档位/生成应用 config 同锚):
|
|
181
|
+
// resolveModelName 与 getModel 同链解析(config modelName ?? env),记录名与实际请求恒一致。
|
|
182
|
+
const { getModel, resolveModelName } = createModelProvider({
|
|
183
|
+
configPath: path.join(CONFIG_DIR, 'llm_provider.json'),
|
|
111
184
|
});
|
|
112
|
-
const
|
|
113
|
-
const pluginTools = createPluginIntegration();
|
|
185
|
+
const AI_MODEL_NAME = resolveModelName();
|
|
114
186
|
|
|
115
|
-
|
|
116
|
-
// 单实例双消费:engine(recall + 三工具)与 /api/memory 管理面共享同一存储。
|
|
117
|
-
// 017 M4:KB 校验三态(TBOX_MEMORY_KB_VALIDATE:1=异步降级不阻塞启动 / strict=同步 fail-fast /
|
|
118
|
-
// 未设置不校验)——gated store 统一收口,装配面恒定(INV-D),降级只发生在调用执行时。
|
|
119
|
-
const memoryBase = await resolveMemoryStoreGuarded({
|
|
120
|
-
store: process.env.TBOX_MEMORY_STORE,
|
|
121
|
-
kbId: process.env.TBOX_MEMORY_KB_ID,
|
|
122
|
-
kbDocId: process.env.TBOX_MEMORY_KB_DOC_ID,
|
|
123
|
-
kbBaseUrl: process.env.TBOX_MEMORY_KB_BASE_URL,
|
|
124
|
-
kbCookie: process.env.TBOX_MEMORY_KB_COOKIE,
|
|
125
|
-
validate: process.env.TBOX_MEMORY_KB_VALIDATE,
|
|
126
|
-
validateTimeoutMs: process.env.TBOX_MEMORY_KB_VALIDATE_TIMEOUT_MS,
|
|
127
|
-
});
|
|
128
|
-
// B4:recall 缓存装饰器(list 60s TTL + 空结果负缓存短路——零记忆用户 recall <10ms)。
|
|
129
|
-
// 组合序:withRecallCache 包 gated 外层(gate 失败穿透不污染缓存);单实例双消费
|
|
130
|
-
// (engine recall + 三工具 + /api/memory 管理面)共享 wrapped 实例——save 写失效钩子生效。
|
|
131
|
-
const memory = {
|
|
132
|
-
...memoryBase,
|
|
133
|
-
...(memoryBase.store ? { store: withRecallCache(memoryBase.store) } : {}),
|
|
134
|
-
};
|
|
187
|
+
const pluginTools = createPluginIntegration();
|
|
135
188
|
|
|
136
189
|
/** 懒初始化单例 ConversationService */
|
|
137
190
|
let _conversationService: ConversationService | null = null;
|
|
@@ -153,70 +206,67 @@ process.on('uncaughtException', (err) => {
|
|
|
153
206
|
});
|
|
154
207
|
|
|
155
208
|
export const app = express();
|
|
156
|
-
|
|
209
|
+
|
|
210
|
+
// ===== CORS allowlist(统一登录改造 T20:全开 cors() 退役) =====
|
|
211
|
+
// 双轴分离:iframe 嵌入走 CSP frame-ancestors(下方中间件),跨源 fetch 走本 allowlist。
|
|
212
|
+
// 三元组与 frame-ancestors 对齐(用户裁决 2026-09-13);credentials 恒 false(token 走
|
|
213
|
+
// Authorization 头,不依赖 cookie——跨源带凭据面直接关死)。
|
|
214
|
+
// 生成应用可编辑此常量(长尾 origin 逃生通道)——刻意不加 env(决策权归部署面代码)。
|
|
215
|
+
const CORS_ALLOWED_ORIGINS = ['https://tbox.cn', 'https://*.tbox.cn', 'http://localhost:3000'];
|
|
216
|
+
function originAllowed(origin: string): boolean {
|
|
217
|
+
return CORS_ALLOWED_ORIGINS.some((pattern) =>
|
|
218
|
+
pattern.includes('*')
|
|
219
|
+
? origin.endsWith(pattern.slice(pattern.indexOf('*') + 1))
|
|
220
|
+
: origin === pattern,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
app.use((req, res, next) => {
|
|
224
|
+
const origin = req.headers.origin;
|
|
225
|
+
if (typeof origin === 'string' && originAllowed(origin)) {
|
|
226
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
227
|
+
res.setHeader('Vary', 'Origin');
|
|
228
|
+
}
|
|
229
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,DELETE,OPTIONS');
|
|
230
|
+
res.setHeader('Access-Control-Allow-Headers', 'Authorization,Content-Type,X-Request-ID');
|
|
231
|
+
res.setHeader('Access-Control-Allow-Credentials', 'false');
|
|
232
|
+
if (req.method === 'OPTIONS') {
|
|
233
|
+
res.sendStatus(204);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
next();
|
|
237
|
+
});
|
|
238
|
+
|
|
157
239
|
app.use(express.json());
|
|
158
240
|
|
|
159
241
|
// 🔒 平台基础设施 — 禁止 AI 删除或修改(@infrastructure-locked)
|
|
160
242
|
installInspector(app);
|
|
161
243
|
|
|
162
|
-
//
|
|
163
|
-
|
|
244
|
+
// 浏览器登录形态提示:非 prod 裸跑(无 mall 控制面)默认开放 guest 登录(内置换码器
|
|
245
|
+
// allowsGuestLogin 声明);公网实例应以 NODE_ENV=production + 换码器档位收敛(browserLoginMode: off)
|
|
246
|
+
if (!isProduction) {
|
|
164
247
|
authLogger.warn(
|
|
165
|
-
'
|
|
248
|
+
'浏览器登录(browser 轴)按换码器档位开放:裸跑/demo 默认 guest。若本实例暴露公网,请设 NODE_ENV=production 并在登录槽 config 收敛 browserLoginMode。',
|
|
166
249
|
);
|
|
167
250
|
}
|
|
168
251
|
|
|
169
252
|
// 自注册闭环:模块路由挂载到 /api/m/<moduleId>/(在 apiRouter 之前,避免被 /api 兜底 404 截获)
|
|
170
253
|
// ctx 为模块级单例:registerAll 在 start() 内执行,ApiRouteRegistry 共享 root 保证延迟注册即时生效
|
|
171
|
-
// 011 L1 + F2/F3
|
|
172
|
-
// provider
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
const scopeDefaults = deployment?.controlPlane.defaultMallId
|
|
176
|
-
? { defaultMallId: deployment.controlPlane.defaultMallId }
|
|
177
|
-
: undefined;
|
|
178
|
-
// 011 B2:可变认证策略注册表——模块 register 期注册(厂商签名策略),transportFactory 同引用
|
|
179
|
-
const authStrategies = createAuthStrategyRegistry();
|
|
180
|
-
|
|
181
|
-
// provider 拆包 F2/F3:integrations 槽位解析轴(config/integrations.json 存在即生效;
|
|
182
|
-
// 缺席 → undefined,模块槽解析与换码链 fail-fast 显性暴露)
|
|
183
|
-
// instanceIdOf 注入 mall 语义(SDK 零领域铁律——mallId 派生归模板层)
|
|
184
|
-
// root config.miniAppId(016 默认商圈兜底拼装用;与 mall 模板同构——env 注入形态无参考表可查)
|
|
185
|
-
const rootMiniAppId =
|
|
186
|
-
typeof integrationsConfig?.config.config?.miniAppId === 'string' && integrationsConfig.config.config.miniAppId
|
|
187
|
-
? integrationsConfig.config.config.miniAppId
|
|
188
|
-
: undefined;
|
|
189
|
-
const integrations = integrationsConfig
|
|
190
|
-
? createIntegrationsRegistry({
|
|
191
|
-
integrations: integrationsConfig.config,
|
|
192
|
-
// catalog 真源 = 应用根 .tbox/app.json(上方无条件装载)
|
|
193
|
-
catalog: providerCatalog,
|
|
194
|
-
authStrategies,
|
|
195
|
-
secretProvider: createEnvironmentSecretProvider({
|
|
196
|
-
source: process.env as Record<string, string | undefined>,
|
|
197
|
-
// 文件通道(v2):config/credentials/ 凭据文件优先(ref = 文件名 stem 直引),env 兜底。
|
|
198
|
-
// dev 态 TBOX_CONFIG_DIR 指档位目录 → files 指档位凭据目录;PR-2 前目录缺席零行为。
|
|
199
|
-
...(process.env.TBOX_CONFIG_DIR
|
|
200
|
-
? { files: path.resolve(process.env.TBOX_CONFIG_DIR, 'credentials') }
|
|
201
|
-
: { files: path.resolve(__dirname, '../../../config/credentials') }),
|
|
202
|
-
allowedHeaderNames: ['content-type', 'sign', 'timestamp', 'publickey', 'x-api-key'],
|
|
203
|
-
production: isProduction,
|
|
204
|
-
}),
|
|
205
|
-
instanceIdOf: (scope) => scope.mallId ?? '',
|
|
206
|
-
// F5 观测轴:provider factory 收到的 transport 统一包外部调用日志(→/← path durMs)
|
|
207
|
-
logger: createLogger('provider'),
|
|
208
|
-
})
|
|
209
|
-
: undefined;
|
|
210
|
-
|
|
254
|
+
// 011 L1 + F2/F3:条件 mall 装配——integrations 槽位轴在上方装配段创建;
|
|
255
|
+
// F2:authExchanges 同引用传递(provider 包 register 期自注册换码器)+ scopeDefaults 中立注入
|
|
256
|
+
// W3-1:scopeDefaults 静态注入退役——域装配(含 defaultMallId)经 registry 读取视图供给
|
|
257
|
+
// (SDK ctx.scopeDefaults 惰性优先注册表 → provider getter 登录期求值)
|
|
211
258
|
const ctx = createServerContext({
|
|
212
259
|
authStrategies,
|
|
213
260
|
authExchanges: auth.exchanges,
|
|
214
261
|
...(memory.store ? { memory: memory.store } : {}),
|
|
215
262
|
...(integrations ? { integrations } : {}),
|
|
216
|
-
...(scopeDefaults ? { scopeDefaults } : {}),
|
|
217
263
|
});
|
|
218
264
|
|
|
219
|
-
//
|
|
265
|
+
// real-env 统一配置链:app-config 服务(模块资源消费入口)——由 registerServerModules
|
|
266
|
+
// 自动装配(W1-④:raw 派生 modules 资源节 + 源目录台账锚;无条件注册、与控制面正交,
|
|
267
|
+
// 防无控制面启动静默丢失)。模板显式注册已拆(W3-1:装配默认兜底覆盖同语义)。
|
|
268
|
+
|
|
269
|
+
// 全局 /api 鉴权(H2 挂载顺序契约):除 public(login/health)外一律要求 Bearer token;
|
|
220
270
|
// optional 模式缺失 token → 匿名身份(写通道仍被 action gateway 拒绝)
|
|
221
271
|
// ⚠️ 必须先于 /api/m、/api/action、createPlatformApiRouter 挂载,保证全 API 面受鉴权约束
|
|
222
272
|
app.use('/api', auth.middleware);
|
|
@@ -227,12 +277,37 @@ app.use('/api/m', ctx.routes.mount());
|
|
|
227
277
|
// 006 D11:匿名写拒绝,不随 AUTH_MODE 变)
|
|
228
278
|
app.use('/api/action', ctx.actions.middleware());
|
|
229
279
|
|
|
230
|
-
//
|
|
280
|
+
// ===== GET /assets/deploy/:file — 部署态静态资产(.real-env/assets;厂商物料不进中立模板) =====
|
|
281
|
+
// ⚠️ 必须在 createPlatformApiRouter 与 vite 中间件之前注册。文件名白名单防目录穿越。
|
|
282
|
+
const deploymentAssetsDir =
|
|
283
|
+
process.env.DEPLOYMENT_ASSETS_DIR ?? path.resolve(appRoot(), '../../.real-env/assets');
|
|
284
|
+
app.get('/assets/deploy/:file', (req, res) => {
|
|
285
|
+
const file = typeof req.params.file === 'string' ? req.params.file : '';
|
|
286
|
+
if (!/^[A-Za-z0-9._-]+$/.test(file)) {
|
|
287
|
+
res.status(400).end();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
res.sendFile(path.resolve(deploymentAssetsDir, file), (err) => {
|
|
291
|
+
if (err) res.status(404).end();
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// API 路由(平台路由面:auth/health/tbox/conversation [+memory] / 404 兜底;卡片恢复内部化)
|
|
231
296
|
// 017:装配记忆时挂载 /api/memory 管理面(与引擎同源 store)
|
|
232
297
|
app.use('/api', createPlatformApiRouter({ ctx, auth, ...(memory.store ? { memory: memory.store } : {}) }));
|
|
233
298
|
|
|
234
299
|
// tbox dev 工具族(/_tbox:卡片预览 API + 页面族;dev-only 自门控——NODE_ENV=production
|
|
235
300
|
// 零挂载;import 期注册先于 dev.ts 运行期 vite middleware,路由序恒胜)
|
|
301
|
+
// iframe 嵌入收紧(AI 构建面板 card-preview 预览):现状无该头 = 隐式全允许 → 显式三元组
|
|
302
|
+
// (用户裁决 2026-09-13:不存在其他 origin 嵌入既有 demo,行为收紧确认;'self' + 生产域 + 本地面板 dev 端口)
|
|
303
|
+
app.use((_req, res, next) => {
|
|
304
|
+
res.setHeader(
|
|
305
|
+
'Content-Security-Policy',
|
|
306
|
+
"frame-ancestors 'self' https://tbox.cn https://*.tbox.cn http://localhost:3000",
|
|
307
|
+
);
|
|
308
|
+
next();
|
|
309
|
+
});
|
|
310
|
+
|
|
236
311
|
installTboxDev(app, { ctx, auth });
|
|
237
312
|
|
|
238
313
|
// 从 ServerContext 构造 ChatEngine(自注册闭环组装端)
|
|
@@ -247,10 +322,15 @@ async function createEngine(
|
|
|
247
322
|
|
|
248
323
|
const engine = await createEngineFromContext(ctx, {
|
|
249
324
|
instructions: AGENT_PROMPT.instructions,
|
|
325
|
+
...(AGENT_PROMPT.memoryInstructions ? { memoryInstructions: AGENT_PROMPT.memoryInstructions } : {}),
|
|
250
326
|
model: getModel(AI_MODEL_NAME),
|
|
251
327
|
traceExporter: tboxTraceExporter,
|
|
252
328
|
modelName: AI_MODEL_NAME,
|
|
253
329
|
fileUrlPrefix: TBOX_FILE_URL_PREFIX,
|
|
330
|
+
// 018 tool-debug:工具调试视图(memory/silent preTool 前端表达;缺省关)
|
|
331
|
+
toolDebug,
|
|
332
|
+
// PR-2 尽早工具执行(缺省 off;env TBOX_EARLY_TOOL_EXECUTION=1 开启)
|
|
333
|
+
earlyToolExecution,
|
|
254
334
|
// 009:双模式全局配置(accelerated 默认:本地路由命中 → reminder 注入当前轮,零额外 LLM 轮次;
|
|
255
335
|
// progressive:全量注册 InlineSkill,模型渐进 skill_read)
|
|
256
336
|
skillMode,
|
|
@@ -258,12 +338,19 @@ async function createEngine(
|
|
|
258
338
|
// 010:前置工具加速(应用级声明 + 单工具预算;TBOX_PRE_TOOL_TIMEOUT_MS=0 整体禁用)
|
|
259
339
|
preTools: APP_PRE_TOOLS,
|
|
260
340
|
preToolTimeoutMs,
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
341
|
+
// 017:长期记忆(默认 inmemory;mall 装配时按 mallId 隔离——scope 经 deployment.assembly
|
|
342
|
+
// buildScope 单点,与 scope 会话/授权签发同源;无 mall 上下文 → 用户级全局记忆)
|
|
343
|
+
...(memory.enabled && memory.store
|
|
344
|
+
? {
|
|
345
|
+
memory: {
|
|
346
|
+
store: memory.store,
|
|
347
|
+
resolveScope: (attributes: Record<string, unknown>) => {
|
|
348
|
+
const scope = mallAssembly()?.buildScope(attributes);
|
|
349
|
+
return scope?.mallId ? { mallId: scope.mallId } : undefined;
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
}
|
|
353
|
+
: {}),
|
|
267
354
|
// 011 B3:卡片动作 token 签发器(mall 装配时注入)
|
|
268
355
|
cardActionAttacher: extra?.cardActionAttacher,
|
|
269
356
|
// B21:意图路由接线(业务意图表 + handlerDispatch)
|
|
@@ -326,24 +413,31 @@ export function registerUpgradeDelegate(delegate: UpgradeDelegate): void {
|
|
|
326
413
|
}
|
|
327
414
|
|
|
328
415
|
export async function start() {
|
|
329
|
-
//
|
|
330
|
-
if (deployment && isProduction && !process.env.ACTION_TOKEN_SECRET && !process.env.AUTH_TOKEN_SECRET) {
|
|
331
|
-
actionLogger.warn('生产环境未配置 ACTION_TOKEN_SECRET/AUTH_TOKEN_SECRET——卡片动作授权装配将 fail-fast 拒绝启动');
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// 注册业务模块(自注册闭环)
|
|
416
|
+
// 注册业务模块(自注册闭环)——mall-directory 服务由 module-mall-core register 期注入(W3-1)
|
|
335
417
|
await registerAll(ctx);
|
|
336
418
|
|
|
337
419
|
// 模板演示工具(apps 级工具,P3 建议迁入模块):不参与模块体系,经统一注册面注册
|
|
338
420
|
// (010:start() 与 cli/collect-registry.ts 双处同调,防注册面失同步)
|
|
339
421
|
registerAppTools(ctx);
|
|
340
422
|
|
|
423
|
+
// W3-1 时序契约:域装配解析必须晚于 registerAll——module-mall-core 在 register 期经
|
|
424
|
+
// ctx.domainAssemblies 注册装配;解析点置于 start() 头部会查空表(W4 引入回归:
|
|
425
|
+
// 默认商圈兜底/卡片动作授权/enhanceContext 全断——见 Agent Note 2026-09-17-mall-assembly-resolution-order)
|
|
426
|
+
const assembly = mallAssembly();
|
|
427
|
+
|
|
428
|
+
// 011 S6:mall 装配 + 生产环境缺动作授权密钥 → 启动期提示(≥32B TBOX_API_KEY 在场回退 apiKey 档;整链空装配期 fail-fast)
|
|
429
|
+
if (assembly && isProduction && !process.env.ACTION_TOKEN_SECRET && !process.env.AUTH_TOKEN_SECRET) {
|
|
430
|
+
actionLogger.warn('生产环境未配置 ACTION_TOKEN_SECRET/AUTH_TOKEN_SECRET——TBOX_API_KEY(≥32B) 缺席时装配将 fail-fast 拒绝启动;在场则回退 apiKey 档(平台运维可伪 action token)');
|
|
431
|
+
}
|
|
432
|
+
|
|
341
433
|
// 011 F2/E2:mall 装配(条件)——scope 会话 + 动作授权先行(engine attacher 依赖)
|
|
342
|
-
|
|
434
|
+
// 014 T1:buildScope 回填 mallName(ChatHeader 品牌区服务端真源;三级回退内聚于域装配)
|
|
435
|
+
// W3-1:域装配经查表(assembly 于上方 registerAll 后解析——解析点必须晚于模块注册)
|
|
436
|
+
const scopeSessions: ReturnType<typeof createScopeSessionRegistry> | undefined = assembly
|
|
343
437
|
? createScopeSessionRegistry({
|
|
344
|
-
extractScopeKey:
|
|
345
|
-
validateScope:
|
|
346
|
-
buildScope:
|
|
438
|
+
extractScopeKey: (c) => assembly.extractScopeKey(c),
|
|
439
|
+
validateScope: (a) => assembly.validateScope(a),
|
|
440
|
+
buildScope: (a) => assembly.buildScopeWithMallName(a),
|
|
347
441
|
})
|
|
348
442
|
: undefined;
|
|
349
443
|
const grants = scopeSessions ? createActionGrantService({ store: ctx.state }) : undefined;
|
|
@@ -369,7 +463,7 @@ export async function start() {
|
|
|
369
463
|
|
|
370
464
|
// 011 F2/E2:模块动作分发(依赖 scopeSessions/grants)
|
|
371
465
|
let moduleActions: ReturnType<typeof createModuleActionDispatcher> | undefined;
|
|
372
|
-
if (
|
|
466
|
+
if (assembly && scopeSessions && grants) {
|
|
373
467
|
moduleActions = createModuleActionDispatcher({
|
|
374
468
|
grants,
|
|
375
469
|
sessions: scopeSessions,
|
|
@@ -379,32 +473,48 @@ export async function start() {
|
|
|
379
473
|
// 015 P3:effect 运行时纯投影(payment/authorization 回调补签在 dispatcher step7,
|
|
380
474
|
// 客户端执行在 SDK host-effect-runner——模板零装配效果链)
|
|
381
475
|
effects: createEffectRuntime(),
|
|
382
|
-
extractScopeKey:
|
|
476
|
+
extractScopeKey: (c) => assembly.extractScopeKey(c),
|
|
383
477
|
});
|
|
384
478
|
for (const action of ctx.cardActions.getAll().values()) {
|
|
385
479
|
moduleActions.register(action);
|
|
386
480
|
}
|
|
387
481
|
}
|
|
388
482
|
|
|
389
|
-
// chat.json
|
|
390
|
-
// 配置单一真源 + settings 运行时下发;chatConfig 无条件传递,非 scope 部署亦发 ack)
|
|
483
|
+
// chat.json 单次装载:chatConfig 下发与 /ws/tts 通道门同值同源(改文件需重启进程,新连接生效)
|
|
391
484
|
const loadedChatConfig = loadChatConfig(CONFIG_DIR);
|
|
485
|
+
const defaultContext = assembly?.defaultMallContext();
|
|
392
486
|
const chatWss = createWsServer(engine, {
|
|
393
487
|
auth: auth.wsAuth,
|
|
394
|
-
//
|
|
395
|
-
|
|
488
|
+
// 会话 β:会话建立服务(懒代理——TBOX_API_KEY 缺席时 NEW/RESUME 失败 notice,RUN 恒严格拒绝)
|
|
489
|
+
conversationService: {
|
|
490
|
+
createConversation: (agentId, userId) => {
|
|
491
|
+
const service = getConversationService();
|
|
492
|
+
if (!service) return Promise.reject(new Error('TBOX_API_KEY not configured'));
|
|
493
|
+
return service.createConversation(agentId, userId);
|
|
494
|
+
},
|
|
495
|
+
getOrCreateConversation: (agentId, userId) => {
|
|
496
|
+
const service = getConversationService();
|
|
497
|
+
if (!service) return Promise.reject(new Error('TBOX_API_KEY not configured'));
|
|
498
|
+
return service.getOrCreateConversation(agentId, userId);
|
|
499
|
+
},
|
|
500
|
+
},
|
|
396
501
|
...(scopeSessions
|
|
397
502
|
? {
|
|
398
503
|
scopeSessions,
|
|
399
504
|
...(moduleActions ? { moduleActions } : {}),
|
|
400
|
-
// 016:默认商圈兜底——无 URL 参数访问时服务端补 HELLO context
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
505
|
+
// 016:默认商圈兜底——无 URL 参数访问时服务端补 HELLO context(显式参数优先)
|
|
506
|
+
// W3-1:defaultContext/enhanceContext 双钩内聚于域装配(mall-core;fail-soft 契约不变:
|
|
507
|
+
// 增强是增益不是闸门——抛错/超时原 context 继续)
|
|
508
|
+
...(defaultContext ? { defaultContext } : {}),
|
|
509
|
+
...(assembly ? { enhanceContext: (c) => assembly.enhanceHelloContext(c) } : {}),
|
|
405
510
|
}
|
|
406
511
|
: {}),
|
|
512
|
+
// real-env 统一配置链:chat.json → HELLO chatConfig(query→prompt 映射 + settings 五键,
|
|
513
|
+
// 剥离扩展字段;映射真源已下沉 SDK server)
|
|
514
|
+
// 无条件传递(chat 配置与控制面正交;loadChatConfig 缺文件/坏格式 fail-soft 回默认)
|
|
407
515
|
chatConfig: toWsChatConfig(loadedChatConfig),
|
|
516
|
+
// 018 tool-debug:HELLO_ACK 旗标(仅开启时携带;与 engine.toolDebug 同值同源)
|
|
517
|
+
toolDebug,
|
|
408
518
|
});
|
|
409
519
|
const ttsWss = createTtsProxy();
|
|
410
520
|
|
|
@@ -419,7 +529,7 @@ export async function start() {
|
|
|
419
529
|
process.exit(1);
|
|
420
530
|
});
|
|
421
531
|
|
|
422
|
-
httpServer.on('upgrade', (request, socket, head) => {
|
|
532
|
+
httpServer.on('upgrade', async (request, socket, head) => {
|
|
423
533
|
socket.on('error', (e) => upgradeLogger.warn('socket error:', e.message));
|
|
424
534
|
if (upgradeDelegate?.(request, socket, head)) return;
|
|
425
535
|
const { pathname } = new URL(request.url || '/', 'http://localhost');
|
|
@@ -429,8 +539,27 @@ export async function start() {
|
|
|
429
539
|
chatWss.emit('connection', ws, request);
|
|
430
540
|
});
|
|
431
541
|
} else if (pathname === '/ws/tts') {
|
|
432
|
-
//
|
|
433
|
-
//
|
|
542
|
+
// 双层门 1(S5 修复,pre-101 鉴权):token 校验先行——TTS 代理原为全开放面。
|
|
543
|
+
// 双形态载体(IP-2):Node 客户端 = Authorization Bearer;浏览器 WebSocket 无法
|
|
544
|
+
// 自定义 header = ?token= query。无效/缺失 token 于 upgrade 前 destroy
|
|
545
|
+
// (对齐 Terminal channel 先例)。
|
|
546
|
+
const headerToken = (request.headers.authorization ?? '').replace(/^Bearer\s+/i, '');
|
|
547
|
+
const queryToken = new URL(request.url || '/', 'http://localhost').searchParams.get('token') ?? '';
|
|
548
|
+
const ttsToken = headerToken || queryToken;
|
|
549
|
+
if (!ttsToken) {
|
|
550
|
+
upgradeLogger.warn('TTS upgrade rejected: missing token');
|
|
551
|
+
socket.destroy();
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
await auth.tokenService.verify(ttsToken);
|
|
556
|
+
} catch {
|
|
557
|
+
upgradeLogger.warn('TTS upgrade rejected: invalid token');
|
|
558
|
+
socket.destroy();
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
// 双层门 2(通道门与 UI 同值同源):chat.json enableTTS 单源——关态硬拒 upgrade,
|
|
562
|
+
// 防服务端开着 TTS 代理但 UI 已隐藏按钮的僵尸路径
|
|
434
563
|
if (!loadedChatConfig.enableTTS) {
|
|
435
564
|
upgradeLogger.warn('TTS disabled (chat.json enableTTS:false) — /ws/tts upgrade rejected');
|
|
436
565
|
socket.destroy();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 旧 `config/deployments/development.json` 已退役,stale 防线在装载链 fail-fast
|
|
7
7
|
* (mall 家族形态判定:含 malls 键;support 独立体系不受影响)。
|
|
8
8
|
* - zod 单源 = SDK parseIntegrationsConfig(断言 → strict 校验;拼错键启动即报);
|
|
9
|
-
*
|
|
9
|
+
* 控制平面投影归 module-mall-core(W3-1:消费 ctx.integrations.raw 自持计算)。
|
|
10
10
|
* - 本壳只保留 I/O:占位展开(real-env 统一配置链——缺变量 fail-fast,错误含文件与
|
|
11
11
|
* 缺失清单)+ DEFAULT_PATH 双态锚点探测(打包形态私有知识,不入 SDK)。
|
|
12
12
|
* - 与 base 模板同构(双生无同步机制——公共能力先行落位,漂移由生成应用 verify 守护)。
|
|
@@ -16,8 +16,6 @@ import path from 'node:path';
|
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { expandEnvVars, parseIntegrationsConfig } from '@tbox.cn/app-sdk/server';
|
|
18
18
|
import type { IntegrationsConfig } from '@tbox.cn/app-contracts';
|
|
19
|
-
import { mallControlPlaneOf } from '@app/contracts-mall/server';
|
|
20
|
-
import type { MallDeploymentControlPlane } from '@app/contracts-mall';
|
|
21
19
|
|
|
22
20
|
// 默认路径 = 应用根 config/integrations.json。双态锚点探测(bundle 打平后 import.meta.url 层级漂移):
|
|
23
21
|
// dev 态本文件在 src/config/(四层到根);tsup bundle 态打平进 dist/(三层到根)。
|
|
@@ -37,8 +35,6 @@ const DEFAULT_PATH = (() => {
|
|
|
37
35
|
export interface LoadedIntegrations {
|
|
38
36
|
config: IntegrationsConfig;
|
|
39
37
|
source: string;
|
|
40
|
-
/** mall 控制平面投影(恒在场——无参考表 = 空平面;装配链零拓扑分支) */
|
|
41
|
-
controlPlane: MallDeploymentControlPlane;
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
/**
|
|
@@ -77,5 +73,5 @@ export function loadIntegrations(envPath?: string): LoadedIntegrations | undefin
|
|
|
77
73
|
// 顺序固定:展开(占位符)→ 断言 → zod(后两步在 parseIntegrationsConfig 内,防线单点)
|
|
78
74
|
const raw = JSON.parse(expandEnvVars(readFileSync(p, 'utf8'), process.env, p));
|
|
79
75
|
const config = parseIntegrationsConfig(raw);
|
|
80
|
-
return { config, source: p
|
|
76
|
+
return { config, source: p };
|
|
81
77
|
}
|
package/apps/server/src/dev.ts
CHANGED
|
@@ -5,9 +5,9 @@ import type { IntentRoute, HandlerResult, ServerContext } from '@tbox.cn/app-sdk
|
|
|
5
5
|
* chat() 先 matchIntent(统一检索层 BM25 竞争:长词 OR ≥2 token 规则,score 降序 + id 字典序);
|
|
6
6
|
* handler 缺省直接出卡/文字,也可用 continueWith 续接完整 Agent。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* W3-3(单一模板统一化):停车缴费硬路由迁 module-parking handler `intents` 声明
|
|
9
|
+
* (意图与处理器同处定义,011 D7;collectIntentRoutes 装配端自动汇集)——
|
|
10
|
+
* 本表归零,业务方在此声明应用级意图路由(handlerId 对应已装模块的 handler id)。
|
|
11
11
|
*/
|
|
12
12
|
export const INTENT_ROUTES: IntentRoute[] = [];
|
|
13
13
|
|
|
@@ -10,16 +10,16 @@ import type { ServerContext, InferModuleCards } from '@tbox.cn/app-sdk/server';
|
|
|
10
10
|
// @tbox:imports-begin
|
|
11
11
|
// SDK 装配框架(非模块行,CLI 增量维护保留非标行)
|
|
12
12
|
import { registerServerModules } from '@tbox.cn/app-sdk/server';
|
|
13
|
-
import { serverModule as moduleWeather } from '@app/module-weather/server';
|
|
14
13
|
import { serverModule as providerCustom } from '@app/provider-custom/server';
|
|
15
14
|
// @tbox:imports-end
|
|
16
15
|
|
|
17
16
|
// @tbox:register-begin
|
|
18
|
-
//
|
|
17
|
+
// W4 单一模板统一化:缺省出厂 = 空装配(仅 provider-custom 自定义 API 落点 + contracts-mall shim)——
|
|
18
|
+
// mall 能力(mall-core/mall-home + 业务模块)经 profiles/mall 加装(tbox-app create --profile mall,W5 接线)
|
|
19
|
+
// footer `] as const` 为空数组类型锚(CLI 增删模块时保留变体)
|
|
19
20
|
const modules = [
|
|
20
|
-
moduleWeather,
|
|
21
21
|
providerCustom,
|
|
22
|
-
];
|
|
22
|
+
] as const;
|
|
23
23
|
// @tbox:register-end
|
|
24
24
|
|
|
25
25
|
// 自由区:可做装配级定制——条件注册、顺序调整、包裹中间件
|
|
@@ -3,7 +3,7 @@ import { definePreTool } from '@tbox.cn/app-sdk/server';
|
|
|
3
3
|
import { clockTool } from './tools/clock.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* 应用级前置工具声明(010):BuildEngineOptions.preTools
|
|
6
|
+
* 应用级前置工具声明(010):BuildEngineOptions.preTools 通道(与模块声明合并、应用覆盖模块)。
|
|
7
7
|
*
|
|
8
8
|
* 声明形态(详见 README「前置 Tool 加速执行」节):
|
|
9
9
|
* ① tool —— 工具实例引用(单一真源,id/description 派生)
|
|
@@ -11,8 +11,9 @@ import { clockTool } from './tools/clock.js';
|
|
|
11
11
|
* 或 { mode: 'always' } 必选候选(decide 门控;慎用防每轮噪音)
|
|
12
12
|
* ③ decide —— 决策即构造:{ decision: 'run', args } = 预执行 / { decision: 'skip' } = 不生效
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* getCurrentTime —— 零参工程化演示(keywords 词表命中即执行,decide 恒 run 空参)。
|
|
15
|
+
* 词表刻意排除「今天/时间」等泛词,防止与业务模块 preTool(如 member 的 always 预执行)语义叠加误触发。
|
|
16
|
+
* W3-1:mall-info preTool 迁 module-mall-core(模块声明式 preTools 通道)。
|
|
16
17
|
*/
|
|
17
18
|
export const APP_PRE_TOOLS = [
|
|
18
19
|
definePreTool(clockTool, {
|