Files
openclaw/extensions/buzz/src/directory.ts
Peter Steinberger 992948356f fix(secrets): fail closed for configured references (#127669)
* fix(secrets): fail closed for configured references

Keep explicit SecretRefs authoritative across Gateway auth, onboarding, probes, channel credentials, outbound sends, and GitHub Copilot discovery/embeddings. Ambient credentials remain available only when no reference owns the surface.

* test(secrets): align gateway fallback expectations

* test(providers): remove load-sensitive stream timing

* test(xai): normalize stream capture failures

* fix(copilot): preserve direct auth precedence

* test(ollama): keep progressing streams alive

* test(models): mark resolved config fixtures

* fix(models): drop stale probe import
2026-08-21 18:00:28 -07:00

143 lines
4.5 KiB
TypeScript

import type {
ChannelDirectoryEntry,
DirectoryConfigParams,
} from "openclaw/plugin-sdk/directory-runtime";
import { queryBuzzDirectoryProfiles, queryBuzzDirectoryRooms } from "./directory-relay.js";
import { BuzzDirectoryState } from "./directory-state.js";
import { getActiveBuzzBus } from "./gateway.js";
import { connectAuthenticatedBuzzRelaySession, parseBuzzAuthTag } from "./relay-auth.js";
import { queryBuzzRoomMemberships } from "./room-membership-query.js";
import { parseBuzzTarget } from "./target.js";
import { assertBuzzAccountAvailable, decodeBuzzPrivateKey, resolveBuzzAccount } from "./types.js";
const DIRECTORY_LIVE_TIMEOUT_MS = 10_000;
function resolveConfiguredRoomIds(account: ReturnType<typeof resolveBuzzAccount>): string[] {
return Object.entries(account.config.groups ?? {})
.filter(([, config]) => config.enabled !== false)
.map(([roomId]) => parseBuzzTarget(roomId));
}
function createConfiguredDirectoryState(params: DirectoryConfigParams): {
account: ReturnType<typeof resolveBuzzAccount>;
channelIds: string[];
state: BuzzDirectoryState;
} | null {
const account = resolveBuzzAccount({ cfg: params.cfg, accountId: params.accountId });
assertBuzzAccountAvailable(account);
if (!account.publicKey) {
return null;
}
const channelIds = resolveConfiguredRoomIds(account);
return {
account,
channelIds,
state: new BuzzDirectoryState({
publicKey: account.publicKey,
fallbackProfileName: account.name ?? "OpenClaw",
channelIds,
}),
};
}
async function loadBuzzDirectoryState(
params: DirectoryConfigParams,
options: { refreshRooms: boolean },
): Promise<BuzzDirectoryState | null> {
const configured = createConfiguredDirectoryState(params);
if (!configured || !configured.account.configured || configured.channelIds.length === 0) {
return configured?.state ?? null;
}
const activeBus = getActiveBuzzBus(configured.account.accountId);
if (activeBus) {
if (options.refreshRooms) {
try {
await activeBus.refreshDirectory();
} catch {
// A stalled metadata refresh recycles the relay session. Directory
// reads can still return the last complete in-memory snapshot.
}
}
return activeBus.directory;
}
const timeoutSignal = AbortSignal.timeout(DIRECTORY_LIVE_TIMEOUT_MS);
const { relay, relayPublicKey } = await connectAuthenticatedBuzzRelaySession({
relayUrl: configured.account.relayUrl,
secretKey: decodeBuzzPrivateKey(configured.account.privateKey),
authTag: parseBuzzAuthTag(configured.account.authTag),
signal: timeoutSignal,
});
try {
await queryBuzzDirectoryRooms({
relay,
relayPublicKey,
state: configured.state,
channelIds: configured.channelIds,
signal: timeoutSignal,
});
const activeChannelIds = configured.state.activeRoomIds();
configured.state.replaceMemberships(
activeChannelIds.length > 0
? await queryBuzzRoomMemberships({
relay,
relayPublicKey,
channelIds: activeChannelIds,
signal: timeoutSignal,
})
: new Map(),
);
await queryBuzzDirectoryProfiles({
relay,
state: configured.state,
publicKeys: configured.state.profilePublicKeys(),
signal: timeoutSignal,
});
return configured.state;
} finally {
relay.close();
}
}
export async function getBuzzDirectorySelf(
params: DirectoryConfigParams,
): Promise<ChannelDirectoryEntry | null> {
return (await loadBuzzDirectoryState(params, { refreshRooms: false }))?.self() ?? null;
}
export async function listBuzzDirectoryPeersLive(
params: DirectoryConfigParams,
): Promise<ChannelDirectoryEntry[]> {
return (
(await loadBuzzDirectoryState(params, { refreshRooms: false }))?.listPeers({
query: params.query,
limit: params.limit,
}) ?? []
);
}
export async function listBuzzDirectoryGroupsLive(
params: DirectoryConfigParams,
): Promise<ChannelDirectoryEntry[]> {
return (
(await loadBuzzDirectoryState(params, { refreshRooms: true }))?.listGroups({
query: params.query,
limit: params.limit,
}) ?? []
);
}
export async function listBuzzDirectoryGroupMembers(params: {
cfg: DirectoryConfigParams["cfg"];
accountId?: string | null;
groupId: string;
limit?: number | null;
}): Promise<ChannelDirectoryEntry[]> {
return (
(await loadBuzzDirectoryState(params, { refreshRooms: false }))?.listGroupMembers({
groupId: params.groupId,
limit: params.limit,
}) ?? []
);
}