|
|
@@ -6,6 +6,7 @@ import { AppError, badRequest, notFound } from '../lib/errors'
|
|
|
import type {
|
|
|
PluginInstallResponse, PluginInstalledResponse, PluginListResponse, PluginMarketplaceEntry, PluginReadResponse, PluginSummary,
|
|
|
} from '../providers/codex/protocol/v2'
|
|
|
+import { RpcRequestError } from '../providers/codex/rpc'
|
|
|
|
|
|
const CACHE_MS = 10 * 60_000
|
|
|
const PAGE_MAX = 100
|
|
|
@@ -32,8 +33,14 @@ export class CodexPluginService {
|
|
|
return c
|
|
|
}
|
|
|
|
|
|
- private call<T>(method: string, params: unknown, timeoutMs = 60_000): Promise<T> {
|
|
|
- return this.rpc().request<T>(this.settings(), method, params, timeoutMs)
|
|
|
+ /** Codex 返回的错误(如远程目录请求失败)原样转给后台,而不是笼统的"服务器内部错误" */
|
|
|
+ private async call<T>(method: string, params: unknown, timeoutMs = 60_000): Promise<T> {
|
|
|
+ try {
|
|
|
+ return await this.rpc().request<T>(this.settings(), method, params, timeoutMs)
|
|
|
+ } catch (err) {
|
|
|
+ if (err instanceof RpcRequestError) throw new AppError(502, 'codex_error', `Codex:${err.rpc.message}`)
|
|
|
+ throw err
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async catalog(q: { query?: string; category?: string; installed?: boolean; offset?: number; limit?: number; refresh?: boolean }): Promise<CodexPluginCatalogDto> {
|
|
|
@@ -80,7 +87,10 @@ export class CodexPluginService {
|
|
|
async install(name: string): Promise<CodexPluginInstallResultDto> {
|
|
|
const p = await this.find(name)
|
|
|
if (p.availability !== 'AVAILABLE' || p.installPolicy === 'NOT_AVAILABLE') throw badRequest(`插件 ${p.displayName} 当前不可安装`)
|
|
|
- const r = await this.call<PluginInstallResponse>('plugin/install', { remoteMarketplaceName: p.marketplace, pluginName: p.name }, 300_000)
|
|
|
+ // 远程插件按 remotePluginId 安装:目录里的名称(如 clickup)在远程安装接口上会返回 404 Plugin not found
|
|
|
+ const read = await this.call<PluginReadResponse>('plugin/read', { remoteMarketplaceName: p.marketplace, pluginName: p.name })
|
|
|
+ const pluginName = read.plugin.summary.remotePluginId ?? p.name
|
|
|
+ const r = await this.call<PluginInstallResponse>('plugin/install', { remoteMarketplaceName: p.marketplace, pluginName }, 300_000)
|
|
|
this.cache = null
|
|
|
this.audit(`安装 Codex 插件 ${p.displayName}(${p.id})`)
|
|
|
return { appsNeedingAuth: r.appsNeedingAuth.map(a => ({ id: a.id, name: a.name, installUrl: a.installUrl })) }
|