@gt-fe/eap-cli 0.2.11 → 0.2.12

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.
Files changed (75) hide show
  1. package/README.md +304 -304
  2. package/dist/static/playground/index.html +131 -131
  3. package/dist/static/playground/playground.css +209 -209
  4. package/dist/static/playground/playground.js +151 -151
  5. package/dist/static/playground-app/index.html +11 -11
  6. package/dist/templates/README.md +51 -51
  7. package/dist/templates/agent-api-orchestration/.env +16 -16
  8. package/dist/templates/agent-api-orchestration/README.md +326 -326
  9. package/dist/templates/agent-api-orchestration/agent.manifest.json +19 -19
  10. package/dist/templates/agent-api-orchestration/eap.config.json +1 -1
  11. package/dist/templates/agent-api-orchestration/eap.lock.yaml +5 -5
  12. package/dist/templates/agent-api-orchestration/gitignore +3 -3
  13. package/dist/templates/agent-api-orchestration/graph.json +35 -35
  14. package/dist/templates/agent-api-orchestration/package.json +1 -1
  15. package/dist/templates/agent-api-orchestration/scripts/api-test.mjs +443 -443
  16. package/dist/templates/agent-api-orchestration/scripts/build-runtime.mjs +26 -26
  17. package/dist/templates/agent-api-orchestration/scripts/validate.mjs +12 -12
  18. package/dist/templates/agent-api-orchestration/src/embedded-agent.ts +91 -91
  19. package/dist/templates/agent-api-orchestration/src/http/app.ts +58 -58
  20. package/dist/templates/agent-api-orchestration/src/http/config.ts +34 -34
  21. package/dist/templates/agent-api-orchestration/src/http/errors.ts +63 -63
  22. package/dist/templates/agent-api-orchestration/src/http/index.ts +4 -4
  23. package/dist/templates/agent-api-orchestration/src/http/middleware/cors.ts +17 -17
  24. package/dist/templates/agent-api-orchestration/src/http/routes/agent.ts +33 -33
  25. package/dist/templates/agent-api-orchestration/src/http/routes/health.ts +15 -15
  26. package/dist/templates/agent-api-orchestration/src/http/routes/invocations.ts +29 -29
  27. package/dist/templates/agent-api-orchestration/src/http/routes/sessions.ts +70 -70
  28. package/dist/templates/agent-api-orchestration/src/http/services/agent-service.ts +60 -60
  29. package/dist/templates/agent-api-orchestration/src/http/services/resource-service.ts +95 -95
  30. package/dist/templates/agent-api-orchestration/src/http/shared/request.ts +82 -82
  31. package/dist/templates/agent-api-orchestration/src/http/shared/sse.ts +32 -32
  32. package/dist/templates/agent-api-orchestration/src/http/types.ts +39 -39
  33. package/dist/templates/agent-api-orchestration/src/index.ts +91 -91
  34. package/dist/templates/agent-api-orchestration/src/platform-user.ts +36 -36
  35. package/dist/templates/agent-api-orchestration/src/project-config.ts +45 -45
  36. package/dist/templates/agent-api-orchestration/tsconfig.json +15 -15
  37. package/dist/templates/dag-workflow/README.md +102 -102
  38. package/dist/templates/dag-workflow/agent.manifest.json +19 -19
  39. package/dist/templates/dag-workflow/eap.config.json +1 -1
  40. package/dist/templates/dag-workflow/eap.lock.yaml +5 -5
  41. package/dist/templates/dag-workflow/gitignore +51 -51
  42. package/dist/templates/dag-workflow/graph.json +51 -51
  43. package/dist/templates/dag-workflow/package.json +10 -10
  44. package/dist/templates/dag-workflow/skills/README.md +116 -116
  45. package/dist/templates/plan-and-execute/README.md +109 -109
  46. package/dist/templates/plan-and-execute/agent.manifest.json +19 -19
  47. package/dist/templates/plan-and-execute/eap.config.json +1 -1
  48. package/dist/templates/plan-and-execute/eap.lock.yaml +5 -5
  49. package/dist/templates/plan-and-execute/gitignore +51 -51
  50. package/dist/templates/plan-and-execute/graph.json +49 -49
  51. package/dist/templates/plan-and-execute/package.json +10 -10
  52. package/dist/templates/plan-and-execute/skills/README.md +120 -120
  53. package/dist/templates/plan-and-execute/tools/echo-tool/src/index.js +17 -17
  54. package/dist/templates/plan-and-execute/tools/echo-tool/toolset.manifest.json +55 -55
  55. package/dist/templates/simple-flow/README.md +87 -87
  56. package/dist/templates/simple-flow/agent.manifest.json +19 -19
  57. package/dist/templates/simple-flow/eap.config.json +1 -1
  58. package/dist/templates/simple-flow/eap.lock.yaml +5 -5
  59. package/dist/templates/simple-flow/gitignore +51 -51
  60. package/dist/templates/simple-flow/graph.json +32 -32
  61. package/dist/templates/simple-flow/package.json +10 -10
  62. package/dist/templates/simple-flow/skills/README.md +113 -113
  63. package/dist/templates/single-agent-with-tools/README.md +138 -138
  64. package/dist/templates/single-agent-with-tools/agent.manifest.json +19 -19
  65. package/dist/templates/single-agent-with-tools/eap.config.json +1 -1
  66. package/dist/templates/single-agent-with-tools/eap.lock.yaml +5 -5
  67. package/dist/templates/single-agent-with-tools/gitignore +51 -51
  68. package/dist/templates/single-agent-with-tools/graph.json +41 -41
  69. package/dist/templates/single-agent-with-tools/package.json +10 -10
  70. package/dist/templates/single-agent-with-tools/skills/README.md +119 -119
  71. package/dist/templates/single-agent-with-tools/tools/echo-tool/src/index.js +17 -17
  72. package/dist/templates/single-agent-with-tools/tools/echo-tool/toolset.manifest.json +55 -55
  73. package/dist/templates/single-agent-with-tools/tools/http-weather/toolset.manifest.json +73 -73
  74. package/dist/templates/single-agent-with-tools/tools/web-search/toolset.manifest.json +41 -41
  75. package/package.json +3 -3
@@ -1,26 +1,26 @@
1
- import { config } from 'dotenv';
2
- import { packageStandaloneAgentProject } from '@gt-fe/eap-sdk';
3
- import { dirname, resolve } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
-
6
- const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
7
-
8
- // --dev 使用项目根的 .eap/runtime;生产构建使用 dist/.eap/runtime。
9
- const isDev = process.argv.includes('--dev');
10
-
11
- // 统一读取项目根 .env。
12
- config({ path: resolve(root, '.env') });
13
-
14
- // 生产输出目录必须位于 dist 下,便于与 dist/index.js 一起发布。
15
- const runtimeBase = isDev ? root : resolve(root, 'dist');
16
- const runtimeRoot = resolve(
17
- runtimeBase,
18
- process.env.EAP_RUNTIME_ROOT?.trim() || './.eap/runtime',
19
- );
20
-
21
- const prepared = await packageStandaloneAgentProject({
22
- root,
23
- outputDirectory: runtimeRoot,
24
- });
25
-
26
- console.log(`Runtime 已生成: ${prepared.runtimeRoot}`);
1
+ import { config } from 'dotenv';
2
+ import { packageStandaloneAgentProject } from '@gt-fe/eap-sdk';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
7
+
8
+ // --dev 使用项目根的 .eap/runtime;生产构建使用 dist/.eap/runtime。
9
+ const isDev = process.argv.includes('--dev');
10
+
11
+ // 统一读取项目根 .env。
12
+ config({ path: resolve(root, '.env') });
13
+
14
+ // 生产输出目录必须位于 dist 下,便于与 dist/index.js 一起发布。
15
+ const runtimeBase = isDev ? root : resolve(root, 'dist');
16
+ const runtimeRoot = resolve(
17
+ runtimeBase,
18
+ process.env.EAP_RUNTIME_ROOT?.trim() || './.eap/runtime',
19
+ );
20
+
21
+ const prepared = await packageStandaloneAgentProject({
22
+ root,
23
+ outputDirectory: runtimeRoot,
24
+ });
25
+
26
+ console.log(`Runtime 已生成: ${prepared.runtimeRoot}`);
@@ -1,12 +1,12 @@
1
- import { validateAgentProject } from '@gt-fe/eap-sdk';
2
- import { dirname, resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- // 校验脚本始终以模板项目根目录为 Agent 源项目根。
6
- const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
7
- const result = validateAgentProject(root);
8
- // 输出全部问题,交给调用方根据退出码阻止后续构建。
9
- for (const issue of result.issues) {
10
- console.log(`[${issue.severity}] ${issue.message}`);
11
- }
12
- if (!result.valid) process.exitCode = 1;
1
+ import { validateAgentProject } from '@gt-fe/eap-sdk';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ // 校验脚本始终以模板项目根目录为 Agent 源项目根。
6
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
7
+ const result = validateAgentProject(root);
8
+ // 输出全部问题,交给调用方根据退出码阻止后续构建。
9
+ for (const issue of result.issues) {
10
+ console.log(`[${issue.severity}] ${issue.message}`);
11
+ }
12
+ if (!result.valid) process.exitCode = 1;
@@ -1,92 +1,92 @@
1
- import {
2
- createAgent,
3
- resolveProjectRuntimeEnvironment,
4
- type Agent,
5
- type AgentCredentialProvider,
6
- type AgentResourceManager,
7
- type EmbeddedSdkConfig,
8
- } from '@gt-fe/eap-sdk';
9
- import { dirname, resolve } from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- import { loadTemplateRuntime, resolveTemplateRuntimeRoot } from './project-config.js';
13
- export { AgentResourceService, createAgentResourceService } from './http/services/resource-service.js';
14
- export { createAgentApp, createAgentHttpServer, startAgentHttpServer } from './http/index.js';
15
- export type { AgentHttpStartOptions } from './http/index.js';
16
-
17
- const entryDirectory = dirname(fileURLToPath(import.meta.url));
18
- const isDev = process.argv.includes('--dev');
19
-
20
- export interface EmbeddedAgentOptions {
21
- /** 生产宿主显式注入的短期凭证 Provider;优先级高于 EAP_SDK_TOKEN。 */
22
- credentialProvider?: AgentCredentialProvider;
23
- /** 仅用于开发演示的 Token 回退。 */
24
- sdkToken?: string;
25
- /** 覆盖模板默认的物化 Runtime 路径。 */
26
- runtimeRoot?: string;
27
- /** 宿主实现的资源 manager;模板只负责把它注入 SDK。 */
28
- resourceManager?: AgentResourceManager;
29
- }
30
-
31
- /**
32
- * 创建一个使用模板项目配置的内嵌 Agent。
33
- *
34
- * platform.portalUrl 和 platform.foundationServiceUrl 来自 eap.config.json;
35
- * 凭证优先使用宿主显式传入的 Provider;只有开发模式才回退到 EAP_SDK_TOKEN。
36
- */
37
- export async function createEmbeddedAgent(options: EmbeddedAgentOptions = {}): Promise<Agent> {
38
- const runtimeRoot = options.runtimeRoot ?? resolveTemplateRuntimeRoot(entryDirectory);
39
- const loadedRuntime = loadTemplateRuntime(entryDirectory, runtimeRoot);
40
- const platform = {
41
- portalEndpoint: loadedRuntime.config.platform.portalUrl.trim(),
42
- ...(loadedRuntime.config.platform.foundationServiceUrl?.trim()
43
- ? { foundationEndpoint: loadedRuntime.config.platform.foundationServiceUrl.trim() }
44
- : {}),
45
- };
46
- // 凭证优先级:宿主显式注入的 credentialProvider > EAP_SDK_TOKEN 环境变量回退。
47
- // 显式注入用于生产托管;EAP_SDK_TOKEN 用于本地演示(npm run dev / 本地起 dist 冒烟)。
48
- const provider =
49
- options.credentialProvider ??
50
- createSdkTokenProvider(options.sdkToken ?? process.env.EAP_SDK_TOKEN);
51
- const embeddedConfig: EmbeddedSdkConfig = {
52
- platform,
53
- credentials: { provider },
54
- ...(options.resourceManager ? { resources: { manager: options.resourceManager } } : {}),
55
- };
56
- const deploymentRoot = resolve(runtimeRoot, '..', '..');
57
-
58
- // 把 eap.config.json 里的 runtime.llm.models 映射成运行时识别的本地模型连接
59
- // (EAP_LLM_MODE=local + EAP_LLM_CONNECTIONS)。漏掉这一步时,运行时会因为拿不到
60
- // 本地网关而退回 EchoLLMProvider,表现为 [echo:model] 回显用户输入而非真实对话。
61
- const projectRoot = resolve(entryDirectory, '..');
62
- const runtimeEnvironment = resolveProjectRuntimeEnvironment({
63
- root: projectRoot,
64
- preinstallRoot: runtimeRoot,
65
- }).environment;
66
-
67
- return createAgent({
68
- runtimeRoot,
69
- deploymentContext: isDev ? 'dev' : 'server',
70
- embeddedConfig,
71
- environment: {
72
- ...runtimeEnvironment,
73
- // 模板以物化 Runtime 目录为基准,覆盖 SDK 相对项目根算出的路径。
74
- EAP_DATA_DIR: resolve(runtimeRoot, '..'),
75
- EAP_CHECKPOINT_DIR: resolve(deploymentRoot, loadedRuntime.config.runtime.storage.checkpointDir),
76
- },
77
- // 模板默认 eap.config.json 使用 dev 存储档,由 Runtime 提供本地文件实现。
78
- sessionPersistence: loadedRuntime.config.runtime.storage.profile === 'dev',
79
- });
80
- }
81
-
82
- export function createSdkTokenProvider(token?: string): AgentCredentialProvider {
83
- return {
84
- async getToken() {
85
- const value = token?.trim() || process.env.EAP_SDK_TOKEN?.trim();
86
- if (!value) {
87
- throw new Error('请由生产宿主显式注入 credentialProvider;EAP_SDK_TOKEN 仅用于本地演示回退');
88
- }
89
- return { value };
90
- },
91
- };
1
+ import {
2
+ createAgent,
3
+ resolveProjectRuntimeEnvironment,
4
+ type Agent,
5
+ type AgentCredentialProvider,
6
+ type AgentResourceManager,
7
+ type EmbeddedSdkConfig,
8
+ } from '@gt-fe/eap-sdk';
9
+ import { dirname, resolve } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ import { loadTemplateRuntime, resolveTemplateRuntimeRoot } from './project-config.js';
13
+ export { AgentResourceService, createAgentResourceService } from './http/services/resource-service.js';
14
+ export { createAgentApp, createAgentHttpServer, startAgentHttpServer } from './http/index.js';
15
+ export type { AgentHttpStartOptions } from './http/index.js';
16
+
17
+ const entryDirectory = dirname(fileURLToPath(import.meta.url));
18
+ const isDev = process.argv.includes('--dev');
19
+
20
+ export interface EmbeddedAgentOptions {
21
+ /** 生产宿主显式注入的短期凭证 Provider;优先级高于 EAP_SDK_TOKEN。 */
22
+ credentialProvider?: AgentCredentialProvider;
23
+ /** 仅用于开发演示的 Token 回退。 */
24
+ sdkToken?: string;
25
+ /** 覆盖模板默认的物化 Runtime 路径。 */
26
+ runtimeRoot?: string;
27
+ /** 宿主实现的资源 manager;模板只负责把它注入 SDK。 */
28
+ resourceManager?: AgentResourceManager;
29
+ }
30
+
31
+ /**
32
+ * 创建一个使用模板项目配置的内嵌 Agent。
33
+ *
34
+ * platform.portalUrl 和 platform.foundationServiceUrl 来自 eap.config.json;
35
+ * 凭证优先使用宿主显式传入的 Provider;只有开发模式才回退到 EAP_SDK_TOKEN。
36
+ */
37
+ export async function createEmbeddedAgent(options: EmbeddedAgentOptions = {}): Promise<Agent> {
38
+ const runtimeRoot = options.runtimeRoot ?? resolveTemplateRuntimeRoot(entryDirectory);
39
+ const loadedRuntime = loadTemplateRuntime(entryDirectory, runtimeRoot);
40
+ const platform = {
41
+ portalEndpoint: loadedRuntime.config.platform.portalUrl.trim(),
42
+ ...(loadedRuntime.config.platform.foundationServiceUrl?.trim()
43
+ ? { foundationEndpoint: loadedRuntime.config.platform.foundationServiceUrl.trim() }
44
+ : {}),
45
+ };
46
+ // 凭证优先级:宿主显式注入的 credentialProvider > EAP_SDK_TOKEN 环境变量回退。
47
+ // 显式注入用于生产托管;EAP_SDK_TOKEN 用于本地演示(npm run dev / 本地起 dist 冒烟)。
48
+ const provider =
49
+ options.credentialProvider ??
50
+ createSdkTokenProvider(options.sdkToken ?? process.env.EAP_SDK_TOKEN);
51
+ const embeddedConfig: EmbeddedSdkConfig = {
52
+ platform,
53
+ credentials: { provider },
54
+ ...(options.resourceManager ? { resources: { manager: options.resourceManager } } : {}),
55
+ };
56
+ const deploymentRoot = resolve(runtimeRoot, '..', '..');
57
+
58
+ // 把 eap.config.json 里的 runtime.llm.models 映射成运行时识别的本地模型连接
59
+ // (EAP_LLM_MODE=local + EAP_LLM_CONNECTIONS)。漏掉这一步时,运行时会因为拿不到
60
+ // 本地网关而退回 EchoLLMProvider,表现为 [echo:model] 回显用户输入而非真实对话。
61
+ const projectRoot = resolve(entryDirectory, '..');
62
+ const runtimeEnvironment = resolveProjectRuntimeEnvironment({
63
+ root: projectRoot,
64
+ preinstallRoot: runtimeRoot,
65
+ }).environment;
66
+
67
+ return createAgent({
68
+ runtimeRoot,
69
+ deploymentContext: isDev ? 'dev' : 'server',
70
+ embeddedConfig,
71
+ environment: {
72
+ ...runtimeEnvironment,
73
+ // 模板以物化 Runtime 目录为基准,覆盖 SDK 相对项目根算出的路径。
74
+ EAP_DATA_DIR: resolve(runtimeRoot, '..'),
75
+ EAP_CHECKPOINT_DIR: resolve(deploymentRoot, loadedRuntime.config.runtime.storage.checkpointDir),
76
+ },
77
+ // 模板默认 eap.config.json 使用 dev 存储档,由 Runtime 提供本地文件实现。
78
+ sessionPersistence: loadedRuntime.config.runtime.storage.profile === 'dev',
79
+ });
80
+ }
81
+
82
+ export function createSdkTokenProvider(token?: string): AgentCredentialProvider {
83
+ return {
84
+ async getToken() {
85
+ const value = token?.trim() || process.env.EAP_SDK_TOKEN?.trim();
86
+ if (!value) {
87
+ throw new Error('请由生产宿主显式注入 credentialProvider;EAP_SDK_TOKEN 仅用于本地演示回退');
88
+ }
89
+ return { value };
90
+ },
91
+ };
92
92
  }
@@ -1,58 +1,58 @@
1
- import express, { type Express } from 'express';
2
- import { createServer, type Server } from 'node:http';
3
- import type { Agent } from '@gt-fe/eap-sdk';
4
- import { errorHandler, notFound } from './errors.js';
5
- import { resolveAgentHttpStartOptions, formatHost } from './config.js';
6
- import { corsMiddleware } from './middleware/cors.js';
7
- import { createAgentApiRouter } from './routes/agent.js';
8
- import { createHealthRouter } from './routes/health.js';
9
- import type { AgentHttpService, AgentHttpStartOptions, ResolvedAgentHttpOptions } from './types.js';
10
-
11
- export function createAgentApp(agent: Agent, options: AgentHttpStartOptions = {}): Express {
12
- const resolved = resolveAgentHttpStartOptions(options);
13
- const app = express();
14
- app.disable('x-powered-by');
15
- app.use(corsMiddleware(resolved.corsOrigin));
16
- app.use((_request, response, next) => {
17
- // 统一返回 Agent 标识,前端可以用响应头确认当前服务版本。
18
- response.setHeader('X-Agent-Code', agent.target.agentCode);
19
- response.setHeader('X-Agent-Version', agent.target.version);
20
- next();
21
- });
22
- app.use(express.json({ limit: resolved.maxBodyBytes }));
23
- app.use('/health', createHealthRouter(agent));
24
- app.use('/api/v1/agents', createAgentApiRouter(agent));
25
- app.use((_request, _response, next) => next(notFound()));
26
- app.use(errorHandler);
27
- return app;
28
- }
29
-
30
- export function createAgentHttpServer(agent: Agent, options: AgentHttpStartOptions = {}): Server {
31
- return createServer(createAgentApp(agent, options));
32
- }
33
-
34
- export async function startAgentHttpServer(agent: Agent, options: AgentHttpStartOptions = {}): Promise<AgentHttpService> {
35
- const resolved: ResolvedAgentHttpOptions = resolveAgentHttpStartOptions(options);
36
- // 返回实际监听的 app,便于宿主读取 Express 实例或交给框架托管。
37
- const app = createAgentApp(agent, resolved);
38
- const server = createServer(app);
39
- await new Promise<void>((resolve, reject) => {
40
- const onError = (error: Error) => reject(error);
41
- server.once('error', onError);
42
- server.listen(resolved.port, resolved.host, () => {
43
- server.off('error', onError);
44
- resolve();
45
- });
46
- });
47
- const address = server.address();
48
- const port = address && typeof address !== 'string' ? address.port : resolved.port;
49
- return {
50
- app,
51
- server,
52
- address: `http://${formatHost(resolved.host)}:${port}`,
53
- async stop() {
54
- if (!server.listening) return;
55
- await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
56
- },
57
- };
58
- }
1
+ import express, { type Express } from 'express';
2
+ import { createServer, type Server } from 'node:http';
3
+ import type { Agent } from '@gt-fe/eap-sdk';
4
+ import { errorHandler, notFound } from './errors.js';
5
+ import { resolveAgentHttpStartOptions, formatHost } from './config.js';
6
+ import { corsMiddleware } from './middleware/cors.js';
7
+ import { createAgentApiRouter } from './routes/agent.js';
8
+ import { createHealthRouter } from './routes/health.js';
9
+ import type { AgentHttpService, AgentHttpStartOptions, ResolvedAgentHttpOptions } from './types.js';
10
+
11
+ export function createAgentApp(agent: Agent, options: AgentHttpStartOptions = {}): Express {
12
+ const resolved = resolveAgentHttpStartOptions(options);
13
+ const app = express();
14
+ app.disable('x-powered-by');
15
+ app.use(corsMiddleware(resolved.corsOrigin));
16
+ app.use((_request, response, next) => {
17
+ // 统一返回 Agent 标识,前端可以用响应头确认当前服务版本。
18
+ response.setHeader('X-Agent-Code', agent.target.agentCode);
19
+ response.setHeader('X-Agent-Version', agent.target.version);
20
+ next();
21
+ });
22
+ app.use(express.json({ limit: resolved.maxBodyBytes }));
23
+ app.use('/health', createHealthRouter(agent));
24
+ app.use('/api/v1/agents', createAgentApiRouter(agent));
25
+ app.use((_request, _response, next) => next(notFound()));
26
+ app.use(errorHandler);
27
+ return app;
28
+ }
29
+
30
+ export function createAgentHttpServer(agent: Agent, options: AgentHttpStartOptions = {}): Server {
31
+ return createServer(createAgentApp(agent, options));
32
+ }
33
+
34
+ export async function startAgentHttpServer(agent: Agent, options: AgentHttpStartOptions = {}): Promise<AgentHttpService> {
35
+ const resolved: ResolvedAgentHttpOptions = resolveAgentHttpStartOptions(options);
36
+ // 返回实际监听的 app,便于宿主读取 Express 实例或交给框架托管。
37
+ const app = createAgentApp(agent, resolved);
38
+ const server = createServer(app);
39
+ await new Promise<void>((resolve, reject) => {
40
+ const onError = (error: Error) => reject(error);
41
+ server.once('error', onError);
42
+ server.listen(resolved.port, resolved.host, () => {
43
+ server.off('error', onError);
44
+ resolve();
45
+ });
46
+ });
47
+ const address = server.address();
48
+ const port = address && typeof address !== 'string' ? address.port : resolved.port;
49
+ return {
50
+ app,
51
+ server,
52
+ address: `http://${formatHost(resolved.host)}:${port}`,
53
+ async stop() {
54
+ if (!server.listening) return;
55
+ await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
56
+ },
57
+ };
58
+ }
@@ -1,34 +1,34 @@
1
- import type { AgentHttpStartOptions, ResolvedAgentHttpOptions } from './types.js';
2
-
3
- export function resolveAgentHttpStartOptions(
4
- options: AgentHttpStartOptions = {},
5
- env: NodeJS.ProcessEnv = process.env,
6
- ): ResolvedAgentHttpOptions {
7
- // 显式 options 优先于环境变量,便于测试、嵌入其他服务或动态选择端口。
8
- return {
9
- host: options.host ?? env.EAP_HTTP_HOST ?? '127.0.0.1',
10
- port: options.port ?? parsePort(env.EAP_HTTP_PORT ?? env.EAP_RUNTIME_PORT),
11
- corsOrigin: options.corsOrigin ?? env.EAP_HTTP_CORS_ORIGIN ?? '*',
12
- maxBodyBytes: options.maxBodyBytes ?? parseBodyLimit(env.EAP_HTTP_MAX_BODY_BYTES),
13
- };
14
- }
15
-
16
- export function formatHost(host: string): string {
17
- return host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
18
- }
19
-
20
- function parsePort(value: string | undefined): number {
21
- // 允许传 0,让 Node 自动分配空闲端口,适合宿主编排和本地联调。
22
- if (!value) return 3000;
23
- const port = Number(value);
24
- if (Number.isInteger(port) && port >= 0 && port <= 65535) return port;
25
- throw new Error(`Invalid EAP_HTTP_PORT: ${value}`);
26
- }
27
-
28
- function parseBodyLimit(value: string | undefined): number {
29
- // 限制请求体大小,避免前端误传大对象占满服务内存。
30
- if (!value) return 1024 * 1024;
31
- const limit = Number(value);
32
- if (Number.isInteger(limit) && limit >= 1024 && limit <= 50 * 1024 * 1024) return limit;
33
- throw new Error(`Invalid EAP_HTTP_MAX_BODY_BYTES: ${value}`);
34
- }
1
+ import type { AgentHttpStartOptions, ResolvedAgentHttpOptions } from './types.js';
2
+
3
+ export function resolveAgentHttpStartOptions(
4
+ options: AgentHttpStartOptions = {},
5
+ env: NodeJS.ProcessEnv = process.env,
6
+ ): ResolvedAgentHttpOptions {
7
+ // 显式 options 优先于环境变量,便于测试、嵌入其他服务或动态选择端口。
8
+ return {
9
+ host: options.host ?? env.EAP_HTTP_HOST ?? '127.0.0.1',
10
+ port: options.port ?? parsePort(env.EAP_HTTP_PORT ?? env.EAP_RUNTIME_PORT),
11
+ corsOrigin: options.corsOrigin ?? env.EAP_HTTP_CORS_ORIGIN ?? '*',
12
+ maxBodyBytes: options.maxBodyBytes ?? parseBodyLimit(env.EAP_HTTP_MAX_BODY_BYTES),
13
+ };
14
+ }
15
+
16
+ export function formatHost(host: string): string {
17
+ return host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
18
+ }
19
+
20
+ function parsePort(value: string | undefined): number {
21
+ // 允许传 0,让 Node 自动分配空闲端口,适合宿主编排和本地联调。
22
+ if (!value) return 3000;
23
+ const port = Number(value);
24
+ if (Number.isInteger(port) && port >= 0 && port <= 65535) return port;
25
+ throw new Error(`Invalid EAP_HTTP_PORT: ${value}`);
26
+ }
27
+
28
+ function parseBodyLimit(value: string | undefined): number {
29
+ // 限制请求体大小,避免前端误传大对象占满服务内存。
30
+ if (!value) return 1024 * 1024;
31
+ const limit = Number(value);
32
+ if (Number.isInteger(limit) && limit >= 1024 && limit <= 50 * 1024 * 1024) return limit;
33
+ throw new Error(`Invalid EAP_HTTP_MAX_BODY_BYTES: ${value}`);
34
+ }
@@ -1,63 +1,63 @@
1
- import type { ErrorRequestHandler, RequestHandler, Response } from 'express';
2
-
3
- export class HttpError extends Error {
4
- readonly status: number;
5
- readonly code: string;
6
-
7
- constructor(status: number, code: string, message: string) {
8
- super(message);
9
- this.name = 'HttpError';
10
- this.status = status;
11
- this.code = code;
12
- }
13
- }
14
-
15
- export function badRequest(message: string): HttpError {
16
- return new HttpError(400, 'bad_request', message);
17
- }
18
-
19
- export function notFound(message = 'Route not found'): HttpError {
20
- return new HttpError(404, 'not_found', message);
21
- }
22
-
23
- export function asyncRoute(handler: RequestHandler): RequestHandler {
24
- // Express 4 不会自动接住 Promise rejection,统一在这里转给错误处理中间件。
25
- return (request, response, next) => {
26
- Promise.resolve(handler(request, response, next)).catch(next);
27
- };
28
- }
29
-
30
- export const errorHandler: ErrorRequestHandler = (error, _request, response, _next) => {
31
- // 普通请求返回 JSON;如果 SSE 已经开始,则用 error 事件结束当前流。
32
- // SDK 的能力错误(例如 SessionCapabilityError)也带 status/code,直接映射可保留 501 等语义。
33
- const status = error instanceof HttpError ? error.status : readStatus(error) ?? 500;
34
- const code = error instanceof HttpError ? error.code : readCode(error) ?? 'internal_error';
35
- const message = error instanceof Error ? error.message : String(error);
36
- if (response.headersSent) {
37
- if (!response.destroyed) {
38
- response.write(`event: error\ndata: ${JSON.stringify({ code, error: message })}\n\n`);
39
- response.end();
40
- }
41
- return;
42
- }
43
- response.status(status).json({ error: message, code });
44
- };
45
-
46
- function readStatus(error: unknown): number | undefined {
47
- if (!error || typeof error !== 'object') return undefined;
48
- const status = (error as { status?: unknown }).status;
49
- return typeof status === 'number' && status >= 400 && status <= 599 ? status : undefined;
50
- }
51
-
52
- function readCode(error: unknown): string | undefined {
53
- if (!error || typeof error !== 'object') return undefined;
54
- const code = (error as { code?: unknown }).code;
55
- return typeof code === 'string' && code.trim() ? code : undefined;
56
- }
57
-
58
- export function sendSseError(response: Response, error: unknown): void {
59
- if (response.destroyed) return;
60
- const message = error instanceof Error ? error.message : String(error);
61
- response.write(`event: error\ndata: ${JSON.stringify({ code: 'stream_error', error: message })}\n\n`);
62
- response.end();
63
- }
1
+ import type { ErrorRequestHandler, RequestHandler, Response } from 'express';
2
+
3
+ export class HttpError extends Error {
4
+ readonly status: number;
5
+ readonly code: string;
6
+
7
+ constructor(status: number, code: string, message: string) {
8
+ super(message);
9
+ this.name = 'HttpError';
10
+ this.status = status;
11
+ this.code = code;
12
+ }
13
+ }
14
+
15
+ export function badRequest(message: string): HttpError {
16
+ return new HttpError(400, 'bad_request', message);
17
+ }
18
+
19
+ export function notFound(message = 'Route not found'): HttpError {
20
+ return new HttpError(404, 'not_found', message);
21
+ }
22
+
23
+ export function asyncRoute(handler: RequestHandler): RequestHandler {
24
+ // Express 4 不会自动接住 Promise rejection,统一在这里转给错误处理中间件。
25
+ return (request, response, next) => {
26
+ Promise.resolve(handler(request, response, next)).catch(next);
27
+ };
28
+ }
29
+
30
+ export const errorHandler: ErrorRequestHandler = (error, _request, response, _next) => {
31
+ // 普通请求返回 JSON;如果 SSE 已经开始,则用 error 事件结束当前流。
32
+ // SDK 的能力错误(例如 SessionCapabilityError)也带 status/code,直接映射可保留 501 等语义。
33
+ const status = error instanceof HttpError ? error.status : readStatus(error) ?? 500;
34
+ const code = error instanceof HttpError ? error.code : readCode(error) ?? 'internal_error';
35
+ const message = error instanceof Error ? error.message : String(error);
36
+ if (response.headersSent) {
37
+ if (!response.destroyed) {
38
+ response.write(`event: error\ndata: ${JSON.stringify({ code, error: message })}\n\n`);
39
+ response.end();
40
+ }
41
+ return;
42
+ }
43
+ response.status(status).json({ error: message, code });
44
+ };
45
+
46
+ function readStatus(error: unknown): number | undefined {
47
+ if (!error || typeof error !== 'object') return undefined;
48
+ const status = (error as { status?: unknown }).status;
49
+ return typeof status === 'number' && status >= 400 && status <= 599 ? status : undefined;
50
+ }
51
+
52
+ function readCode(error: unknown): string | undefined {
53
+ if (!error || typeof error !== 'object') return undefined;
54
+ const code = (error as { code?: unknown }).code;
55
+ return typeof code === 'string' && code.trim() ? code : undefined;
56
+ }
57
+
58
+ export function sendSseError(response: Response, error: unknown): void {
59
+ if (response.destroyed) return;
60
+ const message = error instanceof Error ? error.message : String(error);
61
+ response.write(`event: error\ndata: ${JSON.stringify({ code: 'stream_error', error: message })}\n\n`);
62
+ response.end();
63
+ }
@@ -1,4 +1,4 @@
1
- // 对外只暴露 Express 适配器的稳定入口,内部路由文件可按宿主需求替换。
2
- export { createAgentApp, createAgentHttpServer, startAgentHttpServer } from './app.js';
3
- export { resolveAgentHttpStartOptions } from './config.js';
4
- export type { AgentHttpService, AgentHttpStartOptions, ResolvedAgentHttpOptions } from './types.js';
1
+ // 对外只暴露 Express 适配器的稳定入口,内部路由文件可按宿主需求替换。
2
+ export { createAgentApp, createAgentHttpServer, startAgentHttpServer } from './app.js';
3
+ export { resolveAgentHttpStartOptions } from './config.js';
4
+ export type { AgentHttpService, AgentHttpStartOptions, ResolvedAgentHttpOptions } from './types.js';
@@ -1,17 +1,17 @@
1
- import type { RequestHandler } from 'express';
2
-
3
- export function corsMiddleware(origin: string): RequestHandler {
4
- return (_request, response, next) => {
5
- // 这是最小的跨域适配器;生产环境建议由宿主按自己的 Origin 白名单替换。
6
- response.setHeader('Access-Control-Allow-Origin', origin);
7
- response.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Correlation-Id');
8
- response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
9
- response.setHeader('Access-Control-Expose-Headers', 'X-Agent-Code, X-Agent-Version');
10
- response.setHeader('Vary', 'Origin');
11
- if (_request.method === 'OPTIONS') {
12
- response.status(204).end();
13
- return;
14
- }
15
- next();
16
- };
17
- }
1
+ import type { RequestHandler } from 'express';
2
+
3
+ export function corsMiddleware(origin: string): RequestHandler {
4
+ return (_request, response, next) => {
5
+ // 这是最小的跨域适配器;生产环境建议由宿主按自己的 Origin 白名单替换。
6
+ response.setHeader('Access-Control-Allow-Origin', origin);
7
+ response.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Correlation-Id');
8
+ response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
9
+ response.setHeader('Access-Control-Expose-Headers', 'X-Agent-Code, X-Agent-Version');
10
+ response.setHeader('Vary', 'Origin');
11
+ if (_request.method === 'OPTIONS') {
12
+ response.status(204).end();
13
+ return;
14
+ }
15
+ next();
16
+ };
17
+ }