mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-07-02 10:33:00 +08:00
feat: add Hermes provider
This commit is contained in:
@@ -5,6 +5,10 @@ import type {
|
||||
McpScope,
|
||||
NormalizedMessage,
|
||||
ProviderSkill,
|
||||
ProviderSkillRegistryActionResult,
|
||||
ProviderSkillRegistryInstallInput,
|
||||
ProviderSkillRegistrySearchOptions,
|
||||
ProviderSkillRegistrySearchResult,
|
||||
ProviderSkillListOptions,
|
||||
ProviderAuthStatus,
|
||||
ProviderChangeActiveModelInput,
|
||||
@@ -116,6 +120,21 @@ export interface IProviderSkills {
|
||||
removeSkill(
|
||||
input: ProviderSkillRemoveInput,
|
||||
): Promise<{ removed: boolean; provider: LLMProvider; directoryName: string }>;
|
||||
|
||||
searchRegistry?(
|
||||
query: string,
|
||||
options?: ProviderSkillRegistrySearchOptions,
|
||||
): Promise<ProviderSkillRegistrySearchResult[]>;
|
||||
|
||||
installRegistrySkill?(input: ProviderSkillRegistryInstallInput): Promise<ProviderSkillRegistryActionResult>;
|
||||
|
||||
uninstallRegistrySkill?(name: string): Promise<ProviderSkillRegistryActionResult>;
|
||||
|
||||
checkRegistryUpdates?(): Promise<ProviderSkillRegistryActionResult>;
|
||||
|
||||
updateRegistrySkills?(): Promise<ProviderSkillRegistryActionResult>;
|
||||
|
||||
auditRegistrySkills?(): Promise<ProviderSkillRegistryActionResult>;
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
|
||||
83
server/shared/tool-approval-registry.js
Normal file
83
server/shared/tool-approval-registry.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const pendingApprovals = new Map();
|
||||
const APPROVAL_MAX_AGE_MS = 30 * 60 * 1000;
|
||||
|
||||
// Drop approvals whose run died without resolving them (WS disconnect, process
|
||||
// crash) so their captured payloads/closures don't accumulate unbounded.
|
||||
function sweepExpiredApprovals(now = Date.now()) {
|
||||
for (const [requestId, entry] of pendingApprovals) {
|
||||
const receivedAt = entry.receivedAt instanceof Date ? entry.receivedAt.getTime() : 0;
|
||||
if (receivedAt && now - receivedAt > APPROVAL_MAX_AGE_MS) {
|
||||
pendingApprovals.delete(requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearApprovalsForSession(sessionId) {
|
||||
if (!sessionId) {
|
||||
return;
|
||||
}
|
||||
for (const [requestId, entry] of pendingApprovals) {
|
||||
if (entry.sessionId === sessionId) {
|
||||
pendingApprovals.delete(requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function registerApproval(requestId, { resolver, sessionId = null, provider = null, meta = {} } = {}) {
|
||||
if (!requestId || typeof resolver !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
sweepExpiredApprovals();
|
||||
|
||||
pendingApprovals.set(requestId, {
|
||||
resolver,
|
||||
sessionId,
|
||||
provider,
|
||||
meta,
|
||||
receivedAt: meta.receivedAt || meta._receivedAt || new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
function unregisterApproval(requestId) {
|
||||
pendingApprovals.delete(requestId);
|
||||
}
|
||||
|
||||
function resolveToolApproval(requestId, decision) {
|
||||
const entry = pendingApprovals.get(requestId);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entry.resolver(decision);
|
||||
return true;
|
||||
}
|
||||
|
||||
function getPendingApprovalsForSession(sessionId) {
|
||||
const pending = [];
|
||||
for (const [requestId, entry] of pendingApprovals.entries()) {
|
||||
if (entry.sessionId !== sessionId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pending.push({
|
||||
requestId,
|
||||
toolName: entry.meta.toolName || entry.meta._toolName || 'UnknownTool',
|
||||
input: entry.meta.input ?? entry.meta._input,
|
||||
context: entry.meta.context ?? entry.meta._context,
|
||||
sessionId,
|
||||
provider: entry.provider,
|
||||
receivedAt: entry.receivedAt,
|
||||
});
|
||||
}
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
export {
|
||||
registerApproval,
|
||||
unregisterApproval,
|
||||
resolveToolApproval,
|
||||
getPendingApprovalsForSession,
|
||||
clearApprovalsForSession,
|
||||
};
|
||||
@@ -65,7 +65,7 @@ export type AuthenticatedWebSocketRequest = IncomingMessage & {
|
||||
* Use this as the source of truth whenever a function or payload needs to identify
|
||||
* a specific LLM integration.
|
||||
*/
|
||||
export type LLMProvider = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode';
|
||||
export type LLMProvider = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'hermes';
|
||||
|
||||
/**
|
||||
* One selectable model row in a provider model catalog.
|
||||
@@ -365,6 +365,32 @@ export type ProviderSkillRemoveInput = {
|
||||
directoryName: string;
|
||||
};
|
||||
|
||||
export type ProviderSkillRegistrySearchOptions = {
|
||||
source?: string;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
export type ProviderSkillRegistrySearchResult = {
|
||||
name: string;
|
||||
identifier: string;
|
||||
source?: string;
|
||||
trustLevel?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type ProviderSkillRegistryInstallInput = {
|
||||
identifier: string;
|
||||
category?: string;
|
||||
name?: string;
|
||||
force?: boolean;
|
||||
};
|
||||
|
||||
export type ProviderSkillRegistryActionResult = {
|
||||
ok: boolean;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalized skill record returned by provider skill adapters.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user