opencode-copilot-account-switcher 0.3.1 → 0.3.2
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/plugin-actions.d.ts +14 -2
- package/dist/plugin-actions.js +15 -3
- package/dist/plugin-hooks.d.ts +2 -2
- package/dist/plugin-hooks.js +4 -1
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.js +70 -18
- package/dist/store.d.ts +11 -1
- package/dist/store.js +56 -2
- package/dist/ui/select.d.ts +12 -0
- package/dist/ui/select.js +55 -0
- package/package.json +1 -1
package/dist/plugin-actions.d.ts
CHANGED
|
@@ -4,10 +4,22 @@ export declare function persistAccountSwitch(input: {
|
|
|
4
4
|
store: StoreFile;
|
|
5
5
|
name: string;
|
|
6
6
|
at: number;
|
|
7
|
-
writeStore: (store: StoreFile
|
|
7
|
+
writeStore: (store: StoreFile, meta?: {
|
|
8
|
+
reason?: string;
|
|
9
|
+
source?: string;
|
|
10
|
+
actionType?: string;
|
|
11
|
+
inputStage?: string;
|
|
12
|
+
parsedKey?: string;
|
|
13
|
+
}) => Promise<void>;
|
|
8
14
|
}): Promise<void>;
|
|
9
15
|
export declare function applyMenuAction(input: {
|
|
10
16
|
action: MenuAction;
|
|
11
17
|
store: StoreFile;
|
|
12
|
-
writeStore: (store: StoreFile
|
|
18
|
+
writeStore: (store: StoreFile, meta?: {
|
|
19
|
+
reason?: string;
|
|
20
|
+
source?: string;
|
|
21
|
+
actionType?: string;
|
|
22
|
+
inputStage?: string;
|
|
23
|
+
parsedKey?: string;
|
|
24
|
+
}) => Promise<void>;
|
|
13
25
|
}): Promise<boolean>;
|
package/dist/plugin-actions.js
CHANGED
|
@@ -2,17 +2,29 @@ export async function persistAccountSwitch(input) {
|
|
|
2
2
|
input.store.active = input.name;
|
|
3
3
|
input.store.accounts[input.name].lastUsed = input.at;
|
|
4
4
|
input.store.lastAccountSwitchAt = input.at;
|
|
5
|
-
await input.writeStore(input.store
|
|
5
|
+
await input.writeStore(input.store, {
|
|
6
|
+
reason: "persist-account-switch",
|
|
7
|
+
source: "persistAccountSwitch",
|
|
8
|
+
actionType: "switch",
|
|
9
|
+
});
|
|
6
10
|
}
|
|
7
11
|
export async function applyMenuAction(input) {
|
|
8
12
|
if (input.action.type === "toggle-loop-safety") {
|
|
9
13
|
input.store.loopSafetyEnabled = input.store.loopSafetyEnabled !== true;
|
|
10
|
-
await input.writeStore(input.store
|
|
14
|
+
await input.writeStore(input.store, {
|
|
15
|
+
reason: "toggle-loop-safety",
|
|
16
|
+
source: "applyMenuAction",
|
|
17
|
+
actionType: "toggle-loop-safety",
|
|
18
|
+
});
|
|
11
19
|
return true;
|
|
12
20
|
}
|
|
13
21
|
if (input.action.type === "toggle-network-retry") {
|
|
14
22
|
input.store.networkRetryEnabled = input.store.networkRetryEnabled !== true;
|
|
15
|
-
await input.writeStore(input.store
|
|
23
|
+
await input.writeStore(input.store, {
|
|
24
|
+
reason: "toggle-network-retry",
|
|
25
|
+
source: "applyMenuAction",
|
|
26
|
+
actionType: "toggle-network-retry",
|
|
27
|
+
});
|
|
16
28
|
return true;
|
|
17
29
|
}
|
|
18
30
|
return false;
|
package/dist/plugin-hooks.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CopilotPluginHooks } from "./loop-safety-plugin.js";
|
|
2
2
|
import { type CopilotRetryContext, type FetchLike } from "./copilot-network-retry.js";
|
|
3
|
-
import { type StoreFile } from "./store.js";
|
|
3
|
+
import { type StoreFile, type StoreWriteDebugMeta } from "./store.js";
|
|
4
4
|
import { type CopilotAuthState, type CopilotProviderConfig, type OfficialCopilotConfig } from "./upstream/copilot-loader-adapter.js";
|
|
5
5
|
type ChatHeadersHook = (input: {
|
|
6
6
|
sessionID: string;
|
|
@@ -25,7 +25,7 @@ type CopilotPluginHooksWithChatHeaders = CopilotPluginHooks & {
|
|
|
25
25
|
export declare function buildPluginHooks(input: {
|
|
26
26
|
auth: NonNullable<CopilotPluginHooks["auth"]>;
|
|
27
27
|
loadStore?: () => Promise<StoreFile | undefined>;
|
|
28
|
-
writeStore?: (store: StoreFile) => Promise<void>;
|
|
28
|
+
writeStore?: (store: StoreFile, meta?: StoreWriteDebugMeta) => Promise<void>;
|
|
29
29
|
loadOfficialConfig?: (input: {
|
|
30
30
|
getAuth: () => Promise<CopilotAuthState | undefined>;
|
|
31
31
|
provider?: CopilotProviderConfig;
|
package/dist/plugin-hooks.js
CHANGED
|
@@ -31,7 +31,10 @@ export function buildPluginHooks(input) {
|
|
|
31
31
|
if (latestStore.lastAccountSwitchAt !== capturedLastAccountSwitchAt)
|
|
32
32
|
return;
|
|
33
33
|
delete latestStore.lastAccountSwitchAt;
|
|
34
|
-
await persistStore(latestStore
|
|
34
|
+
await persistStore(latestStore, {
|
|
35
|
+
reason: "clear-account-switch-context",
|
|
36
|
+
source: "plugin-hooks",
|
|
37
|
+
});
|
|
35
38
|
}
|
|
36
39
|
catch (error) {
|
|
37
40
|
console.warn("[plugin-hooks] failed to clear account-switch context", error);
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
-
import { type StoreFile } from "./store.js";
|
|
2
|
+
import { type StoreFile, type StoreWriteDebugMeta } from "./store.js";
|
|
3
3
|
export declare function activateAddedAccount(input: {
|
|
4
4
|
store: StoreFile;
|
|
5
5
|
name: string;
|
|
6
6
|
switchAccount: () => Promise<void>;
|
|
7
|
-
writeStore: (store: StoreFile) => Promise<void>;
|
|
7
|
+
writeStore: (store: StoreFile, meta?: StoreWriteDebugMeta) => Promise<void>;
|
|
8
8
|
now?: () => number;
|
|
9
9
|
}): Promise<void>;
|
|
10
10
|
export declare const CopilotAccountSwitcher: Plugin;
|
package/dist/plugin.js
CHANGED
|
@@ -444,7 +444,11 @@ async function switchAccount(client, entry) {
|
|
|
444
444
|
});
|
|
445
445
|
}
|
|
446
446
|
export async function activateAddedAccount(input) {
|
|
447
|
-
await input.writeStore(input.store
|
|
447
|
+
await input.writeStore(input.store, {
|
|
448
|
+
reason: "activate-added-account",
|
|
449
|
+
source: "activateAddedAccount",
|
|
450
|
+
actionType: "add",
|
|
451
|
+
});
|
|
448
452
|
await input.switchAccount();
|
|
449
453
|
await persistAccountSwitch({
|
|
450
454
|
store: input.store,
|
|
@@ -501,14 +505,17 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
501
505
|
store,
|
|
502
506
|
name,
|
|
503
507
|
switchAccount: () => switchAccount(client, entry),
|
|
504
|
-
writeStore,
|
|
508
|
+
writeStore: persistStore,
|
|
505
509
|
});
|
|
506
510
|
// fallthrough to menu
|
|
507
511
|
}
|
|
508
512
|
if (!Object.values(store.accounts).some((entry) => entry.user || entry.email || (entry.orgs && entry.orgs.length > 0))) {
|
|
509
513
|
await refreshIdentity(store);
|
|
510
514
|
dedupe(store);
|
|
511
|
-
await
|
|
515
|
+
await persistStore(store, {
|
|
516
|
+
reason: "refresh-identity-bootstrap",
|
|
517
|
+
source: "plugin.runMenu",
|
|
518
|
+
});
|
|
512
519
|
}
|
|
513
520
|
if (!isTTY()) {
|
|
514
521
|
console.log("Interactive menu requires a TTY terminal");
|
|
@@ -528,7 +535,11 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
528
535
|
store.accounts[item.name] = item.entry;
|
|
529
536
|
}
|
|
530
537
|
store.lastQuotaRefresh = now();
|
|
531
|
-
await
|
|
538
|
+
await persistStore(store, {
|
|
539
|
+
reason: "auto-refresh",
|
|
540
|
+
source: "plugin.runMenu",
|
|
541
|
+
actionType: "toggle-refresh",
|
|
542
|
+
});
|
|
532
543
|
nextRefresh = now() + (store.refreshMinutes ?? 15) * 60_000;
|
|
533
544
|
}
|
|
534
545
|
const entries = Object.entries(store.accounts);
|
|
@@ -583,7 +594,7 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
583
594
|
const active = store.active ? store.accounts[store.active] : undefined;
|
|
584
595
|
return active;
|
|
585
596
|
}
|
|
586
|
-
if (await applyMenuAction({ action, store, writeStore })) {
|
|
597
|
+
if (await applyMenuAction({ action, store, writeStore: persistStore })) {
|
|
587
598
|
continue;
|
|
588
599
|
}
|
|
589
600
|
if (action.type === "add") {
|
|
@@ -609,11 +620,15 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
609
620
|
store,
|
|
610
621
|
name: entry.name,
|
|
611
622
|
switchAccount: () => switchAccount(client, entry),
|
|
612
|
-
writeStore,
|
|
623
|
+
writeStore: persistStore,
|
|
613
624
|
});
|
|
614
625
|
}
|
|
615
626
|
else {
|
|
616
|
-
await
|
|
627
|
+
await persistStore(store, {
|
|
628
|
+
reason: "add-account-device-login",
|
|
629
|
+
source: "plugin.runMenu",
|
|
630
|
+
actionType: "add",
|
|
631
|
+
});
|
|
617
632
|
}
|
|
618
633
|
continue;
|
|
619
634
|
}
|
|
@@ -633,11 +648,15 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
633
648
|
store,
|
|
634
649
|
name: manual.entry.name,
|
|
635
650
|
switchAccount: () => switchAccount(client, manual.entry),
|
|
636
|
-
writeStore,
|
|
651
|
+
writeStore: persistStore,
|
|
637
652
|
});
|
|
638
653
|
}
|
|
639
654
|
else {
|
|
640
|
-
await
|
|
655
|
+
await persistStore(store, {
|
|
656
|
+
reason: "add-account-manual",
|
|
657
|
+
source: "plugin.runMenu",
|
|
658
|
+
actionType: "add",
|
|
659
|
+
});
|
|
641
660
|
}
|
|
642
661
|
continue;
|
|
643
662
|
}
|
|
@@ -656,19 +675,31 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
656
675
|
entry.name = buildName(entry, user?.login);
|
|
657
676
|
}
|
|
658
677
|
mergeAuth(store, imported);
|
|
659
|
-
await
|
|
678
|
+
await persistStore(store, {
|
|
679
|
+
reason: "import-auth",
|
|
680
|
+
source: "plugin.runMenu",
|
|
681
|
+
actionType: "import",
|
|
682
|
+
});
|
|
660
683
|
continue;
|
|
661
684
|
}
|
|
662
685
|
if (action.type === "refresh-identity") {
|
|
663
686
|
await refreshIdentity(store);
|
|
664
687
|
dedupe(store);
|
|
665
|
-
await
|
|
688
|
+
await persistStore(store, {
|
|
689
|
+
reason: "refresh-identity",
|
|
690
|
+
source: "plugin.runMenu",
|
|
691
|
+
actionType: "refresh-identity",
|
|
692
|
+
});
|
|
666
693
|
continue;
|
|
667
694
|
}
|
|
668
695
|
if (action.type === "toggle-refresh") {
|
|
669
696
|
store.autoRefresh = !store.autoRefresh;
|
|
670
697
|
store.refreshMinutes = store.refreshMinutes ?? 15;
|
|
671
|
-
await
|
|
698
|
+
await persistStore(store, {
|
|
699
|
+
reason: "toggle-refresh",
|
|
700
|
+
source: "plugin.runMenu",
|
|
701
|
+
actionType: "toggle-refresh",
|
|
702
|
+
});
|
|
672
703
|
continue;
|
|
673
704
|
}
|
|
674
705
|
if (action.type === "set-interval") {
|
|
@@ -676,7 +707,11 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
676
707
|
const minutes = Math.max(1, Math.min(180, Number(value)));
|
|
677
708
|
if (Number.isFinite(minutes))
|
|
678
709
|
store.refreshMinutes = minutes;
|
|
679
|
-
await
|
|
710
|
+
await persistStore(store, {
|
|
711
|
+
reason: "set-interval",
|
|
712
|
+
source: "plugin.runMenu",
|
|
713
|
+
actionType: "set-interval",
|
|
714
|
+
});
|
|
680
715
|
continue;
|
|
681
716
|
}
|
|
682
717
|
if (action.type === "quota") {
|
|
@@ -691,7 +726,11 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
691
726
|
store.accounts[item.name] = item.entry;
|
|
692
727
|
}
|
|
693
728
|
store.lastQuotaRefresh = now();
|
|
694
|
-
await
|
|
729
|
+
await persistStore(store, {
|
|
730
|
+
reason: "quota-refresh",
|
|
731
|
+
source: "plugin.runMenu",
|
|
732
|
+
actionType: "quota",
|
|
733
|
+
});
|
|
695
734
|
continue;
|
|
696
735
|
}
|
|
697
736
|
if (action.type === "check-models") {
|
|
@@ -705,13 +744,21 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
705
744
|
for (const item of updated) {
|
|
706
745
|
store.accounts[item.name] = item.entry;
|
|
707
746
|
}
|
|
708
|
-
await
|
|
747
|
+
await persistStore(store, {
|
|
748
|
+
reason: "check-models",
|
|
749
|
+
source: "plugin.runMenu",
|
|
750
|
+
actionType: "check-models",
|
|
751
|
+
});
|
|
709
752
|
continue;
|
|
710
753
|
}
|
|
711
754
|
if (action.type === "remove-all") {
|
|
712
755
|
store.accounts = {};
|
|
713
756
|
store.active = undefined;
|
|
714
|
-
await
|
|
757
|
+
await persistStore(store, {
|
|
758
|
+
reason: "remove-all",
|
|
759
|
+
source: "plugin.runMenu",
|
|
760
|
+
actionType: "remove-all",
|
|
761
|
+
});
|
|
715
762
|
continue;
|
|
716
763
|
}
|
|
717
764
|
if (action.type === "switch") {
|
|
@@ -726,7 +773,11 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
726
773
|
delete store.accounts[name];
|
|
727
774
|
if (store.active === name)
|
|
728
775
|
store.active = undefined;
|
|
729
|
-
await
|
|
776
|
+
await persistStore(store, {
|
|
777
|
+
reason: "remove-account",
|
|
778
|
+
source: "plugin.runMenu",
|
|
779
|
+
actionType: "remove",
|
|
780
|
+
});
|
|
730
781
|
continue;
|
|
731
782
|
}
|
|
732
783
|
await switchAccount(client, entry);
|
|
@@ -734,7 +785,7 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
734
785
|
store,
|
|
735
786
|
name,
|
|
736
787
|
at: now(),
|
|
737
|
-
writeStore,
|
|
788
|
+
writeStore: persistStore,
|
|
738
789
|
});
|
|
739
790
|
console.log("Switched account. If a later Copilot session hits input[*].id too long after switching, enable Copilot Network Retry from the menu.");
|
|
740
791
|
continue;
|
|
@@ -751,3 +802,4 @@ export const CopilotAccountSwitcher = async (input) => {
|
|
|
751
802
|
serverUrl,
|
|
752
803
|
});
|
|
753
804
|
};
|
|
805
|
+
const persistStore = (store, meta) => writeStore(store, { debug: meta });
|
package/dist/store.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export type StoreWriteDebugMeta = {
|
|
2
|
+
reason?: string;
|
|
3
|
+
source?: string;
|
|
4
|
+
actionType?: string;
|
|
5
|
+
inputStage?: string;
|
|
6
|
+
parsedKey?: string;
|
|
7
|
+
};
|
|
1
8
|
export type AccountEntry = {
|
|
2
9
|
name: string;
|
|
3
10
|
refresh: string;
|
|
@@ -64,4 +71,7 @@ export declare function parseStore(raw: string): StoreFile;
|
|
|
64
71
|
export declare function readStore(filePath?: string): Promise<StoreFile>;
|
|
65
72
|
export declare function readStoreSafe(filePath?: string): Promise<StoreFile | undefined>;
|
|
66
73
|
export declare function readAuth(filePath?: string): Promise<Record<string, AccountEntry>>;
|
|
67
|
-
export declare function writeStore(store: StoreFile
|
|
74
|
+
export declare function writeStore(store: StoreFile, options?: {
|
|
75
|
+
filePath?: string;
|
|
76
|
+
debug?: StoreWriteDebugMeta;
|
|
77
|
+
}): Promise<void>;
|
package/dist/store.js
CHANGED
|
@@ -4,6 +4,53 @@ import { promises as fs } from "node:fs";
|
|
|
4
4
|
import { xdgConfig, xdgData } from "xdg-basedir";
|
|
5
5
|
const filename = "copilot-accounts.json";
|
|
6
6
|
const authFile = "auth.json";
|
|
7
|
+
const defaultStoreDebugLogFile = (() => {
|
|
8
|
+
const tmp = process.env.TEMP || process.env.TMP || "/tmp";
|
|
9
|
+
return `${tmp}/opencode-copilot-store-debug.log`;
|
|
10
|
+
})();
|
|
11
|
+
function isStoreDebugEnabled() {
|
|
12
|
+
return process.env.OPENCODE_COPILOT_STORE_DEBUG === "1";
|
|
13
|
+
}
|
|
14
|
+
function buildStoreSnapshot(store) {
|
|
15
|
+
return {
|
|
16
|
+
active: store?.active ?? null,
|
|
17
|
+
accountCount: Object.keys(store?.accounts ?? {}).length,
|
|
18
|
+
loopSafetyEnabled: store?.loopSafetyEnabled ?? null,
|
|
19
|
+
networkRetryEnabled: store?.networkRetryEnabled ?? null,
|
|
20
|
+
lastAccountSwitchAt: store?.lastAccountSwitchAt ?? null,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function buildCallStack() {
|
|
24
|
+
const stack = new Error().stack?.split("\n") ?? [];
|
|
25
|
+
return stack
|
|
26
|
+
.slice(2)
|
|
27
|
+
.map((line) => line.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.slice(0, 12);
|
|
30
|
+
}
|
|
31
|
+
async function logStoreWrite(input) {
|
|
32
|
+
if (!isStoreDebugEnabled())
|
|
33
|
+
return;
|
|
34
|
+
const filePath = process.env.OPENCODE_COPILOT_STORE_DEBUG_FILE || defaultStoreDebugLogFile;
|
|
35
|
+
const event = {
|
|
36
|
+
kind: "store-write",
|
|
37
|
+
at: new Date().toISOString(),
|
|
38
|
+
targetFile: input.filePath,
|
|
39
|
+
cwd: process.cwd(),
|
|
40
|
+
argv: process.argv.slice(0, 8),
|
|
41
|
+
stack: buildCallStack(),
|
|
42
|
+
...input.debug,
|
|
43
|
+
before: buildStoreSnapshot(input.before),
|
|
44
|
+
after: buildStoreSnapshot(input.after),
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
48
|
+
await fs.appendFile(filePath, `${JSON.stringify(event)}\n`, "utf8");
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.warn("[copilot-store-debug] failed to write debug log", error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
7
54
|
export function storePath() {
|
|
8
55
|
const base = xdgConfig ?? path.join(os.homedir(), ".config");
|
|
9
56
|
return path.join(base, "opencode", filename);
|
|
@@ -81,8 +128,15 @@ export async function readAuth(filePath) {
|
|
|
81
128
|
return acc;
|
|
82
129
|
}, {});
|
|
83
130
|
}
|
|
84
|
-
export async function writeStore(store) {
|
|
85
|
-
const file = storePath();
|
|
131
|
+
export async function writeStore(store, options) {
|
|
132
|
+
const file = options?.filePath ?? storePath();
|
|
133
|
+
const before = await readStoreSafe(file);
|
|
134
|
+
await logStoreWrite({
|
|
135
|
+
filePath: file,
|
|
136
|
+
before,
|
|
137
|
+
after: store,
|
|
138
|
+
debug: options?.debug,
|
|
139
|
+
});
|
|
86
140
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
87
141
|
await fs.writeFile(file, JSON.stringify(store, null, 2), { mode: 0o600 });
|
|
88
142
|
}
|
package/dist/ui/select.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
export declare function buildSelectDebugEvent(input: {
|
|
2
|
+
stage: "key" | "result";
|
|
3
|
+
parsedKey: string | null;
|
|
4
|
+
currentValue?: unknown;
|
|
5
|
+
nextValue?: unknown;
|
|
6
|
+
}): {
|
|
7
|
+
stage: "key" | "result";
|
|
8
|
+
parsedKey: string | null;
|
|
9
|
+
currentActionType: string | null;
|
|
10
|
+
nextActionType: string | null;
|
|
11
|
+
actionType: string;
|
|
12
|
+
} | undefined;
|
|
1
13
|
export interface MenuItem<T = string> {
|
|
2
14
|
label: string;
|
|
3
15
|
value: T;
|
package/dist/ui/select.js
CHANGED
|
@@ -1,4 +1,46 @@
|
|
|
1
1
|
import { ANSI, isTTY, parseKey } from "./ansi.js";
|
|
2
|
+
const defaultSelectDebugLogFile = (() => {
|
|
3
|
+
const tmp = process.env.TEMP || process.env.TMP || "/tmp";
|
|
4
|
+
return `${tmp}/opencode-copilot-store-debug.log`;
|
|
5
|
+
})();
|
|
6
|
+
function shouldLogSuspiciousAction(actionType) {
|
|
7
|
+
return actionType === "toggle-loop-safety" || actionType === "toggle-network-retry";
|
|
8
|
+
}
|
|
9
|
+
function getActionType(value) {
|
|
10
|
+
if (!value || typeof value !== "object")
|
|
11
|
+
return undefined;
|
|
12
|
+
const actionType = value.type;
|
|
13
|
+
return typeof actionType === "string" ? actionType : undefined;
|
|
14
|
+
}
|
|
15
|
+
export function buildSelectDebugEvent(input) {
|
|
16
|
+
const currentActionType = getActionType(input.currentValue);
|
|
17
|
+
const nextActionType = getActionType(input.nextValue);
|
|
18
|
+
const actionType = nextActionType ?? currentActionType;
|
|
19
|
+
if (!shouldLogSuspiciousAction(actionType))
|
|
20
|
+
return undefined;
|
|
21
|
+
return {
|
|
22
|
+
stage: input.stage,
|
|
23
|
+
parsedKey: input.parsedKey,
|
|
24
|
+
currentActionType: currentActionType ?? null,
|
|
25
|
+
nextActionType: nextActionType ?? null,
|
|
26
|
+
actionType,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async function logSelectDebug(input) {
|
|
30
|
+
const event = buildSelectDebugEvent(input);
|
|
31
|
+
if (!event)
|
|
32
|
+
return;
|
|
33
|
+
const filePath = process.env.OPENCODE_COPILOT_STORE_DEBUG_FILE || defaultSelectDebugLogFile;
|
|
34
|
+
try {
|
|
35
|
+
const { promises: fs } = await import("node:fs");
|
|
36
|
+
const { dirname } = await import("node:path");
|
|
37
|
+
await fs.mkdir(dirname(filePath), { recursive: true });
|
|
38
|
+
await fs.appendFile(filePath, `${JSON.stringify({ kind: "select-action", at: new Date().toISOString(), ...event })}\n`, "utf8");
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.warn("[copilot-store-debug] failed to write select debug log", error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
2
44
|
const ESCAPE_TIMEOUT_MS = 50;
|
|
3
45
|
const ANSI_REGEX = new RegExp("\\x1b\\[[0-9;]*m", "g");
|
|
4
46
|
const ANSI_LEADING_REGEX = new RegExp("^\\x1b\\[[0-9;]*m");
|
|
@@ -195,16 +237,29 @@ export async function select(items, options) {
|
|
|
195
237
|
}
|
|
196
238
|
const action = parseKey(data);
|
|
197
239
|
if (action === "up") {
|
|
240
|
+
void logSelectDebug({ stage: "key", parsedKey: action, currentValue: items[cursor]?.value });
|
|
198
241
|
cursor = findNextSelectable(cursor, -1);
|
|
199
242
|
render();
|
|
200
243
|
return;
|
|
201
244
|
}
|
|
202
245
|
if (action === "down") {
|
|
246
|
+
void logSelectDebug({ stage: "key", parsedKey: action, currentValue: items[cursor]?.value });
|
|
203
247
|
cursor = findNextSelectable(cursor, 1);
|
|
204
248
|
render();
|
|
205
249
|
return;
|
|
206
250
|
}
|
|
207
251
|
if (action === "enter") {
|
|
252
|
+
void logSelectDebug({
|
|
253
|
+
stage: "key",
|
|
254
|
+
parsedKey: action,
|
|
255
|
+
currentValue: items[cursor]?.value,
|
|
256
|
+
});
|
|
257
|
+
void logSelectDebug({
|
|
258
|
+
stage: "result",
|
|
259
|
+
parsedKey: action,
|
|
260
|
+
currentValue: items[cursor]?.value,
|
|
261
|
+
nextValue: items[cursor]?.value,
|
|
262
|
+
});
|
|
208
263
|
finish(items[cursor]?.value ?? null);
|
|
209
264
|
return;
|
|
210
265
|
}
|