opencode-copilot-account-switcher 0.12.4 → 0.13.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/dist/codex-oauth.d.ts +39 -0
- package/dist/codex-oauth.js +316 -0
- package/dist/codex-status-command.js +85 -28
- package/dist/codex-store.d.ts +36 -13
- package/dist/codex-store.js +231 -39
- package/dist/menu-runtime.d.ts +69 -0
- package/dist/menu-runtime.js +108 -0
- package/dist/plugin.js +141 -682
- package/dist/providers/codex-menu-adapter.d.ts +47 -0
- package/dist/providers/codex-menu-adapter.js +307 -0
- package/dist/providers/copilot-menu-adapter.d.ts +65 -0
- package/dist/providers/copilot-menu-adapter.js +763 -0
- package/dist/providers/descriptor.js +7 -2
- package/dist/providers/registry.js +3 -2
- package/dist/ui/menu.d.ts +26 -2
- package/dist/ui/menu.js +194 -41
- package/package.json +1 -1
package/dist/codex-store.js
CHANGED
|
@@ -3,52 +3,244 @@ import os from "node:os";
|
|
|
3
3
|
import { promises as fs } from "node:fs";
|
|
4
4
|
import { xdgConfig } from "xdg-basedir";
|
|
5
5
|
const filename = "codex-store.json";
|
|
6
|
-
function
|
|
6
|
+
function asRecord(input) {
|
|
7
7
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
8
|
+
return undefined;
|
|
9
|
+
return input;
|
|
10
|
+
}
|
|
11
|
+
function pickString(input) {
|
|
12
|
+
return typeof input === "string" && input.length > 0 ? input : undefined;
|
|
13
|
+
}
|
|
14
|
+
function pickNumber(input) {
|
|
15
|
+
return typeof input === "number" && Number.isFinite(input) ? input : undefined;
|
|
16
|
+
}
|
|
17
|
+
function pickBoolean(input) {
|
|
18
|
+
return typeof input === "boolean" ? input : undefined;
|
|
19
|
+
}
|
|
20
|
+
function pickUsageWindow(input) {
|
|
21
|
+
const source = asRecord(input);
|
|
22
|
+
if (!source)
|
|
23
|
+
return undefined;
|
|
24
|
+
const next = {};
|
|
25
|
+
if (pickNumber(source.entitlement) !== undefined)
|
|
26
|
+
next.entitlement = pickNumber(source.entitlement);
|
|
27
|
+
if (pickNumber(source.remaining) !== undefined)
|
|
28
|
+
next.remaining = pickNumber(source.remaining);
|
|
29
|
+
if (pickNumber(source.used) !== undefined)
|
|
30
|
+
next.used = pickNumber(source.used);
|
|
31
|
+
if (pickNumber(source.resetAt) !== undefined)
|
|
32
|
+
next.resetAt = pickNumber(source.resetAt);
|
|
33
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
34
|
+
}
|
|
35
|
+
function pickSnapshot(input) {
|
|
36
|
+
const source = asRecord(input);
|
|
37
|
+
if (!source)
|
|
38
|
+
return undefined;
|
|
39
|
+
const next = {};
|
|
40
|
+
if (pickString(source.plan))
|
|
41
|
+
next.plan = pickString(source.plan);
|
|
42
|
+
const usage5h = pickUsageWindow(source.usage5h);
|
|
43
|
+
if (usage5h)
|
|
44
|
+
next.usage5h = usage5h;
|
|
45
|
+
const usageWeek = pickUsageWindow(source.usageWeek);
|
|
46
|
+
if (usageWeek)
|
|
47
|
+
next.usageWeek = usageWeek;
|
|
48
|
+
if (pickNumber(source.updatedAt) !== undefined)
|
|
49
|
+
next.updatedAt = pickNumber(source.updatedAt);
|
|
50
|
+
if (pickString(source.error))
|
|
51
|
+
next.error = pickString(source.error);
|
|
52
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
53
|
+
}
|
|
54
|
+
function pickEntry(input) {
|
|
55
|
+
const source = asRecord(input);
|
|
56
|
+
if (!source)
|
|
57
|
+
return undefined;
|
|
58
|
+
const next = {};
|
|
59
|
+
if (pickString(source.name))
|
|
60
|
+
next.name = pickString(source.name);
|
|
61
|
+
if (pickString(source.providerId))
|
|
62
|
+
next.providerId = pickString(source.providerId);
|
|
63
|
+
if (pickString(source.refresh))
|
|
64
|
+
next.refresh = pickString(source.refresh);
|
|
65
|
+
if (pickString(source.access))
|
|
66
|
+
next.access = pickString(source.access);
|
|
67
|
+
if (pickNumber(source.expires) !== undefined)
|
|
68
|
+
next.expires = pickNumber(source.expires);
|
|
69
|
+
if (pickString(source.accountId))
|
|
70
|
+
next.accountId = pickString(source.accountId);
|
|
71
|
+
if (pickString(source.email))
|
|
72
|
+
next.email = pickString(source.email);
|
|
73
|
+
if (pickNumber(source.addedAt) !== undefined)
|
|
74
|
+
next.addedAt = pickNumber(source.addedAt);
|
|
75
|
+
if (pickNumber(source.lastUsed) !== undefined)
|
|
76
|
+
next.lastUsed = pickNumber(source.lastUsed);
|
|
77
|
+
if (pickString(source.source))
|
|
78
|
+
next.source = pickString(source.source);
|
|
79
|
+
const snapshot = pickSnapshot(source.snapshot);
|
|
80
|
+
if (snapshot)
|
|
81
|
+
next.snapshot = snapshot;
|
|
82
|
+
return next;
|
|
83
|
+
}
|
|
84
|
+
function pickActiveAccountNames(input, accounts) {
|
|
85
|
+
if (!Array.isArray(input))
|
|
86
|
+
return undefined;
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
const next = [];
|
|
89
|
+
for (const item of input) {
|
|
90
|
+
const name = pickString(item);
|
|
91
|
+
if (!name || seen.has(name) || !accounts[name])
|
|
92
|
+
continue;
|
|
93
|
+
seen.add(name);
|
|
94
|
+
next.push(name);
|
|
31
95
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
96
|
+
return next.length > 0 ? next : undefined;
|
|
97
|
+
}
|
|
98
|
+
function normalizeNewStore(source) {
|
|
99
|
+
const accounts = {};
|
|
100
|
+
const sourceAccounts = asRecord(source.accounts);
|
|
101
|
+
if (sourceAccounts) {
|
|
102
|
+
for (const [name, value] of Object.entries(sourceAccounts)) {
|
|
103
|
+
const entry = pickEntry(value);
|
|
104
|
+
if (!entry)
|
|
105
|
+
continue;
|
|
106
|
+
accounts[name] = {
|
|
107
|
+
...entry,
|
|
108
|
+
...(entry.name ? {} : { name }),
|
|
109
|
+
};
|
|
45
110
|
}
|
|
46
111
|
}
|
|
112
|
+
const store = { accounts };
|
|
113
|
+
const active = pickString(source.active);
|
|
114
|
+
if (active && accounts[active])
|
|
115
|
+
store.active = active;
|
|
116
|
+
const activeNames = pickActiveAccountNames(source.activeAccountNames, accounts);
|
|
117
|
+
if (activeNames)
|
|
118
|
+
store.activeAccountNames = activeNames;
|
|
119
|
+
if (pickBoolean(source.autoRefresh) !== undefined)
|
|
120
|
+
store.autoRefresh = pickBoolean(source.autoRefresh);
|
|
121
|
+
if (pickNumber(source.refreshMinutes) !== undefined)
|
|
122
|
+
store.refreshMinutes = pickNumber(source.refreshMinutes);
|
|
123
|
+
if (pickNumber(source.lastSnapshotRefresh) !== undefined)
|
|
124
|
+
store.lastSnapshotRefresh = pickNumber(source.lastSnapshotRefresh);
|
|
125
|
+
if (pickBoolean(source.bootstrapAuthImportTried) !== undefined) {
|
|
126
|
+
store.bootstrapAuthImportTried = pickBoolean(source.bootstrapAuthImportTried);
|
|
127
|
+
}
|
|
128
|
+
if (pickNumber(source.bootstrapAuthImportAt) !== undefined) {
|
|
129
|
+
store.bootstrapAuthImportAt = pickNumber(source.bootstrapAuthImportAt);
|
|
130
|
+
}
|
|
47
131
|
return store;
|
|
48
132
|
}
|
|
133
|
+
function normalizeLegacyStore(source) {
|
|
134
|
+
const legacyAccount = asRecord(source.account);
|
|
135
|
+
const legacyStatus = asRecord(source.status);
|
|
136
|
+
const legacyPremium = asRecord(legacyStatus?.premium);
|
|
137
|
+
const accountId = pickString(source.activeAccountId) ?? pickString(legacyAccount?.id);
|
|
138
|
+
const email = pickString(source.activeEmail) ?? pickString(legacyAccount?.email);
|
|
139
|
+
const plan = pickString(legacyAccount?.plan);
|
|
140
|
+
const entitlement = pickNumber(legacyPremium?.entitlement);
|
|
141
|
+
const remaining = pickNumber(legacyPremium?.remaining);
|
|
142
|
+
const updatedAt = pickNumber(source.lastStatusRefresh);
|
|
143
|
+
const hasLegacy = Boolean(accountId
|
|
144
|
+
|| email
|
|
145
|
+
|| plan
|
|
146
|
+
|| entitlement !== undefined
|
|
147
|
+
|| remaining !== undefined);
|
|
148
|
+
const store = {
|
|
149
|
+
accounts: {},
|
|
150
|
+
};
|
|
151
|
+
if (pickBoolean(source.bootstrapAuthImportTried) !== undefined) {
|
|
152
|
+
store.bootstrapAuthImportTried = pickBoolean(source.bootstrapAuthImportTried);
|
|
153
|
+
}
|
|
154
|
+
if (pickNumber(source.bootstrapAuthImportAt) !== undefined) {
|
|
155
|
+
store.bootstrapAuthImportAt = pickNumber(source.bootstrapAuthImportAt);
|
|
156
|
+
}
|
|
157
|
+
if (updatedAt !== undefined)
|
|
158
|
+
store.lastSnapshotRefresh = updatedAt;
|
|
159
|
+
if (!hasLegacy)
|
|
160
|
+
return store;
|
|
161
|
+
const name = accountId ?? email ?? "default";
|
|
162
|
+
const snapshot = {};
|
|
163
|
+
if (plan)
|
|
164
|
+
snapshot.plan = plan;
|
|
165
|
+
if (entitlement !== undefined || remaining !== undefined) {
|
|
166
|
+
snapshot.usage5h = {
|
|
167
|
+
...(entitlement !== undefined ? { entitlement } : {}),
|
|
168
|
+
...(remaining !== undefined ? { remaining } : {}),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (updatedAt !== undefined)
|
|
172
|
+
snapshot.updatedAt = updatedAt;
|
|
173
|
+
store.accounts[name] = {
|
|
174
|
+
name,
|
|
175
|
+
providerId: "codex",
|
|
176
|
+
...(accountId ? { accountId } : {}),
|
|
177
|
+
...(email ? { email } : {}),
|
|
178
|
+
...(Object.keys(snapshot).length > 0 ? { snapshot } : {}),
|
|
179
|
+
};
|
|
180
|
+
store.active = name;
|
|
181
|
+
return store;
|
|
182
|
+
}
|
|
183
|
+
function mergeLegacyIntoStore(store, source) {
|
|
184
|
+
const legacy = normalizeLegacyStore(source);
|
|
185
|
+
if (Object.keys(legacy.accounts).length === 0) {
|
|
186
|
+
return {
|
|
187
|
+
...store,
|
|
188
|
+
lastSnapshotRefresh: store.lastSnapshotRefresh ?? legacy.lastSnapshotRefresh,
|
|
189
|
+
...(legacy.bootstrapAuthImportTried !== undefined ? { bootstrapAuthImportTried: legacy.bootstrapAuthImportTried } : {}),
|
|
190
|
+
...(legacy.bootstrapAuthImportAt !== undefined ? { bootstrapAuthImportAt: legacy.bootstrapAuthImportAt } : {}),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (Object.keys(store.accounts).length > 0) {
|
|
194
|
+
return {
|
|
195
|
+
...store,
|
|
196
|
+
lastSnapshotRefresh: store.lastSnapshotRefresh ?? legacy.lastSnapshotRefresh,
|
|
197
|
+
bootstrapAuthImportTried: store.bootstrapAuthImportTried ?? legacy.bootstrapAuthImportTried,
|
|
198
|
+
bootstrapAuthImportAt: store.bootstrapAuthImportAt ?? legacy.bootstrapAuthImportAt,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
...legacy,
|
|
203
|
+
...store,
|
|
204
|
+
accounts: {
|
|
205
|
+
...legacy.accounts,
|
|
206
|
+
...store.accounts,
|
|
207
|
+
},
|
|
208
|
+
active: store.active ?? legacy.active,
|
|
209
|
+
activeAccountNames: store.activeAccountNames ?? legacy.activeAccountNames,
|
|
210
|
+
autoRefresh: store.autoRefresh ?? legacy.autoRefresh,
|
|
211
|
+
refreshMinutes: store.refreshMinutes ?? legacy.refreshMinutes,
|
|
212
|
+
lastSnapshotRefresh: store.lastSnapshotRefresh ?? legacy.lastSnapshotRefresh,
|
|
213
|
+
bootstrapAuthImportTried: store.bootstrapAuthImportTried ?? legacy.bootstrapAuthImportTried,
|
|
214
|
+
bootstrapAuthImportAt: store.bootstrapAuthImportAt ?? legacy.bootstrapAuthImportAt,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
export function normalizeCodexStore(input) {
|
|
218
|
+
const source = asRecord(input);
|
|
219
|
+
if (!source)
|
|
220
|
+
return { accounts: {} };
|
|
221
|
+
if (source.accounts && asRecord(source.accounts)) {
|
|
222
|
+
return mergeLegacyIntoStore(normalizeNewStore(source), source);
|
|
223
|
+
}
|
|
224
|
+
return normalizeLegacyStore(source);
|
|
225
|
+
}
|
|
49
226
|
export function parseCodexStore(raw) {
|
|
50
227
|
const parsed = raw ? JSON.parse(raw) : {};
|
|
51
|
-
return
|
|
228
|
+
return normalizeCodexStore(parsed);
|
|
229
|
+
}
|
|
230
|
+
export function getActiveCodexAccount(store) {
|
|
231
|
+
if (store.active && store.accounts[store.active]) {
|
|
232
|
+
return {
|
|
233
|
+
name: store.active,
|
|
234
|
+
entry: store.accounts[store.active],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const first = Object.entries(store.accounts)[0];
|
|
238
|
+
if (!first)
|
|
239
|
+
return undefined;
|
|
240
|
+
return {
|
|
241
|
+
name: first[0],
|
|
242
|
+
entry: first[1],
|
|
243
|
+
};
|
|
52
244
|
}
|
|
53
245
|
export function codexStorePath() {
|
|
54
246
|
const base = xdgConfig ?? path.join(os.homedir(), ".config");
|
|
@@ -64,7 +256,7 @@ export async function readCodexStore(filePath = codexStorePath()) {
|
|
|
64
256
|
}
|
|
65
257
|
export async function writeCodexStore(store, options) {
|
|
66
258
|
const file = options?.filePath ?? codexStorePath();
|
|
67
|
-
const next =
|
|
259
|
+
const next = normalizeCodexStore(store);
|
|
68
260
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
69
261
|
await fs.writeFile(file, JSON.stringify(next, null, 2), { mode: 0o600 });
|
|
70
262
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
type WriteMeta = {
|
|
2
|
+
reason: string;
|
|
3
|
+
source: string;
|
|
4
|
+
actionType?: string;
|
|
5
|
+
};
|
|
6
|
+
export type MenuAccountInfo = {
|
|
7
|
+
id?: string;
|
|
8
|
+
name: string;
|
|
9
|
+
index: number;
|
|
10
|
+
isCurrent?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type MenuActionAccount = {
|
|
13
|
+
id?: string;
|
|
14
|
+
name: string;
|
|
15
|
+
};
|
|
16
|
+
type SharedActionResult = boolean | {
|
|
17
|
+
changed: boolean;
|
|
18
|
+
persistHandled?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type MenuAction = {
|
|
21
|
+
type: "add";
|
|
22
|
+
} | {
|
|
23
|
+
type: "cancel";
|
|
24
|
+
} | {
|
|
25
|
+
type: "remove";
|
|
26
|
+
account: MenuActionAccount;
|
|
27
|
+
} | {
|
|
28
|
+
type: "remove-all";
|
|
29
|
+
} | {
|
|
30
|
+
type: "switch";
|
|
31
|
+
account: MenuActionAccount;
|
|
32
|
+
} | {
|
|
33
|
+
type: "provider";
|
|
34
|
+
name: string;
|
|
35
|
+
payload?: unknown;
|
|
36
|
+
};
|
|
37
|
+
export type ProviderMenuAdapter<TStore, TEntry> = {
|
|
38
|
+
key: string;
|
|
39
|
+
loadStore: () => Promise<TStore>;
|
|
40
|
+
writeStore: (store: TStore, meta: WriteMeta) => Promise<void>;
|
|
41
|
+
bootstrapAuthImport: (store: TStore) => Promise<boolean>;
|
|
42
|
+
authorizeNewAccount: (store: TStore) => Promise<TEntry | undefined>;
|
|
43
|
+
refreshSnapshots: (store: TStore) => Promise<void>;
|
|
44
|
+
toMenuInfo: (store: TStore) => Promise<MenuAccountInfo[]>;
|
|
45
|
+
getCurrentEntry: (store: TStore) => TEntry | undefined;
|
|
46
|
+
getRefreshConfig: (store: TStore) => {
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
minutes: number;
|
|
49
|
+
};
|
|
50
|
+
getAccountByName: (store: TStore, name: string) => {
|
|
51
|
+
name: string;
|
|
52
|
+
entry: TEntry;
|
|
53
|
+
} | undefined;
|
|
54
|
+
addAccount?: (store: TStore, entry: TEntry) => Promise<SharedActionResult> | SharedActionResult;
|
|
55
|
+
removeAccount?: (store: TStore, name: string) => Promise<SharedActionResult> | SharedActionResult;
|
|
56
|
+
removeAllAccounts?: (store: TStore) => Promise<SharedActionResult> | SharedActionResult;
|
|
57
|
+
switchAccount: (store: TStore, name: string, entry: TEntry) => Promise<{
|
|
58
|
+
persistHandled?: boolean;
|
|
59
|
+
} | void>;
|
|
60
|
+
applyAction?: (store: TStore, action: Extract<MenuAction, {
|
|
61
|
+
type: "provider";
|
|
62
|
+
}>) => Promise<boolean>;
|
|
63
|
+
};
|
|
64
|
+
export declare function runProviderMenu<TStore, TEntry>(input: {
|
|
65
|
+
adapter: ProviderMenuAdapter<TStore, TEntry>;
|
|
66
|
+
showMenu: (accounts: MenuAccountInfo[], store: TStore) => Promise<MenuAction>;
|
|
67
|
+
now?: () => number;
|
|
68
|
+
}): Promise<TEntry | undefined>;
|
|
69
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
function parseSharedActionResult(result) {
|
|
2
|
+
if (typeof result === "object" && result) {
|
|
3
|
+
return {
|
|
4
|
+
changed: result.changed === true,
|
|
5
|
+
persistHandled: result.persistHandled === true,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
changed: result === true,
|
|
10
|
+
persistHandled: false,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function runProviderMenu(input) {
|
|
14
|
+
const now = input.now ?? Date.now;
|
|
15
|
+
const store = await input.adapter.loadStore();
|
|
16
|
+
if (await input.adapter.bootstrapAuthImport(store)) {
|
|
17
|
+
await input.adapter.writeStore(store, {
|
|
18
|
+
reason: "bootstrap-auth-import",
|
|
19
|
+
source: "menu-runtime",
|
|
20
|
+
actionType: "bootstrap-auth-import",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
let nextRefreshAt = 0;
|
|
24
|
+
while (true) {
|
|
25
|
+
const refresh = input.adapter.getRefreshConfig(store);
|
|
26
|
+
if (refresh.enabled && now() >= nextRefreshAt) {
|
|
27
|
+
await input.adapter.refreshSnapshots(store);
|
|
28
|
+
await input.adapter.writeStore(store, {
|
|
29
|
+
reason: "auto-refresh",
|
|
30
|
+
source: "menu-runtime",
|
|
31
|
+
actionType: "auto-refresh",
|
|
32
|
+
});
|
|
33
|
+
nextRefreshAt = now() + refresh.minutes * 60_000;
|
|
34
|
+
}
|
|
35
|
+
const accounts = await input.adapter.toMenuInfo(store);
|
|
36
|
+
const action = await input.showMenu(accounts, store);
|
|
37
|
+
if (action.type === "cancel")
|
|
38
|
+
return input.adapter.getCurrentEntry(store);
|
|
39
|
+
if (action.type === "add") {
|
|
40
|
+
const entry = await input.adapter.authorizeNewAccount(store);
|
|
41
|
+
const result = !entry ? undefined : await input.adapter.addAccount?.(store, entry);
|
|
42
|
+
const parsed = parseSharedActionResult(result);
|
|
43
|
+
if (!entry || !parsed.changed)
|
|
44
|
+
continue;
|
|
45
|
+
if (!parsed.persistHandled) {
|
|
46
|
+
await input.adapter.writeStore(store, {
|
|
47
|
+
reason: "add-account",
|
|
48
|
+
source: "menu-runtime",
|
|
49
|
+
actionType: "add",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (action.type === "remove-all") {
|
|
55
|
+
const result = await input.adapter.removeAllAccounts?.(store);
|
|
56
|
+
const parsed = parseSharedActionResult(result);
|
|
57
|
+
if (!parsed.changed)
|
|
58
|
+
continue;
|
|
59
|
+
if (!parsed.persistHandled) {
|
|
60
|
+
await input.adapter.writeStore(store, {
|
|
61
|
+
reason: "remove-all",
|
|
62
|
+
source: "menu-runtime",
|
|
63
|
+
actionType: "remove-all",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (action.type === "remove") {
|
|
69
|
+
const accountName = action.account.id ?? action.account.name;
|
|
70
|
+
const result = await input.adapter.removeAccount?.(store, accountName);
|
|
71
|
+
const parsed = parseSharedActionResult(result);
|
|
72
|
+
if (!parsed.changed)
|
|
73
|
+
continue;
|
|
74
|
+
if (!parsed.persistHandled) {
|
|
75
|
+
await input.adapter.writeStore(store, {
|
|
76
|
+
reason: "remove-account",
|
|
77
|
+
source: "menu-runtime",
|
|
78
|
+
actionType: "remove",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (action.type === "switch") {
|
|
84
|
+
const accountName = action.account.id ?? action.account.name;
|
|
85
|
+
const selected = input.adapter.getAccountByName(store, accountName);
|
|
86
|
+
if (!selected)
|
|
87
|
+
continue;
|
|
88
|
+
const switchResult = await input.adapter.switchAccount(store, selected.name, selected.entry);
|
|
89
|
+
if (!switchResult?.persistHandled) {
|
|
90
|
+
await input.adapter.writeStore(store, {
|
|
91
|
+
reason: "persist-account-switch",
|
|
92
|
+
source: "menu-runtime",
|
|
93
|
+
actionType: "switch",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (!input.adapter.applyAction)
|
|
99
|
+
continue;
|
|
100
|
+
if (!await input.adapter.applyAction(store, action))
|
|
101
|
+
continue;
|
|
102
|
+
await input.adapter.writeStore(store, {
|
|
103
|
+
reason: `provider-action:${action.name}`,
|
|
104
|
+
source: "menu-runtime",
|
|
105
|
+
actionType: action.name,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|