Skip to content

Commit 3e44d11

Browse files
committed
Preserve experimental settings entry types
1 parent 5e3562c commit 3e44d11

4 files changed

Lines changed: 134 additions & 158 deletions

File tree

lib/codex-manager/experimental-settings-entry.ts

Lines changed: 8 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,14 @@
11
import type { PluginConfig } from "../types.js";
2+
import type { ExperimentalSettingsPromptDeps } from "./experimental-settings-prompt.js";
23

3-
export async function promptExperimentalSettingsEntry(params: {
4-
initialConfig: PluginConfig;
5-
promptExperimentalSettingsMenu: (args: {
4+
export async function promptExperimentalSettingsEntry<TTargetState>(
5+
params: {
66
initialConfig: PluginConfig;
7-
isInteractive: () => boolean;
8-
ui: ReturnType<typeof import("../ui/runtime.js").getUiRuntimeOptions>;
9-
cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig;
10-
select: <T>(
11-
items: Array<Record<string, unknown>>,
12-
options: Record<string, unknown>,
13-
) => Promise<T | null>;
14-
getExperimentalSelectOptions: (
15-
ui: ReturnType<typeof import("../ui/runtime.js").getUiRuntimeOptions>,
16-
help: string,
17-
hotkeyMapper: (raw: string) => unknown,
18-
) => Record<string, unknown>;
19-
mapExperimentalMenuHotkey: (raw: string) => unknown;
20-
mapExperimentalStatusHotkey: (raw: string) => unknown;
21-
formatDashboardSettingState: (enabled: boolean) => string;
22-
copy: Record<string, string>;
23-
input: NodeJS.ReadStream;
24-
output: NodeJS.WriteStream;
25-
runNamedBackupExport: (args: {
26-
name: string;
27-
}) => Promise<{ kind: string; path?: string; error?: unknown }>;
28-
loadAccounts: () => Promise<unknown>;
29-
loadExperimentalSyncTarget: () => Promise<unknown>;
30-
planOcChatgptSync: (args: Record<string, unknown>) => Promise<unknown>;
31-
applyOcChatgptSync: (args: Record<string, unknown>) => Promise<unknown>;
32-
getTargetKind: (targetState: unknown) => string;
33-
getTargetDestination: (targetState: unknown) => unknown;
34-
getTargetDetection: (targetState: unknown) => unknown;
35-
getTargetErrorMessage: (targetState: unknown) => string | null;
36-
getPlanKind: (plan: unknown) => string;
37-
getPlanBlockedReason: (plan: unknown) => string;
38-
getPlanPreview: (plan: unknown) => {
39-
toAdd: unknown[];
40-
toUpdate: unknown[];
41-
toSkip: unknown[];
42-
unchangedDestinationOnly: unknown[];
43-
activeSelectionBehavior: string;
44-
};
45-
getAppliedLabel: (applied: unknown) => { label: string; color: string };
46-
}) => Promise<PluginConfig | null>;
47-
isInteractive: () => boolean;
48-
ui: ReturnType<typeof import("../ui/runtime.js").getUiRuntimeOptions>;
49-
cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig;
50-
select: <T>(
51-
items: Array<Record<string, unknown>>,
52-
options: Record<string, unknown>,
53-
) => Promise<T | null>;
54-
getExperimentalSelectOptions: (
55-
ui: ReturnType<typeof import("../ui/runtime.js").getUiRuntimeOptions>,
56-
help: string,
57-
hotkeyMapper: (raw: string) => unknown,
58-
) => Record<string, unknown>;
59-
mapExperimentalMenuHotkey: (raw: string) => unknown;
60-
mapExperimentalStatusHotkey: (raw: string) => unknown;
61-
formatDashboardSettingState: (enabled: boolean) => string;
62-
copy: Record<string, string>;
63-
input: NodeJS.ReadStream;
64-
output: NodeJS.WriteStream;
65-
runNamedBackupExport: (args: {
66-
name: string;
67-
}) => Promise<{ kind: string; path?: string; error?: unknown }>;
68-
loadAccounts: () => Promise<unknown>;
69-
loadExperimentalSyncTarget: () => Promise<unknown>;
70-
planOcChatgptSync: (args: Record<string, unknown>) => Promise<unknown>;
71-
applyOcChatgptSync: (args: Record<string, unknown>) => Promise<unknown>;
72-
getTargetKind: (targetState: unknown) => string;
73-
getTargetDestination: (targetState: unknown) => unknown;
74-
getTargetDetection: (targetState: unknown) => unknown;
75-
getTargetErrorMessage: (targetState: unknown) => string | null;
76-
getPlanKind: (plan: unknown) => string;
77-
getPlanBlockedReason: (plan: unknown) => string;
78-
getPlanPreview: (plan: unknown) => {
79-
toAdd: unknown[];
80-
toUpdate: unknown[];
81-
toSkip: unknown[];
82-
unchangedDestinationOnly: unknown[];
83-
activeSelectionBehavior: string;
84-
};
85-
getAppliedLabel: (applied: unknown) => { label: string; color: string };
86-
}): Promise<PluginConfig | null> {
7+
promptExperimentalSettingsMenu: (
8+
args: ExperimentalSettingsPromptDeps<TTargetState>,
9+
) => Promise<PluginConfig | null>;
10+
} & ExperimentalSettingsPromptDeps<TTargetState>,
11+
): Promise<PluginConfig | null> {
8712
return params.promptExperimentalSettingsMenu({
8813
initialConfig: params.initialConfig,
8914
isInteractive: params.isInteractive,

lib/codex-manager/experimental-settings-prompt.ts

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,95 @@
11
import { createInterface } from "node:readline/promises";
2+
import type {
3+
ApplyOcChatgptSyncOptions,
4+
OcChatgptSyncApplyResult,
5+
OcChatgptSyncPlanResult,
6+
PlanOcChatgptSyncOptions,
7+
} from "../oc-chatgpt-orchestrator.js";
8+
import type { AccountStorageV3 } from "../storage.js";
29
import type { PluginConfig } from "../types.js";
10+
import type { MenuItem, select } from "../ui/select.js";
311
import type { UiRuntimeOptions } from "../ui/runtime.js";
12+
import type {
13+
ExperimentalSettingsAction,
14+
getExperimentalSelectOptions,
15+
mapExperimentalMenuHotkey,
16+
mapExperimentalStatusHotkey,
17+
} from "./experimental-settings-schema.js";
418

5-
export async function promptExperimentalSettingsMenu<
6-
TAction,
19+
export type ExperimentalSettingsCopy = {
20+
experimentalSync: string;
21+
experimentalBackup: string;
22+
experimentalRefreshGuard: string;
23+
experimentalRefreshInterval: string;
24+
experimentalDecreaseInterval: string;
25+
experimentalIncreaseInterval: string;
26+
saveAndBack: string;
27+
backNoSave: string;
28+
experimentalHelpMenu: string;
29+
experimentalBackupPrompt: string;
30+
back: string;
31+
experimentalHelpStatus: string;
32+
experimentalApplySync: string;
33+
experimentalHelpPreview: string;
34+
};
35+
36+
export type ExperimentalSettingsPromptDeps<
737
TTargetState,
8-
TPlan,
9-
TApplied,
10-
>(params: {
38+
> = {
1139
initialConfig: PluginConfig;
1240
isInteractive: () => boolean;
1341
ui: UiRuntimeOptions;
1442
cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig;
15-
select: <T>(
16-
items: Array<Record<string, unknown>>,
17-
options: Record<string, unknown>,
18-
) => Promise<T | null>;
19-
getExperimentalSelectOptions: (
20-
ui: UiRuntimeOptions,
21-
help: string,
22-
hotkeyMapper: (raw: string) => TAction | undefined,
23-
) => Record<string, unknown>;
24-
mapExperimentalMenuHotkey: (raw: string) => TAction | undefined;
25-
mapExperimentalStatusHotkey: (raw: string) => TAction | undefined;
43+
select: typeof select;
44+
getExperimentalSelectOptions: typeof getExperimentalSelectOptions;
45+
mapExperimentalMenuHotkey: typeof mapExperimentalMenuHotkey;
46+
mapExperimentalStatusHotkey: typeof mapExperimentalStatusHotkey;
2647
formatDashboardSettingState: (enabled: boolean) => string;
27-
copy: {
28-
experimentalSync: string;
29-
experimentalBackup: string;
30-
experimentalRefreshGuard: string;
31-
experimentalRefreshInterval: string;
32-
experimentalDecreaseInterval: string;
33-
experimentalIncreaseInterval: string;
34-
saveAndBack: string;
35-
backNoSave: string;
36-
experimentalHelpMenu: string;
37-
experimentalBackupPrompt: string;
38-
back: string;
39-
experimentalHelpStatus: string;
40-
experimentalApplySync: string;
41-
experimentalHelpPreview: string;
42-
};
48+
copy: ExperimentalSettingsCopy;
4349
input: NodeJS.ReadStream;
4450
output: NodeJS.WriteStream;
4551
runNamedBackupExport: (args: {
4652
name: string;
4753
}) => Promise<{ kind: string; path?: string; error?: unknown }>;
48-
loadAccounts: () => Promise<unknown>;
54+
loadAccounts: () => Promise<AccountStorageV3 | null>;
4955
loadExperimentalSyncTarget: () => Promise<TTargetState>;
50-
planOcChatgptSync: (args: Record<string, unknown>) => Promise<TPlan>;
51-
applyOcChatgptSync: (args: Record<string, unknown>) => Promise<TApplied>;
56+
planOcChatgptSync: (
57+
args: PlanOcChatgptSyncOptions,
58+
) => Promise<OcChatgptSyncPlanResult>;
59+
applyOcChatgptSync: (
60+
args: ApplyOcChatgptSyncOptions,
61+
) => Promise<OcChatgptSyncApplyResult>;
5262
getTargetKind: (targetState: TTargetState) => string;
53-
getTargetDestination: (targetState: TTargetState) => unknown;
54-
getTargetDetection: (targetState: TTargetState) => unknown;
63+
getTargetDestination: (targetState: TTargetState) => AccountStorageV3 | null;
64+
getTargetDetection: (
65+
targetState: TTargetState,
66+
) => ReturnType<
67+
typeof import("../oc-chatgpt-target-detection.js").detectOcChatgptMultiAuthTarget
68+
>;
5569
getTargetErrorMessage: (targetState: TTargetState) => string | null;
56-
getPlanKind: (plan: TPlan) => string;
57-
getPlanBlockedReason: (plan: TPlan) => string;
58-
getPlanPreview: (plan: TPlan) => {
70+
getPlanKind: (plan: OcChatgptSyncPlanResult) => string;
71+
getPlanBlockedReason: (plan: OcChatgptSyncPlanResult) => string;
72+
getPlanPreview: (plan: OcChatgptSyncPlanResult) => {
5973
toAdd: unknown[];
6074
toUpdate: unknown[];
6175
toSkip: unknown[];
6276
unchangedDestinationOnly: unknown[];
6377
activeSelectionBehavior: string;
6478
};
65-
getAppliedLabel: (applied: TApplied) => { label: string; color: string };
66-
}): Promise<PluginConfig | null> {
79+
getAppliedLabel: (
80+
applied: OcChatgptSyncApplyResult,
81+
) => { label: string; color: MenuItem["color"] };
82+
};
83+
84+
export async function promptExperimentalSettingsMenu<TTargetState>(
85+
params: ExperimentalSettingsPromptDeps<TTargetState>,
86+
): Promise<PluginConfig | null> {
6787
if (!params.isInteractive()) return null;
6888
let draft = params.cloneBackendPluginConfig(params.initialConfig);
6989
const copy = params.copy;
7090

7191
while (true) {
72-
const action = await params.select<TAction>(
92+
const action = await params.select<ExperimentalSettingsAction>(
7393
[
7494
{
7595
label: copy.experimentalSync,
@@ -164,7 +184,7 @@ export async function promptExperimentalSettingsMenu<
164184
: backupResult.error instanceof Error
165185
? backupResult.error.message
166186
: String(backupResult.error);
167-
await params.select<TAction>(
187+
await params.select<ExperimentalSettingsAction>(
168188
[
169189
{
170190
label: backupLabel,
@@ -184,7 +204,7 @@ export async function promptExperimentalSettingsMenu<
184204
} catch (error) {
185205
const message =
186206
error instanceof Error ? error.message : String(error);
187-
await params.select<TAction>(
207+
await params.select<ExperimentalSettingsAction>(
188208
[
189209
{
190210
label: message,
@@ -212,7 +232,7 @@ export async function promptExperimentalSettingsMenu<
212232
const targetState = await params.loadExperimentalSyncTarget();
213233
const targetError = params.getTargetErrorMessage(targetState);
214234
if (targetError) {
215-
await params.select<TAction>(
235+
await params.select<ExperimentalSettingsAction>(
216236
[
217237
{
218238
label: targetError,
@@ -246,7 +266,7 @@ export async function promptExperimentalSettingsMenu<
246266
: undefined,
247267
});
248268
if (params.getPlanKind(plan) !== "ready") {
249-
await params.select<TAction>(
269+
await params.select<ExperimentalSettingsAction>(
250270
[
251271
{
252272
label: params.getPlanBlockedReason(plan),
@@ -267,7 +287,7 @@ export async function promptExperimentalSettingsMenu<
267287
}
268288

269289
const preview = params.getPlanPreview(plan);
270-
const review = await params.select<TAction>(
290+
const review = await params.select<ExperimentalSettingsAction>(
271291
[
272292
{
273293
label: `Preview: add ${preview.toAdd.length} | update ${preview.toUpdate.length} | skip ${preview.toSkip.length}`,
@@ -299,15 +319,15 @@ export async function promptExperimentalSettingsMenu<
299319
],
300320
params.getExperimentalSelectOptions(
301321
params.ui,
302-
copy.experimentalHelpPreview,
303-
(raw) => {
304-
const lower = raw.toLowerCase();
305-
if (lower === "q") return { type: "back" } as TAction;
306-
if (lower === "a") return { type: "apply" } as TAction;
307-
return undefined;
308-
},
309-
),
310-
);
322+
copy.experimentalHelpPreview,
323+
(raw) => {
324+
const lower = raw.toLowerCase();
325+
if (lower === "q") return { type: "back" };
326+
if (lower === "a") return { type: "apply" };
327+
return undefined;
328+
},
329+
),
330+
);
311331
if (!review || (review as { type?: string }).type === "back") continue;
312332

313333
const applied = await params.applyOcChatgptSync({
@@ -322,7 +342,7 @@ export async function promptExperimentalSettingsMenu<
322342
: undefined,
323343
});
324344
const appliedLabel = params.getAppliedLabel(applied);
325-
await params.select<TAction>(
345+
await params.select<ExperimentalSettingsAction>(
326346
[
327347
{
328348
label: appliedLabel.label,

lib/codex-manager/settings-hub.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -666,28 +666,37 @@ async function promptExperimentalSettings(
666666
): Promise<PluginConfig | null> {
667667
return promptExperimentalSettingsEntry({
668668
initialConfig,
669-
promptExperimentalSettingsMenu: promptExperimentalSettingsMenu as never,
669+
promptExperimentalSettingsMenu,
670670
isInteractive: () => input.isTTY && output.isTTY,
671671
ui: getUiRuntimeOptions(),
672672
cloneBackendPluginConfig,
673-
select: select as never,
674-
getExperimentalSelectOptions: getExperimentalSelectOptions as never,
675-
mapExperimentalMenuHotkey: mapExperimentalMenuHotkey as never,
676-
mapExperimentalStatusHotkey: mapExperimentalStatusHotkey as never,
673+
select,
674+
getExperimentalSelectOptions,
675+
mapExperimentalMenuHotkey,
676+
mapExperimentalStatusHotkey,
677677
formatDashboardSettingState,
678678
copy: UI_COPY.settings,
679679
input,
680680
output,
681681
runNamedBackupExport,
682682
loadAccounts,
683683
loadExperimentalSyncTarget,
684-
planOcChatgptSync: planOcChatgptSync as never,
685-
applyOcChatgptSync: applyOcChatgptSync as never,
684+
planOcChatgptSync,
685+
applyOcChatgptSync,
686686
getTargetKind: (targetState) => (targetState as { kind: string }).kind,
687-
getTargetDestination: (targetState) =>
688-
(targetState as { kind: string; destination?: unknown }).destination,
689-
getTargetDetection: (targetState) =>
690-
(targetState as { detection?: unknown }).detection,
687+
getTargetDestination: (
688+
targetState,
689+
): import("../storage.js").AccountStorageV3 | null =>
690+
(targetState as {
691+
kind: string;
692+
destination?: import("../storage.js").AccountStorageV3 | null;
693+
}).destination ?? null,
694+
getTargetDetection: (
695+
targetState,
696+
): ReturnType<typeof detectOcChatgptMultiAuthTarget> =>
697+
(targetState as {
698+
detection: ReturnType<typeof detectOcChatgptMultiAuthTarget>;
699+
}).detection,
691700
getTargetErrorMessage: (targetState) =>
692701
(targetState as { kind: string; message?: string }).kind === "error"
693702
? ((targetState as { message?: string }).message ?? "Unknown error")

0 commit comments

Comments
 (0)