aps-agent/server/core/control-plane/control-plane.ts

57 lines
2.2 KiB
TypeScript
Raw Permalink Normal View History

/**
* 控制面(A4):身份、策略、凭据、确认、审计五块绑在一起的入口。
*
* 它们不是五个独立功能,而是同一条链上的五道关:
* 身份(以谁的身份调) → 策略(这次调用允不允许) → 凭据(拿什么去调)
* → 确认(写操作要不要人点头) → 审计(发生后留痕)
* 任何一块缺失都会让「可控」变成空话,所以绑在一起注入 Core。
*
* 这一层与引擎无关:Pi 还是 Codex,走的都是 Core.invoke 这一个出口。
*/
import type { Capability } from '@aps/provider-sdk';
import type { AuditLog } from '../audit/log.js';
import type { CredentialBroker } from '../credentials/broker.js';
import type { IdentityContext } from '../identity/context.js';
import type { PendingActionStore } from '../approval/store.js';
import type { PolicyEngine } from '../policy/engine.js';
import { createRulePolicy } from '../policy/engine.js';
export interface ControlPlaneOptions {
identity: IdentityContext;
credentials: CredentialBroker;
approvals: PendingActionStore;
audit: AuditLog;
/** 能力可见性(A3.5):能力清单与这里必须是同一次过滤 */
isVisible: (capability: Capability, subject: { userId: string; tenantId: string }) => boolean;
policy?: PolicyEngine;
/** 确认卡有效期;超过就失效,必须重新发起 */
approvalTtlMs?: number;
now?: () => number;
}
export interface ControlPlane {
identity: IdentityContext;
credentials: CredentialBroker;
approvals: PendingActionStore;
audit: AuditLog;
policy: PolicyEngine;
isVisible: (capability: Capability, subject: { userId: string; tenantId: string }) => boolean;
approvalTtlMs: number;
now(): number;
}
const DEFAULT_APPROVAL_TTL_MS = 15 * 60_000;
export function createControlPlane(options: ControlPlaneOptions): ControlPlane {
return {
identity: options.identity,
credentials: options.credentials,
approvals: options.approvals,
audit: options.audit,
policy: options.policy ?? createRulePolicy(),
isVisible: options.isVisible,
approvalTtlMs: options.approvalTtlMs ?? DEFAULT_APPROVAL_TTL_MS,
now: options.now ?? Date.now,
};
}