aps-agent/server/core/registry/registry.ts

82 lines
3.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 能力目录(架构设计 2 / 4:registry 模块)。
*
* 保存已注册 Provider 与它们声明的能力;上层按身份取「能力清单」(A4 起按策略过滤,
* 当前只做注册与查找)。能力目录是唯一来源——引擎的工具列表是它的投影。
*/
import type { Capability, Provider, ProviderManifest } from '@aps/provider-sdk';
export interface RegisteredProvider {
manifest: ProviderManifest;
provider: Provider;
}
export interface ResolvedCapability {
capability: Capability;
provider: Provider;
providerId: string;
/** 该系统是否需要平台用户绑定账号(A4.1);缺省 true */
requiresIdentity: boolean;
/** 登录方式声明(ADR-0012);没声明表示该系统没有账号概念 */
authKind?: 'oauth' | 'password-exchange' | 'apikey';
}
export class CapabilityRegistry {
private readonly providers = new Map<string, RegisteredProvider>();
async register(provider: Provider): Promise<ProviderManifest> {
const manifest = await provider.describe();
if (manifest.platformVersion !== 1) {
throw new Error(`Provider ${manifest.id} 声明兼容平台版本 ${manifest.platformVersion},当前平台只支持 1`);
}
this.providers.set(manifest.id, { manifest, provider });
return manifest;
}
list(): Capability[] {
return [...this.providers.values()].flatMap((entry) => entry.manifest.capabilities);
}
resolve(capabilityId: string): ResolvedCapability | undefined {
for (const entry of this.providers.values()) {
const capability = entry.manifest.capabilities.find((item) => item.id === capabilityId);
if (capability) {
return {
capability,
provider: entry.provider,
providerId: entry.manifest.id,
requiresIdentity: entry.manifest.requiresIdentity !== false,
...(entry.manifest.auth === undefined ? {} : { authKind: entry.manifest.auth.kind }),
};
}
}
return undefined;
}
/** 某个 Provider 的 manifest(绑定流程要读它的 auth 声明) */
manifest(providerId: string): ProviderManifest | undefined {
return this.providers.get(providerId)?.manifest;
}
/** 某个 Provider 实例(绑定流程要用它的 exchange / checkCredential) */
provider(providerId: string): Provider | undefined {
return this.providers.get(providerId)?.provider;
}
providerIds(): string[] {
return [...this.providers.keys()];
}
async health(): Promise<Array<{ providerId: string; status: string; detail?: string }>> {
const results: Array<{ providerId: string; status: string; detail?: string }> = [];
for (const [providerId, entry] of this.providers) {
try {
const health = await entry.provider.health();
results.push(health.detail === undefined ? { providerId, status: health.status } : { providerId, status: health.status, detail: health.detail });
} catch (error) {
results.push({ providerId, status: 'down', detail: error instanceof Error ? error.message : '健康探测失败' });
}
}
return results;
}
}