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

82 lines
3.1 KiB
TypeScript
Raw Normal View History

/**
* 能力目录(架构设计 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;
}
}