pi-model-manager 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/NOTICE +8 -0
- package/README.en.md +206 -0
- package/README.md +210 -0
- package/atomic-write.ts +100 -0
- package/builtin-model-catalog.ts +92 -0
- package/claude-code-compat.ts +56 -0
- package/common.ts +66 -0
- package/compat-settings.ts +25 -0
- package/config-value-reference.ts +51 -0
- package/configuration-persistence.ts +72 -0
- package/header-profile-mutations.ts +46 -0
- package/index.ts +82 -0
- package/local-proxy-service.ts +313 -0
- package/model-mutations.ts +210 -0
- package/models-json-manager.ts +238 -0
- package/models-json-mutations.ts +90 -0
- package/models-json-sync.ts +386 -0
- package/openai-responses-payload.ts +80 -0
- package/openai-service-tier.ts +36 -0
- package/package.json +74 -0
- package/presets/builtin-client-headers.ts +23 -0
- package/presets/client-headers.ts +126 -0
- package/presets/providers.ts +80 -0
- package/presets/thinking.ts +81 -0
- package/provider-registrar.ts +281 -0
- package/request-pipeline.ts +88 -0
- package/rescue.ts +69 -0
- package/runtime-base-url.ts +54 -0
- package/state-cache.ts +55 -0
- package/state-document.ts +569 -0
- package/state-metadata-store.ts +455 -0
- package/state-store.ts +238 -0
- package/tui/dashboard.ts +376 -0
- package/tui/editor-model.ts +339 -0
- package/tui/editor-provider.ts +219 -0
- package/tui/header-profiles-panel.ts +307 -0
- package/tui/model-list-fetch.ts +259 -0
- package/tui/model-picker.ts +266 -0
- package/tui/models-json-panel.ts +269 -0
- package/tui/persistent-menu.ts +349 -0
- package/tui/ui-helpers.ts +289 -0
- package/types.ts +165 -0
package/tui/dashboard.ts
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// tui/dashboard.ts
|
|
2
|
+
//
|
|
3
|
+
// 主 dashboard:列出 state 中所有接入/模型,操作通过快捷键触发。
|
|
4
|
+
//
|
|
5
|
+
// 流程:
|
|
6
|
+
// dashboard → n → 接入编辑器 → 模型编辑器 → save & register
|
|
7
|
+
// dashboard → [接入行] → 接入菜单 → Enter 编辑模型 / a 添加模型 / e 编辑接入
|
|
8
|
+
// dashboard → h → 请求头面板
|
|
9
|
+
//
|
|
10
|
+
// 保存/删除事务委托给 model-mutations.ts,dashboard 只保留交互与校验流程。
|
|
11
|
+
|
|
12
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { formatUnknownError } from "../common.ts";
|
|
14
|
+
import {
|
|
15
|
+
deleteModelConfiguration,
|
|
16
|
+
deleteProviderConfiguration,
|
|
17
|
+
saveModelConfiguration,
|
|
18
|
+
saveNewProviderWithModelConfiguration,
|
|
19
|
+
saveProviderConfiguration,
|
|
20
|
+
} from "../model-mutations.ts";
|
|
21
|
+
import {
|
|
22
|
+
createModelDraftForStoredProvider,
|
|
23
|
+
createModelDraftFromStoredModel,
|
|
24
|
+
createProviderDraft,
|
|
25
|
+
createProviderDraftFromStored,
|
|
26
|
+
getModelFullId,
|
|
27
|
+
getProviderChangeWarnings,
|
|
28
|
+
getProviderDisplayName,
|
|
29
|
+
upsertProviderInDocument,
|
|
30
|
+
validateModelDraft,
|
|
31
|
+
validateProviderDraft,
|
|
32
|
+
} from "../state-document.ts";
|
|
33
|
+
import { readState } from "../state-store.ts";
|
|
34
|
+
import { getBuiltinProviderIds } from "../builtin-model-catalog.ts";
|
|
35
|
+
import type { StateDocument, StoredModel, StoredProvider } from "../types.ts";
|
|
36
|
+
import { editModel } from "./editor-model.ts";
|
|
37
|
+
import { editProvider } from "./editor-provider.ts";
|
|
38
|
+
import { showPersistentShortcutMenu, type MenuCursor } from "./persistent-menu.ts";
|
|
39
|
+
import {
|
|
40
|
+
PROVIDER_CONSOLE_HEADER,
|
|
41
|
+
formatModelListHeader,
|
|
42
|
+
formatModelListRow,
|
|
43
|
+
formatProviderConsoleRow,
|
|
44
|
+
formatProviderDetailLines,
|
|
45
|
+
formatProviderEndpointLine,
|
|
46
|
+
formatProviderSummaryLine,
|
|
47
|
+
} from "./ui-helpers.ts";
|
|
48
|
+
import { runHeaderProfilesPanel } from "./header-profiles-panel.ts";
|
|
49
|
+
|
|
50
|
+
interface DashboardRow {
|
|
51
|
+
providerId: string;
|
|
52
|
+
label: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ProviderMenuRow {
|
|
56
|
+
modelId: string;
|
|
57
|
+
model: StoredModel;
|
|
58
|
+
label: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function buildRows(document: StateDocument): DashboardRow[] {
|
|
62
|
+
return Object.keys(document.providers)
|
|
63
|
+
.sort((a, b) => a.localeCompare(b))
|
|
64
|
+
.map((providerId) => ({
|
|
65
|
+
providerId,
|
|
66
|
+
label: formatProviderConsoleRow(providerId, document.providers[providerId]!, document.requestHeaderProfiles),
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildProviderMenuRows(provider: StoredProvider): ProviderMenuRow[] {
|
|
71
|
+
return [...provider.models]
|
|
72
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
73
|
+
.map((model) => ({
|
|
74
|
+
modelId: model.id,
|
|
75
|
+
model,
|
|
76
|
+
label: formatModelListRow(provider, model),
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function notifyValidationErrors(ctx: ExtensionCommandContext, title: string, errors: string[]): void {
|
|
81
|
+
ctx.ui.notify(`${title}:\n${errors.map((e) => `- ${e}`).join("\n")}`, "warning");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function confirmWarnings(ctx: ExtensionCommandContext, warnings: string[]): Promise<boolean> {
|
|
85
|
+
if (warnings.length === 0) return true;
|
|
86
|
+
return ctx.ui.confirm("请确认改动", `${warnings.join("\n")}\n\n继续保存?`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
// ========== save 流程 ==========
|
|
91
|
+
|
|
92
|
+
async function saveProviderDraft(
|
|
93
|
+
pi: ExtensionAPI,
|
|
94
|
+
ctx: ExtensionCommandContext,
|
|
95
|
+
draft: ReturnType<typeof createProviderDraft>,
|
|
96
|
+
oldProviderId: string | undefined,
|
|
97
|
+
): Promise<boolean> {
|
|
98
|
+
const state = await readState();
|
|
99
|
+
const builtInIds = await getBuiltinProviderIds();
|
|
100
|
+
const errors = validateProviderDraft(draft, state, builtInIds, oldProviderId);
|
|
101
|
+
if (errors.length > 0) {
|
|
102
|
+
notifyValidationErrors(ctx, "接入配置无效", errors);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const current = oldProviderId ? state.providers[oldProviderId] : state.providers[draft.providerId];
|
|
106
|
+
if (!(await confirmWarnings(ctx, getProviderChangeWarnings(current, draft)))) return false;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await saveProviderConfiguration(pi, ctx, state, draft, oldProviderId);
|
|
110
|
+
return true;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
ctx.ui.notify(`保存失败:${formatUnknownError(error)}`, "error");
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function saveModelDraft(
|
|
118
|
+
pi: ExtensionAPI,
|
|
119
|
+
ctx: ExtensionCommandContext,
|
|
120
|
+
draft: ReturnType<typeof createModelDraftForStoredProvider>,
|
|
121
|
+
replacedModelId: string | undefined,
|
|
122
|
+
): Promise<boolean> {
|
|
123
|
+
const state = await readState();
|
|
124
|
+
const errors = validateModelDraft(draft, state, replacedModelId);
|
|
125
|
+
if (errors.length > 0) {
|
|
126
|
+
notifyValidationErrors(ctx, "模型配置无效", errors);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
await saveModelConfiguration(pi, ctx, state, draft, replacedModelId);
|
|
131
|
+
return true;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
ctx.ui.notify(`保存失败:${formatUnknownError(error)}`, "error");
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function saveNewProviderAndModelDraft(
|
|
139
|
+
pi: ExtensionAPI,
|
|
140
|
+
ctx: ExtensionCommandContext,
|
|
141
|
+
providerDraft: ReturnType<typeof createProviderDraft>,
|
|
142
|
+
modelDraft: ReturnType<typeof createModelDraftForStoredProvider>,
|
|
143
|
+
): Promise<boolean> {
|
|
144
|
+
const state = await readState();
|
|
145
|
+
const builtInIds = await getBuiltinProviderIds();
|
|
146
|
+
const providerErrors = validateProviderDraft(providerDraft, state, builtInIds, undefined);
|
|
147
|
+
if (providerErrors.length > 0) {
|
|
148
|
+
notifyValidationErrors(ctx, "接入配置无效", providerErrors);
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const providerState = upsertProviderInDocument(state, undefined, providerDraft);
|
|
152
|
+
const modelErrors = validateModelDraft(modelDraft, providerState, undefined);
|
|
153
|
+
if (modelErrors.length > 0) {
|
|
154
|
+
notifyValidationErrors(ctx, "模型配置无效", modelErrors);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
await saveNewProviderWithModelConfiguration(pi, ctx, providerState, providerDraft, modelDraft);
|
|
159
|
+
return true;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
ctx.ui.notify(`保存失败:${formatUnknownError(error)}`, "error");
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ========== delete 流程 ==========
|
|
167
|
+
|
|
168
|
+
async function deleteProvider(pi: ExtensionAPI, ctx: ExtensionCommandContext, providerId: string, provider: StoredProvider): Promise<boolean> {
|
|
169
|
+
const ok = await ctx.ui.confirm(
|
|
170
|
+
`删除接入 ${providerId}`,
|
|
171
|
+
`将删除 ${getProviderDisplayName(providerId, provider)} 下全部 ${provider.models.length} 个模型。`,
|
|
172
|
+
);
|
|
173
|
+
if (!ok) return false;
|
|
174
|
+
try {
|
|
175
|
+
await deleteProviderConfiguration(pi, ctx, providerId, provider);
|
|
176
|
+
return true;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
ctx.ui.notify(`删除失败:${formatUnknownError(error)}`, "error");
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function deleteModel(pi: ExtensionAPI, ctx: ExtensionCommandContext, providerId: string, modelId: string): Promise<boolean> {
|
|
184
|
+
const fullId = getModelFullId(providerId, modelId);
|
|
185
|
+
const stateForPrompt = await readState();
|
|
186
|
+
const currentProvider = stateForPrompt.providers[providerId];
|
|
187
|
+
const removesLastModel = (currentProvider?.models.length ?? 0) <= 1;
|
|
188
|
+
const ok = await ctx.ui.confirm(
|
|
189
|
+
`删除模型 ${fullId}`,
|
|
190
|
+
removesLastModel ? "这是该接入下最后一个模型;删除后接入也会从 models.json 中移除。" : "只删除该模型,接入保留。",
|
|
191
|
+
);
|
|
192
|
+
if (!ok) return false;
|
|
193
|
+
try {
|
|
194
|
+
await deleteModelConfiguration(pi, ctx, providerId, modelId);
|
|
195
|
+
return true;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
ctx.ui.notify(`删除失败:${formatUnknownError(error)}`, "error");
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ========== 子菜单 ==========
|
|
203
|
+
|
|
204
|
+
type ProviderShortcut = "add-model" | "edit-provider" | "delete-model";
|
|
205
|
+
|
|
206
|
+
async function editStoredModel(
|
|
207
|
+
pi: ExtensionAPI,
|
|
208
|
+
ctx: ExtensionCommandContext,
|
|
209
|
+
providerId: string,
|
|
210
|
+
provider: StoredProvider,
|
|
211
|
+
modelId: string,
|
|
212
|
+
requestHeaderProfiles: StateDocument["requestHeaderProfiles"],
|
|
213
|
+
clientHeaderCaptures: StateDocument["clientHeaderCaptures"],
|
|
214
|
+
): Promise<boolean> {
|
|
215
|
+
const model = provider.models.find((m) => m.id === modelId);
|
|
216
|
+
if (!model) {
|
|
217
|
+
ctx.ui.notify(`模型不存在:${getModelFullId(providerId, modelId)}`, "error");
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const draft = createModelDraftFromStoredModel(providerId, provider, model);
|
|
221
|
+
const outcome = await editModel(ctx, draft, `编辑模型 ${getModelFullId(providerId, modelId)}`, requestHeaderProfiles, clientHeaderCaptures);
|
|
222
|
+
if (outcome.action !== "save") return false;
|
|
223
|
+
return saveModelDraft(pi, ctx, outcome.draft, modelId);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function showProviderMenu(pi: ExtensionAPI, ctx: ExtensionCommandContext, providerId: string): Promise<boolean> {
|
|
227
|
+
const cursor: MenuCursor = { index: 0 };
|
|
228
|
+
while (true) {
|
|
229
|
+
const state = await readState();
|
|
230
|
+
const currentProvider = state.providers[providerId];
|
|
231
|
+
if (!currentProvider) {
|
|
232
|
+
ctx.ui.notify(`接入配置已不存在:${providerId}`, "warning");
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
const rows = buildProviderMenuRows(currentProvider);
|
|
236
|
+
const action = await showPersistentShortcutMenu<ProviderShortcut>(
|
|
237
|
+
ctx,
|
|
238
|
+
`/model-manager / ${getProviderDisplayName(providerId, currentProvider)}`,
|
|
239
|
+
"",
|
|
240
|
+
rows.map((row, index) => ({ id: `${index}`, label: row.label })),
|
|
241
|
+
cursor,
|
|
242
|
+
[
|
|
243
|
+
{ input: "a", shortcut: "add-model" },
|
|
244
|
+
{ input: "e", shortcut: "edit-provider" },
|
|
245
|
+
{ input: "d", shortcut: "delete-model" },
|
|
246
|
+
],
|
|
247
|
+
{
|
|
248
|
+
summaryLines: [
|
|
249
|
+
formatProviderSummaryLine(currentProvider, state.requestHeaderProfiles),
|
|
250
|
+
formatProviderEndpointLine(currentProvider),
|
|
251
|
+
],
|
|
252
|
+
tableHeader: formatModelListHeader(currentProvider),
|
|
253
|
+
visibleRows: Math.min(12, Math.max(1, rows.length)),
|
|
254
|
+
footer: "↑↓ 选择 Enter 编辑模型 a 添加模型 e 编辑接入 d 删除模型 Esc 返回",
|
|
255
|
+
emptyLabel: "暂无模型;按 a 添加第一个模型",
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
if (action.type === "cancel") return false;
|
|
259
|
+
if (action.type === "shortcut") {
|
|
260
|
+
if (action.shortcut === "add-model") {
|
|
261
|
+
const draft = createModelDraftForStoredProvider(providerId, currentProvider);
|
|
262
|
+
const outcome = await editModel(ctx, draft, `添加模型到 ${providerId}`, state.requestHeaderProfiles, state.clientHeaderCaptures);
|
|
263
|
+
if (outcome.action === "save") await saveModelDraft(pi, ctx, outcome.draft, undefined);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (action.shortcut === "edit-provider") {
|
|
267
|
+
const draft = createProviderDraftFromStored(providerId, currentProvider);
|
|
268
|
+
const outcome = await editProvider(ctx, draft, `编辑接入 ${providerId}`, state.requestHeaderProfiles);
|
|
269
|
+
if (outcome.action === "save") {
|
|
270
|
+
await saveProviderDraft(pi, ctx, outcome.draft, providerId);
|
|
271
|
+
if (outcome.draft.providerId !== providerId) return true;
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const selectedModelId = rows[cursor.index]?.modelId;
|
|
276
|
+
if (!selectedModelId) {
|
|
277
|
+
ctx.ui.notify("没有可删除的模型;删除接入请返回上一级按 d。", "info");
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
await deleteModel(pi, ctx, providerId, selectedModelId);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const row = rows[Number.parseInt(action.id, 10)];
|
|
284
|
+
if (!row) return false;
|
|
285
|
+
await editStoredModel(pi, ctx, providerId, currentProvider, row.modelId, state.requestHeaderProfiles, state.clientHeaderCaptures);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function createProviderAndModel(pi: ExtensionAPI, ctx: ExtensionCommandContext): Promise<boolean> {
|
|
290
|
+
const stateBeforeEdit = await readState();
|
|
291
|
+
const builtInIds = await getBuiltinProviderIds();
|
|
292
|
+
const providerDraft = createProviderDraft();
|
|
293
|
+
providerDraft.providerName = "";
|
|
294
|
+
while (true) {
|
|
295
|
+
const providerOutcome = await editProvider(
|
|
296
|
+
ctx,
|
|
297
|
+
providerDraft,
|
|
298
|
+
"新建接入配置",
|
|
299
|
+
stateBeforeEdit.requestHeaderProfiles,
|
|
300
|
+
);
|
|
301
|
+
if (providerOutcome.action !== "save") return false;
|
|
302
|
+
const providerErrors = validateProviderDraft(providerOutcome.draft, stateBeforeEdit, builtInIds, undefined);
|
|
303
|
+
if (providerErrors.length > 0) {
|
|
304
|
+
notifyValidationErrors(ctx, "接入配置无效", providerErrors);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const stagedState = upsertProviderInDocument(stateBeforeEdit, undefined, providerOutcome.draft);
|
|
309
|
+
const stored = stagedState.providers[providerOutcome.draft.providerId]!;
|
|
310
|
+
ctx.ui.notify("请继续添加第一个模型;取消模型编辑则不会创建该接入。", "info");
|
|
311
|
+
const modelDraft = createModelDraftForStoredProvider(providerOutcome.draft.providerId, stored);
|
|
312
|
+
const modelOutcome = await editModel(ctx, modelDraft, `添加模型到 ${providerOutcome.draft.providerId}`, stagedState.requestHeaderProfiles, stagedState.clientHeaderCaptures);
|
|
313
|
+
if (modelOutcome.action !== "save") return false;
|
|
314
|
+
return saveNewProviderAndModelDraft(pi, ctx, providerOutcome.draft, modelOutcome.draft);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ========== 入口 ==========
|
|
319
|
+
|
|
320
|
+
type DashboardShortcut = "new-provider" | "header-profiles" | "delete-provider";
|
|
321
|
+
|
|
322
|
+
export async function runDashboard(pi: ExtensionAPI, ctx: ExtensionCommandContext): Promise<void> {
|
|
323
|
+
const cursor: MenuCursor = { index: 0 };
|
|
324
|
+
while (true) {
|
|
325
|
+
const state = await readState();
|
|
326
|
+
const rows = buildRows(state);
|
|
327
|
+
const modelCount = Object.values(state.providers).reduce((sum, provider) => sum + provider.models.length, 0);
|
|
328
|
+
const profileCount = Object.keys(state.requestHeaderProfiles).length;
|
|
329
|
+
const captureCount = Object.keys(state.clientHeaderCaptures).length;
|
|
330
|
+
const menuRows = rows.map((r, index) => ({ id: `${index}`, label: r.label }));
|
|
331
|
+
const action = await showPersistentShortcutMenu<DashboardShortcut>(
|
|
332
|
+
ctx,
|
|
333
|
+
"/model-manager",
|
|
334
|
+
"",
|
|
335
|
+
menuRows,
|
|
336
|
+
cursor,
|
|
337
|
+
[
|
|
338
|
+
{ input: "n", shortcut: "new-provider" },
|
|
339
|
+
{ input: "h", shortcut: "header-profiles" },
|
|
340
|
+
{ input: "d", shortcut: "delete-provider" },
|
|
341
|
+
],
|
|
342
|
+
{
|
|
343
|
+
summaryLines: [`${rows.length} 接入 · ${modelCount} 模型 · ${captureCount} 内置抓包 · ${profileCount} 自定义请求头`],
|
|
344
|
+
tableHeader: PROVIDER_CONSOLE_HEADER,
|
|
345
|
+
getDetailLines: (selectedRow) => {
|
|
346
|
+
const row = rows[Number.parseInt(selectedRow?.id ?? "", 10)];
|
|
347
|
+
const provider = row ? state.providers[row.providerId] : undefined;
|
|
348
|
+
return row && provider ? formatProviderDetailLines(row.providerId, provider, state.requestHeaderProfiles) : [];
|
|
349
|
+
},
|
|
350
|
+
footer: "↑↓ 选择 Enter 进入 n 新建接入 d 删除接入 h 请求头 Esc 退出",
|
|
351
|
+
emptyLabel: "暂无自定义接入;按 n 新建",
|
|
352
|
+
},
|
|
353
|
+
);
|
|
354
|
+
if (action.type === "cancel") return;
|
|
355
|
+
if (action.type === "shortcut") {
|
|
356
|
+
if (action.shortcut === "new-provider") await createProviderAndModel(pi, ctx);
|
|
357
|
+
if (action.shortcut === "header-profiles") await runHeaderProfilesPanel(pi, ctx);
|
|
358
|
+
if (action.shortcut === "delete-provider") {
|
|
359
|
+
const selectedProviderId = rows[cursor.index]?.providerId;
|
|
360
|
+
const selectedProvider = selectedProviderId ? state.providers[selectedProviderId] : undefined;
|
|
361
|
+
if (selectedProviderId && selectedProvider) await deleteProvider(pi, ctx, selectedProviderId, selectedProvider);
|
|
362
|
+
else ctx.ui.notify("没有可删除的接入。", "info");
|
|
363
|
+
}
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const index = Number.parseInt(action.id, 10);
|
|
367
|
+
if (!Number.isFinite(index) || index < 0 || index >= rows.length) continue;
|
|
368
|
+
const row = rows[index]!;
|
|
369
|
+
const provider = state.providers[row.providerId];
|
|
370
|
+
if (!provider) {
|
|
371
|
+
ctx.ui.notify(`接入配置已不存在:${row.providerId}`, "warning");
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
await showProviderMenu(pi, ctx, row.providerId);
|
|
375
|
+
}
|
|
376
|
+
}
|