opencode-copilot-account-switcher 0.13.6 → 0.14.1

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 (58) hide show
  1. package/dist/common-settings-actions.d.ts +1 -1
  2. package/dist/common-settings-actions.js +54 -0
  3. package/dist/common-settings-store.d.ts +25 -0
  4. package/dist/common-settings-store.js +81 -0
  5. package/dist/menu-runtime.js +12 -1
  6. package/dist/plugin-hooks.d.ts +8 -0
  7. package/dist/plugin-hooks.js +96 -0
  8. package/dist/providers/codex-menu-adapter.js +14 -1
  9. package/dist/providers/copilot-menu-adapter.js +13 -0
  10. package/dist/store-paths.d.ts +1 -0
  11. package/dist/store-paths.js +3 -0
  12. package/dist/ui/menu.d.ts +73 -34
  13. package/dist/ui/menu.js +195 -0
  14. package/dist/wechat/bind-flow.d.ts +25 -0
  15. package/dist/wechat/bind-flow.js +101 -0
  16. package/dist/wechat/bridge.d.ts +69 -0
  17. package/dist/wechat/bridge.js +180 -0
  18. package/dist/wechat/broker-client.d.ts +33 -0
  19. package/dist/wechat/broker-client.js +257 -0
  20. package/dist/wechat/broker-entry.d.ts +17 -0
  21. package/dist/wechat/broker-entry.js +182 -0
  22. package/dist/wechat/broker-launcher.d.ts +27 -0
  23. package/dist/wechat/broker-launcher.js +191 -0
  24. package/dist/wechat/broker-server.d.ts +25 -0
  25. package/dist/wechat/broker-server.js +540 -0
  26. package/dist/wechat/command-parser.d.ts +7 -0
  27. package/dist/wechat/command-parser.js +16 -0
  28. package/dist/wechat/compat/openclaw-guided-smoke.d.ts +178 -0
  29. package/dist/wechat/compat/openclaw-guided-smoke.js +1133 -0
  30. package/dist/wechat/compat/openclaw-public-helpers.d.ts +111 -0
  31. package/dist/wechat/compat/openclaw-public-helpers.js +262 -0
  32. package/dist/wechat/compat/openclaw-smoke.d.ts +48 -0
  33. package/dist/wechat/compat/openclaw-smoke.js +100 -0
  34. package/dist/wechat/compat/slash-guard.d.ts +11 -0
  35. package/dist/wechat/compat/slash-guard.js +24 -0
  36. package/dist/wechat/handle.d.ts +8 -0
  37. package/dist/wechat/handle.js +46 -0
  38. package/dist/wechat/ipc-auth.d.ts +6 -0
  39. package/dist/wechat/ipc-auth.js +39 -0
  40. package/dist/wechat/openclaw-account-adapter.d.ts +30 -0
  41. package/dist/wechat/openclaw-account-adapter.js +70 -0
  42. package/dist/wechat/operator-store.d.ts +9 -0
  43. package/dist/wechat/operator-store.js +69 -0
  44. package/dist/wechat/protocol.d.ts +29 -0
  45. package/dist/wechat/protocol.js +75 -0
  46. package/dist/wechat/request-store.d.ts +41 -0
  47. package/dist/wechat/request-store.js +215 -0
  48. package/dist/wechat/session-digest.d.ts +41 -0
  49. package/dist/wechat/session-digest.js +134 -0
  50. package/dist/wechat/state-paths.d.ts +14 -0
  51. package/dist/wechat/state-paths.js +45 -0
  52. package/dist/wechat/status-format.d.ts +14 -0
  53. package/dist/wechat/status-format.js +174 -0
  54. package/dist/wechat/token-store.d.ts +18 -0
  55. package/dist/wechat/token-store.js +100 -0
  56. package/dist/wechat/wechat-status-runtime.d.ts +24 -0
  57. package/dist/wechat/wechat-status-runtime.js +238 -0
  58. package/package.json +8 -3
@@ -0,0 +1,33 @@
1
+ import { type BrokerEnvelope } from "./protocol.js";
2
+ import type { WechatBridge } from "./bridge.js";
3
+ type RegisterMeta = {
4
+ instanceID: string;
5
+ pid: number;
6
+ };
7
+ export type RegisterAck = {
8
+ sessionToken: string;
9
+ registeredAt: number;
10
+ brokerPid: number;
11
+ };
12
+ type SessionSnapshot = {
13
+ instanceID: string;
14
+ sessionToken: string;
15
+ registeredAt: number;
16
+ brokerPid: number;
17
+ };
18
+ type BrokerClient = {
19
+ ping: () => Promise<BrokerEnvelope>;
20
+ registerInstance: (meta: RegisterMeta) => Promise<RegisterAck>;
21
+ heartbeat: () => Promise<BrokerEnvelope>;
22
+ getSessionSnapshot: () => SessionSnapshot | null;
23
+ close: () => Promise<void>;
24
+ };
25
+ export type CollectStatusInput = {
26
+ requestId: string;
27
+ };
28
+ export type BrokerClientOptions = {
29
+ onCollectStatus?: (input: CollectStatusInput) => Promise<unknown> | unknown;
30
+ bridge?: WechatBridge;
31
+ };
32
+ export declare function connect(endpoint: string, options?: BrokerClientOptions): Promise<BrokerClient>;
33
+ export {};
@@ -0,0 +1,257 @@
1
+ import net from "node:net";
2
+ import { parseEnvelopeLine, serializeEnvelope, } from "./protocol.js";
3
+ function isNonEmptyString(value) {
4
+ return typeof value === "string" && value.trim().length > 0;
5
+ }
6
+ function isFiniteNumber(value) {
7
+ return typeof value === "number" && Number.isFinite(value);
8
+ }
9
+ function isResponseForRequest(response, requestId) {
10
+ if (response.id === requestId) {
11
+ return true;
12
+ }
13
+ if (response.id.endsWith(`-${requestId}`)) {
14
+ return true;
15
+ }
16
+ if (response.type === "error") {
17
+ const payload = response.payload;
18
+ return payload.requestId === requestId;
19
+ }
20
+ return false;
21
+ }
22
+ export async function connect(endpoint, options = {}) {
23
+ if (options.bridge && options.onCollectStatus) {
24
+ throw new Error("broker client options are ambiguous: provide either bridge or onCollectStatus");
25
+ }
26
+ const socket = net.createConnection(endpoint);
27
+ let sequence = 0;
28
+ let pendingResolve = null;
29
+ let pendingReject = null;
30
+ let pendingRequestId = null;
31
+ let buffer = "";
32
+ let connected = false;
33
+ let closed = false;
34
+ let session = null;
35
+ const connectedReady = new Promise((resolve, reject) => {
36
+ socket.once("connect", () => {
37
+ connected = true;
38
+ resolve();
39
+ });
40
+ socket.once("error", reject);
41
+ });
42
+ socket.on("data", (chunk) => {
43
+ buffer += chunk.toString("utf8");
44
+ while (true) {
45
+ const newlineIndex = buffer.indexOf("\n");
46
+ if (newlineIndex === -1) {
47
+ break;
48
+ }
49
+ const frame = buffer.slice(0, newlineIndex + 1);
50
+ buffer = buffer.slice(newlineIndex + 1);
51
+ if (pendingResolve) {
52
+ try {
53
+ const parsed = parseEnvelopeLine(frame);
54
+ if (parsed.type === "collectStatus") {
55
+ handleCollectStatus(parsed);
56
+ continue;
57
+ }
58
+ if (pendingRequestId && !isResponseForRequest(parsed, pendingRequestId)) {
59
+ continue;
60
+ }
61
+ const resolve = pendingResolve;
62
+ pendingResolve = null;
63
+ pendingReject = null;
64
+ pendingRequestId = null;
65
+ resolve(parsed);
66
+ }
67
+ catch (error) {
68
+ const reject = pendingReject;
69
+ pendingResolve = null;
70
+ pendingReject = null;
71
+ pendingRequestId = null;
72
+ reject?.(error);
73
+ }
74
+ }
75
+ else {
76
+ try {
77
+ const parsed = parseEnvelopeLine(frame);
78
+ if (parsed.type === "collectStatus") {
79
+ handleCollectStatus(parsed);
80
+ }
81
+ }
82
+ catch {
83
+ // ignore unsolicited invalid frames when no pending request exists
84
+ }
85
+ }
86
+ }
87
+ });
88
+ socket.on("error", (error) => {
89
+ if (pendingReject) {
90
+ const reject = pendingReject;
91
+ pendingResolve = null;
92
+ pendingReject = null;
93
+ pendingRequestId = null;
94
+ reject(error);
95
+ }
96
+ });
97
+ socket.on("close", () => {
98
+ connected = false;
99
+ closed = true;
100
+ session = null;
101
+ if (pendingReject) {
102
+ const reject = pendingReject;
103
+ pendingResolve = null;
104
+ pendingReject = null;
105
+ pendingRequestId = null;
106
+ reject(new Error("broker connection closed"));
107
+ }
108
+ });
109
+ await connectedReady;
110
+ function nextRequestId(prefix) {
111
+ sequence += 1;
112
+ return `${prefix}-${Date.now()}-${sequence}`;
113
+ }
114
+ function sendStatusSnapshot(requestId, snapshot) {
115
+ if (!session) {
116
+ return;
117
+ }
118
+ const envelope = {
119
+ id: nextRequestId("statusSnapshot"),
120
+ type: "statusSnapshot",
121
+ instanceID: session.instanceID,
122
+ sessionToken: session.sessionToken,
123
+ payload: {
124
+ requestId,
125
+ snapshot,
126
+ },
127
+ };
128
+ socket.write(serializeEnvelope(envelope));
129
+ }
130
+ function handleCollectStatus(envelope) {
131
+ const payload = envelope.payload;
132
+ if (!isNonEmptyString(payload.requestId)) {
133
+ return;
134
+ }
135
+ const hasBridge = options.bridge !== undefined;
136
+ const hasHook = options.onCollectStatus !== undefined;
137
+ if (!hasBridge && !hasHook) {
138
+ return;
139
+ }
140
+ const collectPromise = hasBridge
141
+ ? options.bridge.collectStatusSnapshot()
142
+ : options.onCollectStatus({ requestId: payload.requestId });
143
+ void Promise.resolve(collectPromise)
144
+ .then((snapshot) => {
145
+ sendStatusSnapshot(payload.requestId, snapshot);
146
+ })
147
+ .catch(() => {
148
+ // swallow collect handler errors to keep socket alive
149
+ });
150
+ }
151
+ async function send(envelope) {
152
+ if (!connected || closed) {
153
+ throw new Error("broker connection closed");
154
+ }
155
+ if (pendingResolve) {
156
+ throw new Error("broker client has pending request");
157
+ }
158
+ return new Promise((resolve, reject) => {
159
+ pendingResolve = resolve;
160
+ pendingReject = reject;
161
+ pendingRequestId = envelope.id;
162
+ socket.write(serializeEnvelope(envelope));
163
+ });
164
+ }
165
+ return {
166
+ async ping() {
167
+ return send({
168
+ id: nextRequestId("ping"),
169
+ type: "ping",
170
+ payload: {},
171
+ });
172
+ },
173
+ async registerInstance(meta) {
174
+ const instanceID = meta.instanceID;
175
+ if (!isNonEmptyString(instanceID)) {
176
+ throw new Error("invalid instanceID");
177
+ }
178
+ if (!isFiniteNumber(meta.pid)) {
179
+ throw new Error("invalid pid");
180
+ }
181
+ const response = await send({
182
+ id: nextRequestId("register"),
183
+ type: "registerInstance",
184
+ instanceID,
185
+ payload: { pid: meta.pid },
186
+ });
187
+ if (response.type !== "registerAck") {
188
+ throw new Error("register failed");
189
+ }
190
+ const payload = response.payload;
191
+ if (!isNonEmptyString(payload.sessionToken)) {
192
+ throw new Error("registerAck missing sessionToken");
193
+ }
194
+ if (!isFiniteNumber(payload.registeredAt)) {
195
+ throw new Error("registerAck missing registeredAt");
196
+ }
197
+ if (!isFiniteNumber(payload.brokerPid)) {
198
+ throw new Error("registerAck missing brokerPid");
199
+ }
200
+ session = {
201
+ instanceID,
202
+ sessionToken: payload.sessionToken,
203
+ registeredAt: payload.registeredAt,
204
+ brokerPid: payload.brokerPid,
205
+ };
206
+ return {
207
+ sessionToken: session.sessionToken,
208
+ registeredAt: session.registeredAt,
209
+ brokerPid: session.brokerPid,
210
+ };
211
+ },
212
+ async heartbeat() {
213
+ if (!session) {
214
+ throw new Error("missing broker session");
215
+ }
216
+ return send({
217
+ id: nextRequestId("heartbeat"),
218
+ type: "heartbeat",
219
+ instanceID: session.instanceID,
220
+ sessionToken: session.sessionToken,
221
+ payload: {},
222
+ });
223
+ },
224
+ getSessionSnapshot() {
225
+ if (!session) {
226
+ return null;
227
+ }
228
+ return { ...session };
229
+ },
230
+ async close() {
231
+ if (closed) {
232
+ return;
233
+ }
234
+ if (socket.destroyed) {
235
+ closed = true;
236
+ connected = false;
237
+ session = null;
238
+ return;
239
+ }
240
+ const closePromise = new Promise((resolve) => {
241
+ socket.once("close", () => resolve());
242
+ });
243
+ socket.end();
244
+ await Promise.race([
245
+ closePromise,
246
+ new Promise((resolve) => {
247
+ setTimeout(() => {
248
+ if (!socket.destroyed) {
249
+ socket.destroy();
250
+ }
251
+ resolve();
252
+ }, 200);
253
+ }),
254
+ ]);
255
+ },
256
+ };
257
+ }
@@ -0,0 +1,17 @@
1
+ import { type WechatStatusRuntime } from "./wechat-status-runtime.js";
2
+ type BrokerWechatStatusRuntimeLifecycle = {
3
+ start: () => Promise<void>;
4
+ close: () => Promise<void>;
5
+ };
6
+ type BrokerWechatStatusRuntimeLifecycleDeps = {
7
+ createStatusRuntime?: (deps: {
8
+ onSlashCommand: (input: {
9
+ command: import("./command-parser.js").WechatSlashCommand;
10
+ }) => Promise<string>;
11
+ }) => WechatStatusRuntime;
12
+ handleWechatSlashCommand?: (command: import("./command-parser.js").WechatSlashCommand) => Promise<string>;
13
+ onRuntimeError?: (error: unknown) => void;
14
+ };
15
+ export declare function shouldEnableBrokerWechatStatusRuntime(env?: NodeJS.ProcessEnv): boolean;
16
+ export declare function createBrokerWechatStatusRuntimeLifecycle(deps?: BrokerWechatStatusRuntimeLifecycleDeps): BrokerWechatStatusRuntimeLifecycle;
17
+ export {};
@@ -0,0 +1,182 @@
1
+ import path from "node:path";
2
+ import process from "node:process";
3
+ import { readFileSync, rmSync } from "node:fs";
4
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
5
+ import { fileURLToPath } from "node:url";
6
+ import { startBrokerServer } from "./broker-server.js";
7
+ import { WECHAT_FILE_MODE, wechatStateRoot } from "./state-paths.js";
8
+ import { createWechatStatusRuntime } from "./wechat-status-runtime.js";
9
+ async function readPackageVersion() {
10
+ const packageJsonPath = new URL("../../package.json", import.meta.url);
11
+ return readFile(packageJsonPath, "utf8")
12
+ .then((raw) => {
13
+ const parsed = JSON.parse(raw);
14
+ if (typeof parsed.version === "string" && parsed.version.trim().length > 0) {
15
+ return parsed.version;
16
+ }
17
+ return "unknown";
18
+ })
19
+ .catch(() => "unknown");
20
+ }
21
+ function parseEndpointArg(argv) {
22
+ const prefix = "--endpoint=";
23
+ const endpointArg = argv.find((item) => item.startsWith(prefix));
24
+ if (!endpointArg) {
25
+ throw new Error("missing --endpoint argument");
26
+ }
27
+ const endpoint = endpointArg.slice(prefix.length);
28
+ if (!endpoint) {
29
+ throw new Error("missing --endpoint argument");
30
+ }
31
+ return endpoint;
32
+ }
33
+ function parseStateRootArg(argv) {
34
+ const prefix = "--state-root=";
35
+ const arg = argv.find((item) => item.startsWith(prefix));
36
+ if (!arg) {
37
+ return wechatStateRoot();
38
+ }
39
+ const stateRoot = arg.slice(prefix.length);
40
+ if (!stateRoot) {
41
+ throw new Error("missing --state-root argument");
42
+ }
43
+ return stateRoot;
44
+ }
45
+ function brokerStatePathForRoot(stateRoot) {
46
+ return path.join(stateRoot, "broker.json");
47
+ }
48
+ async function writeBrokerState(state, stateRoot) {
49
+ await mkdir(stateRoot, { recursive: true, mode: 0o700 });
50
+ const filePath = brokerStatePathForRoot(stateRoot);
51
+ await writeFile(filePath, JSON.stringify(state, null, 2), { mode: WECHAT_FILE_MODE });
52
+ }
53
+ export function shouldEnableBrokerWechatStatusRuntime(env = process.env) {
54
+ return env.WECHAT_BROKER_ENABLE_STATUS_RUNTIME === "1";
55
+ }
56
+ export function createBrokerWechatStatusRuntimeLifecycle(deps = {}) {
57
+ const onRuntimeError = deps.onRuntimeError ?? ((error) => console.error(error));
58
+ const handleWechatSlashCommand = deps.handleWechatSlashCommand ??
59
+ (async (command) => {
60
+ if (command.type === "status") {
61
+ return "命令暂未实现:/status";
62
+ }
63
+ return `命令暂未实现:/${command.command}`;
64
+ });
65
+ const createStatusRuntime = deps.createStatusRuntime ??
66
+ ((statusRuntimeDeps) => createWechatStatusRuntime({
67
+ onSlashCommand: async ({ command }) => statusRuntimeDeps.onSlashCommand({ command }),
68
+ onRuntimeError,
69
+ }));
70
+ let runtime = null;
71
+ return {
72
+ start: async () => {
73
+ if (runtime) {
74
+ return;
75
+ }
76
+ const created = createStatusRuntime({
77
+ onSlashCommand: async ({ command }) => handleWechatSlashCommand(command),
78
+ });
79
+ runtime = created;
80
+ try {
81
+ await created.start();
82
+ }
83
+ catch (error) {
84
+ onRuntimeError(error);
85
+ }
86
+ },
87
+ close: async () => {
88
+ if (!runtime) {
89
+ return;
90
+ }
91
+ const active = runtime;
92
+ runtime = null;
93
+ await active.close().catch((error) => {
94
+ onRuntimeError(error);
95
+ });
96
+ },
97
+ };
98
+ }
99
+ function removeOwnedBrokerStateFileSync(ownership, stateRoot) {
100
+ try {
101
+ const filePath = brokerStatePathForRoot(stateRoot);
102
+ const raw = readFileSync(filePath, "utf8");
103
+ const parsed = JSON.parse(raw);
104
+ if (parsed.pid !== ownership.pid || parsed.startedAt !== ownership.startedAt) {
105
+ return;
106
+ }
107
+ rmSync(filePath, { force: true });
108
+ }
109
+ catch {
110
+ // ignore cleanup errors on shutdown
111
+ }
112
+ }
113
+ async function run() {
114
+ const args = process.argv.slice(2);
115
+ const endpoint = parseEndpointArg(args);
116
+ const stateRoot = parseStateRootArg(args);
117
+ const server = await startBrokerServer(endpoint);
118
+ const version = await readPackageVersion();
119
+ const state = {
120
+ pid: process.pid,
121
+ endpoint: server.endpoint,
122
+ startedAt: server.startedAt,
123
+ version,
124
+ };
125
+ await writeBrokerState(state, stateRoot);
126
+ const wechatRuntimeLifecycle = createBrokerWechatStatusRuntimeLifecycle({
127
+ handleWechatSlashCommand: server.handleWechatSlashCommand,
128
+ });
129
+ if (shouldEnableBrokerWechatStatusRuntime()) {
130
+ await wechatRuntimeLifecycle.start();
131
+ }
132
+ const ownership = {
133
+ pid: state.pid,
134
+ startedAt: state.startedAt,
135
+ };
136
+ let shuttingDown = false;
137
+ const shutdown = async (exitCode = 0) => {
138
+ if (shuttingDown) {
139
+ return;
140
+ }
141
+ shuttingDown = true;
142
+ removeOwnedBrokerStateFileSync(ownership, stateRoot);
143
+ await wechatRuntimeLifecycle.close();
144
+ await server.close();
145
+ process.exit(exitCode);
146
+ };
147
+ process.once("SIGINT", () => {
148
+ void shutdown(0);
149
+ });
150
+ process.once("SIGTERM", () => {
151
+ void shutdown(0);
152
+ });
153
+ if (process.env.WECHAT_BROKER_EXIT_ON_STDIN_EOF === "1") {
154
+ process.stdin.on("end", () => {
155
+ void shutdown(0);
156
+ });
157
+ process.stdin.resume();
158
+ }
159
+ process.once("uncaughtException", (error) => {
160
+ console.error(error);
161
+ void shutdown(1);
162
+ });
163
+ process.once("unhandledRejection", (error) => {
164
+ console.error(error);
165
+ void shutdown(1);
166
+ });
167
+ process.on("exit", () => {
168
+ removeOwnedBrokerStateFileSync(ownership, stateRoot);
169
+ });
170
+ }
171
+ function isDirectRun() {
172
+ if (!process.argv[1]) {
173
+ return false;
174
+ }
175
+ return path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
176
+ }
177
+ if (isDirectRun()) {
178
+ void run().catch((error) => {
179
+ console.error(error);
180
+ process.exit(1);
181
+ });
182
+ }
@@ -0,0 +1,27 @@
1
+ type BrokerMetadata = {
2
+ pid: number;
3
+ endpoint: string;
4
+ startedAt: number;
5
+ version: string;
6
+ };
7
+ type LaunchLockContent = {
8
+ pid: number;
9
+ acquiredAt: number;
10
+ lockId: string;
11
+ };
12
+ type LaunchOptions = {
13
+ stateRoot?: string;
14
+ brokerJsonPath?: string;
15
+ launchLockPath?: string;
16
+ backoffMs?: number;
17
+ maxAttempts?: number;
18
+ endpointFactory?: () => string;
19
+ spawnImpl?: (endpoint: string, stateRoot: string) => {
20
+ pid?: number | undefined;
21
+ unref?: (() => void) | undefined;
22
+ };
23
+ pingImpl?: (endpoint: string) => Promise<boolean>;
24
+ onLockAcquired?: (lock: LaunchLockContent) => void;
25
+ };
26
+ export declare function connectOrSpawnBroker(options?: LaunchOptions): Promise<BrokerMetadata>;
27
+ export {};