opencode-oncall 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.
Files changed (105) hide show
  1. package/LICENSE +151 -0
  2. package/README.md +50 -0
  3. package/dist/common-settings-actions.d.ts +15 -0
  4. package/dist/common-settings-actions.js +48 -0
  5. package/dist/common-settings-store.d.ts +1 -0
  6. package/dist/common-settings-store.js +1 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +1 -0
  9. package/dist/plugin-hooks.d.ts +51 -0
  10. package/dist/plugin-hooks.js +288 -0
  11. package/dist/plugin.d.ts +10 -0
  12. package/dist/plugin.js +115 -0
  13. package/dist/settings-store.d.ts +50 -0
  14. package/dist/settings-store.js +214 -0
  15. package/dist/store-paths.d.ts +16 -0
  16. package/dist/store-paths.js +61 -0
  17. package/dist/ui/wechat-menu.d.ts +26 -0
  18. package/dist/ui/wechat-menu.js +90 -0
  19. package/dist/wechat/bind-flow.d.ts +29 -0
  20. package/dist/wechat/bind-flow.js +207 -0
  21. package/dist/wechat/bridge.d.ts +136 -0
  22. package/dist/wechat/bridge.js +1059 -0
  23. package/dist/wechat/broker-client.d.ts +23 -0
  24. package/dist/wechat/broker-client.js +274 -0
  25. package/dist/wechat/broker-endpoint.d.ts +21 -0
  26. package/dist/wechat/broker-endpoint.js +78 -0
  27. package/dist/wechat/broker-entry.d.ts +123 -0
  28. package/dist/wechat/broker-entry.js +1321 -0
  29. package/dist/wechat/broker-launcher.d.ts +37 -0
  30. package/dist/wechat/broker-launcher.js +418 -0
  31. package/dist/wechat/broker-mutation-queue.d.ts +93 -0
  32. package/dist/wechat/broker-mutation-queue.js +126 -0
  33. package/dist/wechat/broker-server.d.ts +86 -0
  34. package/dist/wechat/broker-server.js +1340 -0
  35. package/dist/wechat/broker-state-store.d.ts +335 -0
  36. package/dist/wechat/broker-state-store.js +1964 -0
  37. package/dist/wechat/command-parser.d.ts +18 -0
  38. package/dist/wechat/command-parser.js +58 -0
  39. package/dist/wechat/compat/jiti-loader.d.ts +27 -0
  40. package/dist/wechat/compat/jiti-loader.js +118 -0
  41. package/dist/wechat/compat/openclaw-account-helpers.d.ts +29 -0
  42. package/dist/wechat/compat/openclaw-account-helpers.js +60 -0
  43. package/dist/wechat/compat/openclaw-bind-helpers.d.ts +29 -0
  44. package/dist/wechat/compat/openclaw-bind-helpers.js +169 -0
  45. package/dist/wechat/compat/openclaw-guided-smoke.d.ts +180 -0
  46. package/dist/wechat/compat/openclaw-guided-smoke.js +1134 -0
  47. package/dist/wechat/compat/openclaw-public-entry.d.ts +33 -0
  48. package/dist/wechat/compat/openclaw-public-entry.js +62 -0
  49. package/dist/wechat/compat/openclaw-public-helpers.d.ts +70 -0
  50. package/dist/wechat/compat/openclaw-public-helpers.js +68 -0
  51. package/dist/wechat/compat/openclaw-qr-gateway.d.ts +15 -0
  52. package/dist/wechat/compat/openclaw-qr-gateway.js +39 -0
  53. package/dist/wechat/compat/openclaw-smoke.d.ts +48 -0
  54. package/dist/wechat/compat/openclaw-smoke.js +100 -0
  55. package/dist/wechat/compat/openclaw-sync-buf.d.ts +24 -0
  56. package/dist/wechat/compat/openclaw-sync-buf.js +80 -0
  57. package/dist/wechat/compat/openclaw-updates-send.d.ts +47 -0
  58. package/dist/wechat/compat/openclaw-updates-send.js +38 -0
  59. package/dist/wechat/compat/qrcode-terminal-loader.d.ts +12 -0
  60. package/dist/wechat/compat/qrcode-terminal-loader.js +16 -0
  61. package/dist/wechat/compat/slash-guard.d.ts +11 -0
  62. package/dist/wechat/compat/slash-guard.js +24 -0
  63. package/dist/wechat/dead-letter-store.d.ts +48 -0
  64. package/dist/wechat/dead-letter-store.js +224 -0
  65. package/dist/wechat/debug-bundle-collector.d.ts +49 -0
  66. package/dist/wechat/debug-bundle-collector.js +580 -0
  67. package/dist/wechat/debug-bundle-flow.d.ts +37 -0
  68. package/dist/wechat/debug-bundle-flow.js +180 -0
  69. package/dist/wechat/debug-bundle-redaction.d.ts +14 -0
  70. package/dist/wechat/debug-bundle-redaction.js +339 -0
  71. package/dist/wechat/handle.d.ts +10 -0
  72. package/dist/wechat/handle.js +57 -0
  73. package/dist/wechat/ipc-auth.d.ts +6 -0
  74. package/dist/wechat/ipc-auth.js +39 -0
  75. package/dist/wechat/latest-account-state-store.d.ts +8 -0
  76. package/dist/wechat/latest-account-state-store.js +38 -0
  77. package/dist/wechat/notification-dispatcher.d.ts +34 -0
  78. package/dist/wechat/notification-dispatcher.js +266 -0
  79. package/dist/wechat/notification-format.d.ts +15 -0
  80. package/dist/wechat/notification-format.js +196 -0
  81. package/dist/wechat/notification-store.d.ts +72 -0
  82. package/dist/wechat/notification-store.js +807 -0
  83. package/dist/wechat/notification-types.d.ts +37 -0
  84. package/dist/wechat/notification-types.js +1 -0
  85. package/dist/wechat/openclaw-account-adapter.d.ts +30 -0
  86. package/dist/wechat/openclaw-account-adapter.js +60 -0
  87. package/dist/wechat/operator-store.d.ts +9 -0
  88. package/dist/wechat/operator-store.js +69 -0
  89. package/dist/wechat/protocol.d.ts +150 -0
  90. package/dist/wechat/protocol.js +197 -0
  91. package/dist/wechat/question-interaction.d.ts +24 -0
  92. package/dist/wechat/question-interaction.js +180 -0
  93. package/dist/wechat/request-store.d.ts +108 -0
  94. package/dist/wechat/request-store.js +669 -0
  95. package/dist/wechat/session-digest.d.ts +50 -0
  96. package/dist/wechat/session-digest.js +167 -0
  97. package/dist/wechat/state-paths.d.ts +26 -0
  98. package/dist/wechat/state-paths.js +92 -0
  99. package/dist/wechat/status-format.d.ts +26 -0
  100. package/dist/wechat/status-format.js +616 -0
  101. package/dist/wechat/token-store.d.ts +20 -0
  102. package/dist/wechat/token-store.js +193 -0
  103. package/dist/wechat/wechat-status-runtime.d.ts +89 -0
  104. package/dist/wechat/wechat-status-runtime.js +518 -0
  105. package/package.json +74 -0
package/dist/plugin.js ADDED
@@ -0,0 +1,115 @@
1
+ import { appendFile } from "node:fs/promises";
2
+ import { buildPluginHooks, isBridgeCapableBrokerOwnerInput, } from "./plugin-hooks.js";
3
+ import { readWechatSettingsStore } from "./settings-store.js";
4
+ import { buildWechatSettingsMenuEntry, loadWechatMenuItems, } from "./ui/wechat-menu.js";
5
+ import { connectOrSpawnBroker } from "./wechat/broker-launcher.js";
6
+ import { brokerStartupDiagnosticsPath, ensureWechatStateLayout, } from "./wechat/state-paths.js";
7
+ let brokerEnsurePromiseInProcess;
8
+ let brokerEnsureSucceededInProcess = false;
9
+ function toWechatBrokerConnection(value) {
10
+ if (value &&
11
+ typeof value === "object" &&
12
+ typeof value.endpoint === "string") {
13
+ return { endpoint: value.endpoint };
14
+ }
15
+ throw new Error("Wechat broker 启动结果缺少 endpoint");
16
+ }
17
+ function formatBrokerStartupError(error) {
18
+ if (error instanceof Error)
19
+ return error.stack ?? `${error.name}: ${error.message}`;
20
+ if (typeof error === "string")
21
+ return error;
22
+ try {
23
+ return JSON.stringify(error);
24
+ }
25
+ catch {
26
+ return String(error);
27
+ }
28
+ }
29
+ async function recordBrokerStartupFailure(input) {
30
+ const line = JSON.stringify({
31
+ at: new Date().toISOString(),
32
+ provider: "opencode-wechat",
33
+ reason: formatBrokerStartupError(input.error),
34
+ });
35
+ try {
36
+ await ensureWechatStateLayout();
37
+ await appendFile(input.diagnosticsPath, `${line}\n`, "utf8");
38
+ }
39
+ catch { }
40
+ try {
41
+ await input.showToast?.({
42
+ body: {
43
+ message: `Wechat broker 启动失败,已写入诊断文件:${input.diagnosticsPath}`,
44
+ variant: "warning",
45
+ },
46
+ });
47
+ }
48
+ catch { }
49
+ }
50
+ export function buildWechatPluginMetadata() {
51
+ return {
52
+ settingsEntry: buildWechatSettingsMenuEntry(),
53
+ };
54
+ }
55
+ async function ensureBrokerStarted(input) {
56
+ if (!brokerEnsurePromiseInProcess) {
57
+ const starter = input.ensureWechatBrokerStarted ?? connectOrSpawnBroker;
58
+ const promise = Promise.resolve()
59
+ .then(() => starter())
60
+ .then((result) => toWechatBrokerConnection(result))
61
+ .then((connection) => {
62
+ brokerEnsureSucceededInProcess = true;
63
+ return connection;
64
+ });
65
+ brokerEnsurePromiseInProcess = promise;
66
+ void promise
67
+ .catch((error) => recordBrokerStartupFailure({
68
+ diagnosticsPath: input.diagnosticsPath,
69
+ error,
70
+ showToast: input.showToast,
71
+ }))
72
+ .finally(() => {
73
+ if (brokerEnsurePromiseInProcess === promise)
74
+ brokerEnsurePromiseInProcess = undefined;
75
+ });
76
+ }
77
+ return brokerEnsurePromiseInProcess;
78
+ }
79
+ export const OpenCodeWechat = async (input) => {
80
+ const diagnosticsPath = brokerStartupDiagnosticsPath();
81
+ const testSeams = input;
82
+ const showToast = testSeams.client?.tui?.showToast;
83
+ const injectedEnsureBroker = testSeams.ensureWechatBrokerStarted;
84
+ const createWechatBridgeLifecycleImpl = testSeams.createWechatBridgeLifecycleImpl;
85
+ await readWechatSettingsStore().catch(() => undefined);
86
+ await loadWechatMenuItems().catch(() => []);
87
+ let initialWechatBrokerPromise;
88
+ if (isBridgeCapableBrokerOwnerInput({
89
+ client: input.client,
90
+ serverUrl: input.serverUrl,
91
+ }) &&
92
+ !brokerEnsureSucceededInProcess) {
93
+ initialWechatBrokerPromise = ensureBrokerStarted({
94
+ ensureWechatBrokerStarted: injectedEnsureBroker,
95
+ diagnosticsPath,
96
+ showToast,
97
+ });
98
+ }
99
+ return buildPluginHooks({
100
+ client: input.client,
101
+ project: input.project,
102
+ directory: input.directory,
103
+ serverUrl: input.serverUrl,
104
+ initialWechatBrokerPromise,
105
+ createWechatBridgeLifecycleImpl,
106
+ onFallbackToast: showToast
107
+ ? (payload) => showToast({
108
+ body: {
109
+ message: payload.message,
110
+ variant: "warning",
111
+ },
112
+ }).then(() => undefined)
113
+ : undefined,
114
+ });
115
+ };
@@ -0,0 +1,50 @@
1
+ export type WechatBinding = {
2
+ accountId: string;
3
+ userId?: string;
4
+ name?: string;
5
+ enabled?: boolean;
6
+ configured?: boolean;
7
+ boundAt?: number;
8
+ };
9
+ export type WechatSettings = {
10
+ primaryBinding?: WechatBinding;
11
+ notifications: {
12
+ enabled: boolean;
13
+ question: boolean;
14
+ permission: boolean;
15
+ sessionError: boolean;
16
+ retryError: boolean;
17
+ };
18
+ future?: {
19
+ accounts?: WechatBinding[];
20
+ };
21
+ };
22
+ export type WechatSettingsStore = {
23
+ wechat: WechatSettings;
24
+ };
25
+ export type WechatNotificationDispatchSettings = {
26
+ targetUserId?: string;
27
+ targetAccountId?: string;
28
+ notifications: WechatSettings["notifications"];
29
+ };
30
+ export declare function normalizeWechatSettingsStore(input: unknown): WechatSettingsStore;
31
+ export declare function settingsPath(): string;
32
+ export declare function readWechatSettingsStore(options?: {
33
+ filePath?: string;
34
+ legacySettingsFilePath?: string;
35
+ }): Promise<WechatSettingsStore>;
36
+ export declare function readWechatSettingsStoreSync(): WechatSettingsStore | undefined;
37
+ export declare function writeWechatSettingsStore(store: WechatSettingsStore, options?: {
38
+ filePath?: string;
39
+ }): Promise<void>;
40
+ export declare function readWechatNotificationDispatchSettings(options?: {
41
+ filePath?: string;
42
+ legacySettingsFilePath?: string;
43
+ }): Promise<WechatNotificationDispatchSettings>;
44
+ export type CommonSettingsStore = WechatSettingsStore;
45
+ export type WechatMenuSettings = WechatSettings;
46
+ export declare const readCommonSettingsStore: typeof readWechatSettingsStore;
47
+ export declare const readCommonSettingsStoreSync: typeof readWechatSettingsStoreSync;
48
+ export declare const writeCommonSettingsStore: typeof writeWechatSettingsStore;
49
+ export declare const commonSettingsPath: typeof settingsPath;
50
+ export declare const normalizeCommonSettingsStore: typeof normalizeWechatSettingsStore;
@@ -0,0 +1,214 @@
1
+ import { constants as fsConstants } from "node:fs";
2
+ import { access, copyFile, mkdir, readdir, readFile, stat, writeFile, } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { opencodeWechatConfigDir, wechatBridgeDiagnosticsPath, wechatBrokerStateStorePath, wechatDeadLetterDir, wechatInstancesDir, wechatLatestAccountPath, wechatLegacyConfigDir, wechatLegacySettingsPath, wechatNotificationsDir, wechatOperatorPath, wechatRequestsDir, wechatSettingsPath, wechatStatusRuntimeDiagnosticsPath, wechatTokensDir, } from "./store-paths.js";
5
+ const STATE_COPY_TARGETS = [
6
+ ["operator.json", wechatOperatorPath],
7
+ ["broker-state-store.json", wechatBrokerStateStorePath],
8
+ ["latest-account.json", wechatLatestAccountPath],
9
+ ["tokens", wechatTokensDir],
10
+ ["requests", wechatRequestsDir],
11
+ ["notifications", wechatNotificationsDir],
12
+ ["dead-letter", wechatDeadLetterDir],
13
+ ["instances", wechatInstancesDir],
14
+ ["wechat-bridge.diagnostics.jsonl", wechatBridgeDiagnosticsPath],
15
+ [
16
+ "wechat-status-runtime.diagnostics.jsonl",
17
+ wechatStatusRuntimeDiagnosticsPath,
18
+ ],
19
+ ];
20
+ function isRecord(value) {
21
+ return typeof value === "object" && value !== null;
22
+ }
23
+ function booleanOrDefault(value, fallback) {
24
+ return typeof value === "boolean" ? value : fallback;
25
+ }
26
+ function normalizeWechatBinding(input) {
27
+ if (!isRecord(input) ||
28
+ typeof input.accountId !== "string" ||
29
+ input.accountId.length === 0) {
30
+ return undefined;
31
+ }
32
+ return {
33
+ accountId: input.accountId,
34
+ ...(typeof input.userId === "string" ? { userId: input.userId } : {}),
35
+ ...(typeof input.name === "string" ? { name: input.name } : {}),
36
+ ...(typeof input.enabled === "boolean" ? { enabled: input.enabled } : {}),
37
+ ...(typeof input.configured === "boolean"
38
+ ? { configured: input.configured }
39
+ : {}),
40
+ ...(typeof input.boundAt === "number" && Number.isFinite(input.boundAt)
41
+ ? { boundAt: input.boundAt }
42
+ : {}),
43
+ };
44
+ }
45
+ function normalizeFutureAccounts(input) {
46
+ if (!isRecord(input) || !Array.isArray(input.accounts))
47
+ return undefined;
48
+ const accounts = input.accounts
49
+ .map((account) => normalizeWechatBinding(account))
50
+ .filter((account) => Boolean(account));
51
+ return accounts.length > 0 ? { accounts } : undefined;
52
+ }
53
+ function normalizeWechatSettings(input) {
54
+ const source = isRecord(input) ? input : {};
55
+ const notificationsSource = isRecord(source.notifications)
56
+ ? source.notifications
57
+ : {};
58
+ const primaryBinding = normalizeWechatBinding(source.primaryBinding);
59
+ const future = normalizeFutureAccounts(source.future);
60
+ return {
61
+ ...(primaryBinding ? { primaryBinding } : {}),
62
+ notifications: {
63
+ enabled: booleanOrDefault(notificationsSource.enabled, true),
64
+ question: booleanOrDefault(notificationsSource.question, true),
65
+ permission: booleanOrDefault(notificationsSource.permission, true),
66
+ sessionError: booleanOrDefault(notificationsSource.sessionError, true),
67
+ retryError: booleanOrDefault(notificationsSource.retryError, true),
68
+ },
69
+ ...(future ? { future } : {}),
70
+ };
71
+ }
72
+ export function normalizeWechatSettingsStore(input) {
73
+ const source = isRecord(input) ? input : {};
74
+ return {
75
+ wechat: normalizeWechatSettings(source.wechat),
76
+ };
77
+ }
78
+ function migrateLegacySettings(input) {
79
+ const source = isRecord(input) ? input : {};
80
+ const nestedWechat = normalizeWechatSettings(source.wechat);
81
+ const notificationsSource = isRecord(source.wechat) && isRecord(source.wechat.notifications)
82
+ ? source.wechat.notifications
83
+ : {};
84
+ return {
85
+ wechat: {
86
+ ...(nestedWechat.primaryBinding
87
+ ? { primaryBinding: nestedWechat.primaryBinding }
88
+ : {}),
89
+ notifications: {
90
+ enabled: booleanOrDefault(notificationsSource.enabled, source.wechatNotificationsEnabled !== false),
91
+ question: booleanOrDefault(notificationsSource.question, source.wechatQuestionNotifyEnabled !== false),
92
+ permission: booleanOrDefault(notificationsSource.permission, source.wechatPermissionNotifyEnabled !== false),
93
+ sessionError: booleanOrDefault(notificationsSource.sessionError, source.wechatSessionErrorNotifyEnabled !== false),
94
+ retryError: booleanOrDefault(notificationsSource.retryError, source.wechatRetryErrorNotifyEnabled !== false),
95
+ },
96
+ ...(nestedWechat.future ? { future: nestedWechat.future } : {}),
97
+ },
98
+ };
99
+ }
100
+ async function pathExists(filePath) {
101
+ try {
102
+ await access(filePath, fsConstants.F_OK);
103
+ return true;
104
+ }
105
+ catch (error) {
106
+ if (error.code === "ENOENT")
107
+ return false;
108
+ throw error;
109
+ }
110
+ }
111
+ async function readJsonFile(filePath) {
112
+ try {
113
+ return JSON.parse(await readFile(filePath, "utf8"));
114
+ }
115
+ catch (error) {
116
+ if (error.code === "ENOENT")
117
+ return undefined;
118
+ throw error;
119
+ }
120
+ }
121
+ async function copyDirectory(source, target) {
122
+ await mkdir(target, { recursive: true });
123
+ const entries = await readdir(source, { withFileTypes: true });
124
+ for (const entry of entries) {
125
+ const sourcePath = path.join(source, entry.name);
126
+ const targetPath = path.join(target, entry.name);
127
+ if (entry.isDirectory()) {
128
+ await copyDirectory(sourcePath, targetPath);
129
+ continue;
130
+ }
131
+ if (entry.isFile()) {
132
+ await mkdir(path.dirname(targetPath), { recursive: true });
133
+ await copyFile(sourcePath, targetPath, fsConstants.COPYFILE_EXCL).catch((error) => {
134
+ if (error.code !== "EEXIST")
135
+ throw error;
136
+ });
137
+ }
138
+ }
139
+ }
140
+ async function copyLegacyPath(relativePath, targetPath) {
141
+ const sourcePath = path.join(wechatLegacyConfigDir(), relativePath);
142
+ let info;
143
+ try {
144
+ info = await stat(sourcePath);
145
+ }
146
+ catch (error) {
147
+ if (error.code === "ENOENT")
148
+ return;
149
+ throw error;
150
+ }
151
+ if (info.isDirectory()) {
152
+ await copyDirectory(sourcePath, targetPath);
153
+ return;
154
+ }
155
+ if (info.isFile()) {
156
+ await mkdir(path.dirname(targetPath), { recursive: true });
157
+ await copyFile(sourcePath, targetPath, fsConstants.COPYFILE_EXCL).catch((error) => {
158
+ if (error.code !== "EEXIST")
159
+ throw error;
160
+ });
161
+ }
162
+ }
163
+ async function migrateLegacyWechatState() {
164
+ await mkdir(opencodeWechatConfigDir(), { recursive: true });
165
+ for (const [relativePath, targetPath] of STATE_COPY_TARGETS) {
166
+ await copyLegacyPath(relativePath, targetPath());
167
+ }
168
+ }
169
+ export function settingsPath() {
170
+ return wechatSettingsPath();
171
+ }
172
+ export async function readWechatSettingsStore(options) {
173
+ const filePath = options?.filePath ?? wechatSettingsPath();
174
+ const current = await readJsonFile(filePath);
175
+ if (current !== undefined) {
176
+ return normalizeWechatSettingsStore(current);
177
+ }
178
+ const legacyFilePath = options?.legacySettingsFilePath ?? wechatLegacySettingsPath();
179
+ const legacy = await readJsonFile(legacyFilePath);
180
+ const migrated = migrateLegacySettings(legacy);
181
+ if (legacy !== undefined || (await pathExists(wechatLegacyConfigDir()))) {
182
+ await migrateLegacyWechatState();
183
+ await writeWechatSettingsStore(migrated, { filePath });
184
+ }
185
+ return migrated;
186
+ }
187
+ export function readWechatSettingsStoreSync() {
188
+ return undefined;
189
+ }
190
+ export async function writeWechatSettingsStore(store, options) {
191
+ const filePath = options?.filePath ?? wechatSettingsPath();
192
+ const normalized = normalizeWechatSettingsStore(store);
193
+ await mkdir(path.dirname(filePath), { recursive: true });
194
+ await writeFile(filePath, JSON.stringify(normalized, null, 2), {
195
+ mode: 0o600,
196
+ });
197
+ }
198
+ export async function readWechatNotificationDispatchSettings(options) {
199
+ const settings = await readWechatSettingsStore(options);
200
+ return {
201
+ ...(typeof settings.wechat.primaryBinding?.userId === "string"
202
+ ? { targetUserId: settings.wechat.primaryBinding.userId }
203
+ : {}),
204
+ ...(typeof settings.wechat.primaryBinding?.accountId === "string"
205
+ ? { targetAccountId: settings.wechat.primaryBinding.accountId }
206
+ : {}),
207
+ notifications: settings.wechat.notifications,
208
+ };
209
+ }
210
+ export const readCommonSettingsStore = readWechatSettingsStore;
211
+ export const readCommonSettingsStoreSync = readWechatSettingsStoreSync;
212
+ export const writeCommonSettingsStore = writeWechatSettingsStore;
213
+ export const commonSettingsPath = settingsPath;
214
+ export const normalizeCommonSettingsStore = normalizeWechatSettingsStore;
@@ -0,0 +1,16 @@
1
+ export declare function opencodeWechatConfigDir(): string;
2
+ export declare function wechatSettingsPath(): string;
3
+ export declare function wechatLegacySettingsPath(): string;
4
+ export declare function wechatLegacyConfigDir(): string;
5
+ export declare function wechatOperatorPath(): string;
6
+ export declare function wechatBrokerStateStorePath(): string;
7
+ export declare function wechatLatestAccountPath(): string;
8
+ export declare function wechatRequestsDir(): string;
9
+ export declare function wechatNotificationsDir(): string;
10
+ export declare function wechatDeadLetterDir(): string;
11
+ export declare function wechatInstancesDir(): string;
12
+ export declare function wechatTokensDir(): string;
13
+ export declare function wechatBridgeDiagnosticsPath(): string;
14
+ export declare function wechatStatusRuntimeDiagnosticsPath(): string;
15
+ export declare function commonSettingsPath(): string;
16
+ export declare function wechatConfigDir(): string;
@@ -0,0 +1,61 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { xdgConfig } from "xdg-basedir";
4
+ function configBaseDir() {
5
+ const override = process.env.XDG_CONFIG_HOME;
6
+ if (typeof override === "string" && override.trim().length > 0) {
7
+ return override.trim();
8
+ }
9
+ return xdgConfig ?? path.join(os.homedir(), ".config");
10
+ }
11
+ function opencodeConfigDir() {
12
+ return path.join(configBaseDir(), "opencode");
13
+ }
14
+ export function opencodeWechatConfigDir() {
15
+ return path.join(opencodeConfigDir(), "opencode-wechat");
16
+ }
17
+ export function wechatSettingsPath() {
18
+ return path.join(opencodeWechatConfigDir(), "settings.json");
19
+ }
20
+ export function wechatLegacySettingsPath() {
21
+ return path.join(opencodeConfigDir(), "account-switcher", "settings.json");
22
+ }
23
+ export function wechatLegacyConfigDir() {
24
+ return path.join(opencodeConfigDir(), "account-switcher", "wechat");
25
+ }
26
+ export function wechatOperatorPath() {
27
+ return path.join(opencodeWechatConfigDir(), "operator.json");
28
+ }
29
+ export function wechatBrokerStateStorePath() {
30
+ return path.join(opencodeWechatConfigDir(), "broker-state-store.json");
31
+ }
32
+ export function wechatLatestAccountPath() {
33
+ return path.join(opencodeWechatConfigDir(), "latest-account.json");
34
+ }
35
+ export function wechatRequestsDir() {
36
+ return path.join(opencodeWechatConfigDir(), "requests");
37
+ }
38
+ export function wechatNotificationsDir() {
39
+ return path.join(opencodeWechatConfigDir(), "notifications");
40
+ }
41
+ export function wechatDeadLetterDir() {
42
+ return path.join(opencodeWechatConfigDir(), "dead-letter");
43
+ }
44
+ export function wechatInstancesDir() {
45
+ return path.join(opencodeWechatConfigDir(), "instances");
46
+ }
47
+ export function wechatTokensDir() {
48
+ return path.join(opencodeWechatConfigDir(), "tokens");
49
+ }
50
+ export function wechatBridgeDiagnosticsPath() {
51
+ return path.join(opencodeWechatConfigDir(), "wechat-bridge.diagnostics.jsonl");
52
+ }
53
+ export function wechatStatusRuntimeDiagnosticsPath() {
54
+ return path.join(opencodeWechatConfigDir(), "wechat-status-runtime.diagnostics.jsonl");
55
+ }
56
+ export function commonSettingsPath() {
57
+ return wechatSettingsPath();
58
+ }
59
+ export function wechatConfigDir() {
60
+ return opencodeWechatConfigDir();
61
+ }
@@ -0,0 +1,26 @@
1
+ import type { WechatSettingsStore } from "../settings-store.js";
2
+ import type { OperatorBinding } from "../wechat/operator-store.js";
3
+ export type WechatSettingsMenuEntry = {
4
+ title: "OpenCode WeChat";
5
+ value: "opencode-wechat.settings";
6
+ category: "OpenCode";
7
+ };
8
+ export type WechatMenuActionType = "wechat-bind" | "wechat-rebind" | "toggle-wechat-notifications" | "toggle-wechat-question-notifications" | "toggle-wechat-permission-notifications" | "toggle-wechat-session-error-notifications" | "toggle-wechat-retry-error-notifications" | "wechat-export-debug-bundle-sanitized" | "wechat-export-debug-bundle-full" | "wechat-openclaw-dry-run-command";
9
+ export type WechatMenuItem = {
10
+ label: string;
11
+ value: WechatMenuActionType | "noop";
12
+ hint?: string;
13
+ disabled?: boolean;
14
+ };
15
+ export type BuildWechatMenuInput = {
16
+ settings?: WechatSettingsStore;
17
+ operatorBinding?: OperatorBinding;
18
+ readSettings?: () => Promise<WechatSettingsStore>;
19
+ readOperatorBinding?: () => Promise<OperatorBinding | undefined>;
20
+ };
21
+ export declare function buildWechatSettingsMenuEntry(): WechatSettingsMenuEntry;
22
+ export declare function buildWechatMenuItems(input: {
23
+ settings: WechatSettingsStore;
24
+ operatorBinding?: OperatorBinding;
25
+ }): WechatMenuItem[];
26
+ export declare function loadWechatMenuItems(input?: BuildWechatMenuInput): Promise<WechatMenuItem[]>;
@@ -0,0 +1,90 @@
1
+ import { readWechatSettingsStore } from "../settings-store.js";
2
+ import { readOperatorBinding } from "../wechat/operator-store.js";
3
+ function enabledLabel(value) {
4
+ return value ? "已开启" : "已关闭";
5
+ }
6
+ function bindingRows(binding) {
7
+ if (!binding) {
8
+ return [{ label: "当前绑定状态:未绑定", value: "noop", disabled: true }];
9
+ }
10
+ const accountId = "accountId" in binding ? binding.accountId : binding.wechatAccountId;
11
+ return [
12
+ { label: "当前绑定状态:已绑定", value: "noop", disabled: true },
13
+ { label: `accountId: ${accountId}`, value: "noop", disabled: true },
14
+ ...("name" in binding && binding.name
15
+ ? [
16
+ {
17
+ label: `name: ${binding.name}`,
18
+ value: "noop",
19
+ disabled: true,
20
+ },
21
+ ]
22
+ : []),
23
+ ...(binding.userId
24
+ ? [
25
+ {
26
+ label: `userId: ${binding.userId}`,
27
+ value: "noop",
28
+ disabled: true,
29
+ },
30
+ ]
31
+ : []),
32
+ ];
33
+ }
34
+ export function buildWechatSettingsMenuEntry() {
35
+ return {
36
+ title: "OpenCode WeChat",
37
+ value: "opencode-wechat.settings",
38
+ category: "OpenCode",
39
+ };
40
+ }
41
+ export function buildWechatMenuItems(input) {
42
+ const notifications = input.settings.wechat.notifications;
43
+ const binding = input.settings.wechat.primaryBinding ?? input.operatorBinding;
44
+ const bindAction = binding
45
+ ? "wechat-rebind"
46
+ : "wechat-bind";
47
+ return [
48
+ ...bindingRows(binding),
49
+ { label: binding ? "重绑微信" : "绑定微信", value: bindAction },
50
+ {
51
+ label: `微信通知总开关:${enabledLabel(notifications.enabled)}`,
52
+ value: "toggle-wechat-notifications",
53
+ },
54
+ {
55
+ label: `question 通知:${enabledLabel(notifications.question)}`,
56
+ value: "toggle-wechat-question-notifications",
57
+ },
58
+ {
59
+ label: `permission 通知:${enabledLabel(notifications.permission)}`,
60
+ value: "toggle-wechat-permission-notifications",
61
+ },
62
+ {
63
+ label: `session error 通知:${enabledLabel(notifications.sessionError)}`,
64
+ value: "toggle-wechat-session-error-notifications",
65
+ },
66
+ {
67
+ label: `retry-error 通知:${enabledLabel(notifications.retryError)}`,
68
+ value: "toggle-wechat-retry-error-notifications",
69
+ },
70
+ {
71
+ label: "导出脱敏 debug bundle",
72
+ value: "wechat-export-debug-bundle-sanitized",
73
+ },
74
+ {
75
+ label: "导出完整 debug bundle",
76
+ value: "wechat-export-debug-bundle-full",
77
+ },
78
+ {
79
+ label: "显示 OpenClaw dry-run 命令",
80
+ value: "wechat-openclaw-dry-run-command",
81
+ hint: "npm run wechat:smoke:real-account -- --dry-run",
82
+ },
83
+ ];
84
+ }
85
+ export async function loadWechatMenuItems(input = {}) {
86
+ const settings = input.settings ?? (await (input.readSettings ?? readWechatSettingsStore)());
87
+ const operatorBinding = input.operatorBinding ??
88
+ (await (input.readOperatorBinding ?? readOperatorBinding)());
89
+ return buildWechatMenuItems({ settings, operatorBinding });
90
+ }
@@ -0,0 +1,29 @@
1
+ import type { WechatSettingsStore } from "../settings-store.js";
2
+ import { loadOpenClawWeixinBindHelpers } from "./compat/openclaw-bind-helpers.js";
3
+ import { bindOperator, readOperatorBinding, rebindOperator, resetOperatorBinding } from "./operator-store.js";
4
+ type BindAction = "wechat-bind" | "wechat-rebind";
5
+ type WechatBindFlowResult = {
6
+ accountId: string;
7
+ userId: string;
8
+ name?: string;
9
+ enabled?: boolean;
10
+ configured?: boolean;
11
+ boundAt: number;
12
+ };
13
+ type WechatBindFlowInput = {
14
+ action: BindAction;
15
+ loadPublicHelpers?: typeof loadOpenClawWeixinBindHelpers;
16
+ bindOperator?: typeof bindOperator;
17
+ rebindOperator?: typeof rebindOperator;
18
+ readOperatorBinding?: typeof readOperatorBinding;
19
+ resetOperatorBinding?: typeof resetOperatorBinding;
20
+ readCommonSettings: () => Promise<WechatSettingsStore>;
21
+ writeCommonSettings: (settings: WechatSettingsStore) => Promise<void>;
22
+ writeLine?: (line: string) => Promise<void>;
23
+ renderQrTerminal?: (input: {
24
+ value: string;
25
+ }) => Promise<string | undefined>;
26
+ now?: () => number;
27
+ };
28
+ export declare function runWechatBindFlow(input: WechatBindFlowInput): Promise<WechatBindFlowResult>;
29
+ export {};