@omni-api/core 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/zod-to-json-schema.ts","../../src/utils/export-tools.ts","../../src/utils/graceful-shutdown.ts","../../src/utils/load-env.ts","../../src/utils/traced-fetch.ts"],"names":["_zodToJsonSchema","tool"],"mappings":"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","file":"index.js","sourcesContent":["import type { ZodTypeAny } from 'zod';\nimport { zodToJsonSchema as _zodToJsonSchema } from 'zod-to-json-schema';\n\n/**\n * 把 Zod schema 转 JSON Schema(draft-07 风格,OpenAPI/MCP/LLM 通用)。\n *\n * 这里做一层薄封装,统一选项,并去掉 zod-to-json-schema 默认加上的 `$schema` 字段\n * (MCP / OpenAI tool 格式都不需要也不允许该字段)。\n */\nexport function zodToJsonSchema(schema: ZodTypeAny): Record<string, unknown> {\n const raw = _zodToJsonSchema(schema, {\n target: 'jsonSchema7',\n $refStrategy: 'none', // 内联,避免下游处理 $ref\n }) as Record<string, unknown>;\n\n // 去掉 $schema,保留纯净的 schema 体\n const { $schema, ...rest } = raw;\n void $schema;\n return rest;\n}\n","import type { Procedure } from '../procedure.js';\nimport type { Registry } from '../registry.js';\nimport { zodToJsonSchema } from './zod-to-json-schema.js';\n\n/**\n * 把一个 Procedure 转成给 LLM 的\"增强 description\"。\n *\n * 拼接顺序:\n * 1. 危险等级标签(destructive/write 时显式提示)\n * 2. 主 description\n * 3. 别名(aliases)\n * 4. 示例(examples)\n *\n * 这是\"逃生出口\" helper —— 主要给不支持 MCP 的老 Agent 平台用。\n */\nfunction buildAgentDescription(p: Procedure): string {\n const mcpMeta = p.meta.mcp;\n const lines: string[] = [];\n\n if (mcpMeta?.dangerLevel === 'destructive') {\n lines.push('[DESTRUCTIVE] 此操作会造成不可逆后果,调用前务必与用户确认。');\n } else if (mcpMeta?.dangerLevel === 'write') {\n lines.push('[WRITE] 此操作会修改数据。');\n }\n\n lines.push(p.description.trim());\n\n if (mcpMeta?.aliases && mcpMeta.aliases.length > 0) {\n lines.push(`别名: ${mcpMeta.aliases.join(', ')}`);\n }\n\n if (mcpMeta?.examples && mcpMeta.examples.length > 0) {\n lines.push('示例:');\n for (const ex of mcpMeta.examples) {\n const inputStr = JSON.stringify(ex.input);\n lines.push(` - 当 ${ex.when}: ${inputStr}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/** OpenAI Function Calling 工具格式 */\nexport interface OpenAITool {\n type: 'function';\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n}\n\n/** Anthropic(Claude)Tool Use 格式 */\nexport interface AnthropicTool {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\n/** 通用格式(直接是 input schema + 名称描述) */\nexport interface GenericTool {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n}\n\nexport type ToolFormat = 'openai' | 'anthropic' | 'generic';\n\nexport interface ExportToolsOptions {\n /** 输出格式,默认 'openai' */\n format?: ToolFormat;\n /**\n * 名字转换函数。\n *\n * - openai: 默认把 \".\" 替换为 \"_\"(OpenAI tool name 不允许 \".\")\n * - anthropic / generic: 默认保留原名\n */\n renameTool?: (name: string) => string;\n /** 自定义过滤;默认导出所有 procedure */\n filter?: (p: Procedure) => boolean;\n}\n\n/**\n * 把 Registry 中的 Procedure 导出为给 LLM 的工具数组。\n *\n * 这是为了\"老平台兼容 / 调试\"而存在的逃生出口;推荐场景仍是直接走 MCP。\n */\nexport function exportToolsJSON(\n registry: Registry,\n options: ExportToolsOptions & { format: 'openai' },\n): OpenAITool[];\nexport function exportToolsJSON(\n registry: Registry,\n options: ExportToolsOptions & { format: 'anthropic' },\n): AnthropicTool[];\nexport function exportToolsJSON(\n registry: Registry,\n options?: ExportToolsOptions & { format?: 'generic' },\n): GenericTool[];\nexport function exportToolsJSON(\n registry: Registry,\n options: ExportToolsOptions = {},\n): OpenAITool[] | AnthropicTool[] | GenericTool[] {\n const format: ToolFormat = options.format ?? 'generic';\n const procedures = registry.list().filter((p) => (options.filter ? options.filter(p) : true));\n\n const defaultRename = (name: string): string =>\n format === 'openai' ? name.replace(/\\./g, '_') : name;\n const rename = options.renameTool ?? defaultRename;\n\n return procedures.map((p) => {\n const description = buildAgentDescription(p);\n const parameters = zodToJsonSchema(p.input);\n const name = rename(p.name);\n\n if (format === 'openai') {\n const tool: OpenAITool = {\n type: 'function',\n function: { name, description, parameters },\n };\n return tool;\n }\n if (format === 'anthropic') {\n const tool: AnthropicTool = { name, description, input_schema: parameters };\n return tool;\n }\n const tool: GenericTool = { name, description, parameters };\n return tool;\n }) as OpenAITool[] | AnthropicTool[] | GenericTool[];\n}\n","/**\n * Graceful shutdown helper —— 跨 adapter / plugin 的优雅停机协调器。\n *\n * 设计原则:\n * 1. 顺序停机:先停接收(adapters),再停依赖(dataSource / cache / observability)\n * 2. 超时强杀:每个 hook 有独立超时,整体也有超时;防止挂死\n * 3. 幂等:重复信号只触发一次(防止 double-shutdown 撞死)\n * 4. 信号统一:SIGTERM / SIGINT / 可选 uncaughtException\n *\n * 用法:\n * ```ts\n * const g = createGracefulShutdown({ logger });\n * g.add('http', () => httpAdapter.close(), 10_000);\n * g.add('db', () => dataSource.disconnect(), 5_000);\n * g.add('logs', () => logger.flush?.(), 2_000);\n * g.listen(); // 注册 SIGTERM/SIGINT\n * ```\n */\n\nexport interface ShutdownHook {\n /** 名字,仅用于日志 */\n name: string;\n /** 关闭函数 */\n fn: () => Promise<void> | void;\n /** 该 hook 的超时(毫秒),默认 5000 */\n timeoutMs?: number;\n}\n\nexport interface GracefulShutdownOptions {\n /** 整体超时(毫秒),超过则强制 process.exit。默认 30s */\n totalTimeoutMs?: number;\n /** 日志记录器(任意 console-like 接口) */\n logger?: {\n info(msg: string, meta?: Record<string, unknown>): void;\n error(msg: string, meta?: Record<string, unknown>): void;\n warn?(msg: string, meta?: Record<string, unknown>): void;\n };\n /** 是否处理 uncaughtException / unhandledRejection(默认 true) */\n handleFatalErrors?: boolean;\n /** 退出函数(默认 process.exit);测试时可注入 */\n exit?: (code: number) => void;\n /** 信号注入点(默认监听 process);测试时可传自定义 emitter */\n on?: (signal: 'SIGTERM' | 'SIGINT', handler: () => void) => void;\n}\n\nexport interface GracefulShutdown {\n /** 注册 shutdown hook,按注册顺序倒序执行(后注册的先关) */\n add(name: string, fn: () => Promise<void> | void, timeoutMs?: number): void;\n /** 开始监听信号 */\n listen(): void;\n /** 主动触发 shutdown(用于测试 / 业务自管) */\n trigger(reason: string): Promise<void>;\n /** 是否已经在 shutdown 中 */\n readonly isShuttingDown: boolean;\n}\n\n/** 给一个 promise 加超时 */\nfunction withTimeout<T>(p: Promise<T> | T, timeoutMs: number, label: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n reject(new Error(`Shutdown hook \"${label}\" timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n Promise.resolve(p).then(\n (v) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(v);\n },\n (e) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(e);\n },\n );\n });\n}\n\nexport function createGracefulShutdown(\n options: GracefulShutdownOptions = {},\n): GracefulShutdown {\n const totalTimeoutMs = options.totalTimeoutMs ?? 30_000;\n const logger = options.logger ?? console;\n const exit = options.exit ?? ((code) => process.exit(code));\n const on =\n options.on ??\n ((signal, handler) => {\n process.on(signal, handler);\n });\n\n const hooks: ShutdownHook[] = [];\n let shuttingDown = false;\n let shutdownPromise: Promise<void> | undefined;\n\n async function runShutdown(reason: string, exitCode = 0): Promise<void> {\n if (shutdownPromise) return shutdownPromise;\n shuttingDown = true;\n logger.info('shutdown.start', { reason, hooks: hooks.length });\n\n shutdownPromise = (async () => {\n const startedAt = Date.now();\n // 总超时兜底:到点强制退出\n const killTimer = setTimeout(() => {\n logger.error('shutdown.timeout_force_exit', { totalTimeoutMs });\n exit(exitCode || 1);\n }, totalTimeoutMs);\n\n // 后注册的先关(出口先停,依赖后停)\n const ordered = [...hooks].reverse();\n for (const h of ordered) {\n const t = h.timeoutMs ?? 5_000;\n try {\n await withTimeout(h.fn(), t, h.name);\n logger.info('shutdown.hook.ok', { name: h.name });\n } catch (err) {\n logger.error('shutdown.hook.fail', {\n name: h.name,\n error: err instanceof Error ? err.message : String(err),\n });\n // 不抛出,继续关后续 hooks(best-effort)\n }\n }\n\n clearTimeout(killTimer);\n logger.info('shutdown.complete', { durationMs: Date.now() - startedAt });\n exit(exitCode);\n })();\n\n return shutdownPromise;\n }\n\n if (options.handleFatalErrors !== false) {\n process.on('uncaughtException', (err) => {\n logger.error('uncaughtException', { error: err.message, stack: err.stack });\n void runShutdown('uncaughtException', 1);\n });\n process.on('unhandledRejection', (reason) => {\n logger.error('unhandledRejection', {\n reason: reason instanceof Error ? reason.message : String(reason),\n });\n void runShutdown('unhandledRejection', 1);\n });\n }\n\n return {\n add(name, fn, timeoutMs) {\n if (shuttingDown) {\n logger.warn?.('shutdown.add_after_start_ignored', { name });\n return;\n }\n hooks.push({ name, fn, timeoutMs });\n },\n listen() {\n on('SIGTERM', () => void runShutdown('SIGTERM'));\n on('SIGINT', () => void runShutdown('SIGINT'));\n },\n trigger(reason) {\n return runShutdown(reason);\n },\n get isShuttingDown() {\n return shuttingDown;\n },\n };\n}\n","import type { ZodType, z } from 'zod';\n\n/**\n * 启动期环境变量校验。\n *\n * 设计原则:\n * - 缺关键变量直接挂(fail-fast),比启动后第一个请求才报错好\n * - 错误信息友好(指出具体哪个变量错了)\n * - 类型化输出,业务用 `env.JWT_SECRET` 直接拿到 string\n *\n * 用法:\n * ```ts\n * import { z } from 'zod';\n * import { loadEnv } from '@omni-api/core/utils';\n *\n * const Env = z.object({\n * PORT: z.coerce.number().default(3000),\n * JWT_SECRET: z.string().min(32, 'must be ≥ 32 chars'),\n * DATABASE_URL: z.string().url(),\n * });\n * export const env = loadEnv(Env);\n * ```\n */\nexport function loadEnv<T>(schema: ZodType<T>, source: Record<string, string | undefined> = process.env): T {\n const parsed = (schema as unknown as { safeParse: (v: unknown) => { success: boolean; data?: T; error?: { issues: Array<{ path: PropertyKey[]; message: string }> } } }).safeParse(source);\n if (parsed.success) return parsed.data as T;\n\n // 构造易读错误\n const issues = parsed.error?.issues ?? [];\n const lines = issues.map((i) => ` - ${i.path.join('.') || '(root)'}: ${i.message}`);\n const msg = `Invalid environment variables:\\n${lines.join('\\n')}`;\n // 直接 console.error + 抛错,让 process 自然退出(或被 try/catch 接住)\n console.error(msg);\n throw new Error(msg);\n}\n\n// 防止 unused\nexport type _ZodReexport<S extends ZodType> = z.infer<S>;\n","/**\n * createTracedFetch —— 带请求上下文的 fetch 包装。\n *\n * 自动注入:\n * - x-request-id:当前请求 ID(贯穿调用链)\n * - traceparent:W3C Trace Context(如果当前 ctx 有 / 或装了 OTel 能取到 active span)\n * - signal:当前请求的 AbortSignal(客户端断开 → 下游也取消)\n *\n * 业务用法:\n * const fetchx = createTracedFetch(ctx);\n * const res = await fetchx('https://internal-api/users/1');\n *\n * 设计:返回 fetch-compatible 函数,业务无需改动调用语义。\n */\n\nimport type { Context } from '../context.js';\n\nexport interface TracedFetchOptions {\n /** 自定义 fetch 实现(默认 globalThis.fetch) */\n fetch?: typeof fetch;\n /**\n * 额外要透传的请求头键名,从 ctx.state 中按 key 取(小写)。\n * 例如 ['x-tenant-id'] 会让租户 ID 自动透传到下游。\n */\n forwardStateKeys?: string[];\n}\n\n/**\n * 创建一个绑定到当前 ctx 的 fetch。\n *\n * 注意:返回值是普通 fetch 函数,可以直接传给任何接受 fetch 的 SDK\n * (比如 OpenAI SDK 的 `fetch` 选项)。\n */\nexport function createTracedFetch(\n ctx: Context,\n options: TracedFetchOptions = {},\n): typeof fetch {\n const baseFetch = options.fetch ?? globalThis.fetch;\n if (typeof baseFetch !== 'function') {\n throw new Error('createTracedFetch: globalThis.fetch is not available; provide options.fetch');\n }\n const forwardStateKeys = options.forwardStateKeys ?? [];\n\n // 用 fetch 类型包装:返回符合 fetch 契约的函数\n const tracedFetch: typeof fetch = async (input, init) => {\n const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));\n\n // 1. requestId 串联\n if (!headers.has('x-request-id')) {\n headers.set('x-request-id', ctx.requestId);\n }\n\n // 2. W3C traceparent\n const tp = pickTraceparent(ctx);\n if (tp && !headers.has('traceparent')) {\n headers.set('traceparent', tp);\n }\n\n // 3. 业务自定义透传字段(从 ctx.state 取)\n for (const key of forwardStateKeys) {\n const v = ctx.state[key];\n if (typeof v === 'string' && !headers.has(key)) {\n headers.set(key, v);\n }\n }\n\n // 4. AbortSignal 联动:把当前请求的 signal 加上(如果调用方没自带)\n const signal = init?.signal ?? ctx.signal;\n\n return baseFetch(input, {\n ...init,\n headers,\n signal,\n });\n };\n\n return tracedFetch;\n}\n\n/**\n * 从 ctx 拿到 traceparent。优先级:\n * 1. ctx.state.traceparent(HTTP Adapter 已经填好的入站值)\n * 2. OpenTelemetry active span(如果业务装了 @opentelemetry/api)\n * 3. undefined\n *\n * 这里用动态加载,没装 OTel 也能跑。\n */\nfunction pickTraceparent(ctx: Context): string | undefined {\n const fromState = ctx.state['traceparent'];\n if (typeof fromState === 'string') return fromState;\n\n // 尝试从 OTel 当前 span 取(synchronous,需 @opentelemetry/api 已加载)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const otel = (globalThis as any).__OMNI_OTEL_API__;\n if (otel?.trace?.getActiveSpan) {\n try {\n const span = otel.trace.getActiveSpan();\n const sc = span?.spanContext?.();\n if (sc?.traceId && sc?.spanId) {\n const flags = (sc.traceFlags ?? 0).toString(16).padStart(2, '0');\n return `00-${sc.traceId}-${sc.spanId}-${flags}`;\n }\n } catch {\n // ignore\n }\n }\n\n return undefined;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@omni-api/core",
3
+ "version": "0.0.1",
4
+ "description": "OmniAPI core: Procedure / Registry / Middleware / Context / Errors",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./utils": {
15
+ "types": "./dist/utils/index.d.ts",
16
+ "import": "./dist/utils/index.js"
17
+ }
18
+ },
19
+ "files": ["dist", "README.md"],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "typecheck": "tsc --noEmit",
25
+ "clean": "rm -rf dist .turbo coverage"
26
+ },
27
+ "dependencies": {
28
+ "zod": "^3.23.8",
29
+ "zod-to-json-schema": "^3.23.5"
30
+ },
31
+ "devDependencies": {
32
+ "tsup": "^8.3.0",
33
+ "typescript": "^5.7.0",
34
+ "vitest": "^2.1.0",
35
+ "@types/node": "^22.10.0"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }