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
@@ -0,0 +1,33 @@
1
+ type CompatHostApi = {
2
+ runtime?: {
3
+ channelRuntime?: unknown;
4
+ gateway?: {
5
+ startAccount?: unknown;
6
+ };
7
+ };
8
+ registerChannel?: (input: unknown) => void;
9
+ registerCli?: (handler: unknown, options?: unknown) => void;
10
+ };
11
+ type OpenClawWeixinPlugin = {
12
+ id?: string;
13
+ register(api: CompatHostApi): void;
14
+ };
15
+ export type OpenClawWeixinPublicEntry = {
16
+ packageJsonPath: string;
17
+ packageRoot: string;
18
+ extensions: string[];
19
+ entryRelativePath: string;
20
+ entryAbsolutePath: string;
21
+ };
22
+ export declare function resolveOpenClawWeixinPublicEntry(): Promise<OpenClawWeixinPublicEntry>;
23
+ export declare function loadOpenClawWeixinDefaultExport(): Promise<OpenClawWeixinPlugin>;
24
+ export declare function loadRegisteredWeixinPluginPayloads(): Promise<Array<{
25
+ plugin?: unknown;
26
+ }>>;
27
+ export declare function loadRegisteredWeixinPluginContext(): Promise<{
28
+ pluginId: string;
29
+ payloads: Array<{
30
+ plugin?: unknown;
31
+ }>;
32
+ }>;
33
+ export {};
@@ -0,0 +1,62 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import { loadModuleWithTsFallback } from "./jiti-loader.js";
5
+ function requireField(condition, message) {
6
+ if (!condition) {
7
+ throw new Error(`[wechat-compat] ${message}`);
8
+ }
9
+ }
10
+ export async function resolveOpenClawWeixinPublicEntry() {
11
+ const require = createRequire(import.meta.url);
12
+ const packageName = "@tencent-weixin/openclaw-weixin";
13
+ const packageJsonPath = require.resolve(`${packageName}/package.json`);
14
+ const packageJsonRaw = await readFile(packageJsonPath, "utf8");
15
+ const packageJson = JSON.parse(packageJsonRaw);
16
+ const extensions = Array.isArray(packageJson.openclaw?.extensions)
17
+ ? packageJson.openclaw?.extensions.filter((it) => typeof it === "string")
18
+ : [];
19
+ requireField(extensions.length > 0, `${packageName} openclaw.extensions[0] is required`);
20
+ const entryRelativePath = extensions[0];
21
+ requireField(Boolean(entryRelativePath?.startsWith("./")), `${packageName} openclaw.extensions[0] must start with ./`);
22
+ const packageRoot = path.dirname(packageJsonPath);
23
+ const entryAbsolutePath = path.resolve(packageRoot, entryRelativePath);
24
+ return {
25
+ packageJsonPath,
26
+ packageRoot,
27
+ extensions,
28
+ entryRelativePath,
29
+ entryAbsolutePath,
30
+ };
31
+ }
32
+ export async function loadOpenClawWeixinDefaultExport() {
33
+ const entry = await resolveOpenClawWeixinPublicEntry();
34
+ const moduleNamespace = await loadModuleWithTsFallback(entry.entryAbsolutePath, { parentURL: import.meta.url });
35
+ const plugin = moduleNamespace.default;
36
+ if (!plugin || typeof plugin !== "object" || typeof plugin.register !== "function") {
37
+ throw new Error("[wechat-compat] @tencent-weixin/openclaw-weixin public entry default export is missing register(api)");
38
+ }
39
+ return plugin;
40
+ }
41
+ export async function loadRegisteredWeixinPluginPayloads() {
42
+ const context = await loadRegisteredWeixinPluginContext();
43
+ return context.payloads;
44
+ }
45
+ export async function loadRegisteredWeixinPluginContext() {
46
+ const payloads = [];
47
+ const plugin = await loadOpenClawWeixinDefaultExport();
48
+ plugin.register({
49
+ runtime: {
50
+ channelRuntime: { mode: "guided-smoke" },
51
+ gateway: { startAccount: { source: "guided-smoke" } },
52
+ },
53
+ registerChannel(payload) {
54
+ payloads.push(payload);
55
+ },
56
+ registerCli() { },
57
+ });
58
+ return {
59
+ pluginId: typeof plugin.id === "string" && plugin.id.trim().length > 0 ? plugin.id : "wechat-openclaw-weixin",
60
+ payloads,
61
+ };
62
+ }
@@ -0,0 +1,70 @@
1
+ import { loadOpenClawAccountHelpers, type WeixinAccountHelpers } from "./openclaw-account-helpers.js";
2
+ import { loadRegisteredWeixinPluginContext, loadRegisteredWeixinPluginPayloads, resolveOpenClawWeixinPublicEntry, type OpenClawWeixinPublicEntry } from "./openclaw-public-entry.js";
3
+ import { type WeixinQrGateway } from "./openclaw-qr-gateway.js";
4
+ import { loadOpenClawUpdatesAndSendHelpers, type PublicWeixinMessage, type PublicWeixinSendMessage } from "./openclaw-updates-send.js";
5
+ import { loadLatestWeixinAccountState, loadOpenClawSyncBufHelper, type PublicWeixinPersistGetUpdatesBuf } from "./openclaw-sync-buf.js";
6
+ export declare const OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES: {
7
+ readonly stateDir: "@tencent-weixin/openclaw-weixin/src/storage/state-dir.ts";
8
+ readonly syncBuf: "@tencent-weixin/openclaw-weixin/src/storage/sync-buf.ts";
9
+ readonly getUpdates: "@tencent-weixin/openclaw-weixin/src/api/api.ts";
10
+ readonly sendMessageWeixin: "@tencent-weixin/openclaw-weixin/src/messaging/send.ts";
11
+ };
12
+ export type OpenClawWeixinPublicHelpers = {
13
+ entry: OpenClawWeixinPublicEntry;
14
+ pluginId: string;
15
+ qrGateway: WeixinQrGateway;
16
+ accountHelpers: WeixinAccountHelpers;
17
+ latestAccountState: {
18
+ accountId: string;
19
+ token: string;
20
+ baseUrl: string;
21
+ getUpdatesBuf?: string;
22
+ } | null;
23
+ getUpdates: (params: {
24
+ baseUrl: string;
25
+ token?: string;
26
+ get_updates_buf?: string;
27
+ timeoutMs?: number;
28
+ }) => Promise<{
29
+ msgs?: PublicWeixinMessage[];
30
+ get_updates_buf?: string;
31
+ }>;
32
+ sendMessageWeixin: PublicWeixinSendMessage;
33
+ persistGetUpdatesBuf?: PublicWeixinPersistGetUpdatesBuf;
34
+ };
35
+ type OpenClawWeixinPublicHelpersLoaders = {
36
+ resolveOpenClawWeixinPublicEntry?: typeof resolveOpenClawWeixinPublicEntry;
37
+ loadRegisteredWeixinPluginContext?: typeof loadRegisteredWeixinPluginContext;
38
+ loadRegisteredWeixinPluginPayloads?: typeof loadRegisteredWeixinPluginPayloads;
39
+ loadOpenClawQrGateway?: (payloads: Array<{
40
+ plugin?: unknown;
41
+ }>) => Promise<{
42
+ gateway: WeixinQrGateway;
43
+ pluginId: string;
44
+ }>;
45
+ loadPublicWeixinQrGateway?: () => Promise<{
46
+ gateway: WeixinQrGateway;
47
+ pluginId?: string;
48
+ }>;
49
+ loadLatestWeixinAccountState?: typeof loadLatestWeixinAccountState;
50
+ loadOpenClawAccountHelpers?: typeof loadOpenClawAccountHelpers;
51
+ loadOpenClawUpdatesAndSendHelpers?: typeof loadOpenClawUpdatesAndSendHelpers;
52
+ loadOpenClawSyncBufHelper?: typeof loadOpenClawSyncBufHelper;
53
+ loadPublicWeixinHelpers?: () => Promise<{
54
+ getUpdates: (params: {
55
+ baseUrl: string;
56
+ token?: string;
57
+ get_updates_buf?: string;
58
+ timeoutMs?: number;
59
+ }) => Promise<{
60
+ msgs?: PublicWeixinMessage[];
61
+ get_updates_buf?: string;
62
+ }>;
63
+ }>;
64
+ loadPublicWeixinSendHelper?: () => Promise<{
65
+ sendMessageWeixin: PublicWeixinSendMessage;
66
+ }>;
67
+ };
68
+ export declare function loadOpenClawWeixinPublicHelpers(loaders?: OpenClawWeixinPublicHelpersLoaders): Promise<OpenClawWeixinPublicHelpers>;
69
+ export type OpenClawWeixinPublicHelpersLoaderOptions = OpenClawWeixinPublicHelpersLoaders;
70
+ export type { PublicWeixinMessage, PublicWeixinSendMessage, PublicWeixinPersistGetUpdatesBuf };
@@ -0,0 +1,68 @@
1
+ import { loadOpenClawAccountHelpers } from "./openclaw-account-helpers.js";
2
+ import { loadRegisteredWeixinPluginContext, resolveOpenClawWeixinPublicEntry, } from "./openclaw-public-entry.js";
3
+ import { loadOpenClawQrGateway } from "./openclaw-qr-gateway.js";
4
+ import { loadOpenClawUpdatesAndSendHelpers, } from "./openclaw-updates-send.js";
5
+ import { loadLatestWeixinAccountState, loadOpenClawSyncBufHelper, } from "./openclaw-sync-buf.js";
6
+ export const OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES = {
7
+ stateDir: "@tencent-weixin/openclaw-weixin/src/storage/state-dir.ts",
8
+ syncBuf: "@tencent-weixin/openclaw-weixin/src/storage/sync-buf.ts",
9
+ getUpdates: "@tencent-weixin/openclaw-weixin/src/api/api.ts",
10
+ sendMessageWeixin: "@tencent-weixin/openclaw-weixin/src/messaging/send.ts",
11
+ };
12
+ function missingHelperError(helperName) {
13
+ return new Error(`[wechat-compat] required helper missing: ${helperName}`);
14
+ }
15
+ export async function loadOpenClawWeixinPublicHelpers(loaders = {}) {
16
+ const entry = await (loaders.resolveOpenClawWeixinPublicEntry ?? resolveOpenClawWeixinPublicEntry)();
17
+ const qrGatewayResult = loaders.loadPublicWeixinQrGateway
18
+ ? await loaders.loadPublicWeixinQrGateway()
19
+ : await (async () => {
20
+ if (loaders.loadRegisteredWeixinPluginContext) {
21
+ const context = await loaders.loadRegisteredWeixinPluginContext();
22
+ return (loaders.loadOpenClawQrGateway ?? loadOpenClawQrGateway)(context.payloads, { pluginId: context.pluginId });
23
+ }
24
+ const context = await loadRegisteredWeixinPluginContext();
25
+ return (loaders.loadOpenClawQrGateway ?? loadOpenClawQrGateway)(context.payloads, { pluginId: context.pluginId });
26
+ })();
27
+ const accountHelpers = await (loaders.loadOpenClawAccountHelpers ?? loadOpenClawAccountHelpers)();
28
+ const latestAccountState = await (loaders.loadLatestWeixinAccountState ?? loadLatestWeixinAccountState)({
29
+ stateDirModulePath: OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.stateDir,
30
+ syncBufModulePath: OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.syncBuf,
31
+ });
32
+ const updatesSend = loaders.loadOpenClawUpdatesAndSendHelpers
33
+ ? await loaders.loadOpenClawUpdatesAndSendHelpers()
34
+ : await (async () => {
35
+ const defaults = await loadOpenClawUpdatesAndSendHelpers();
36
+ const maybeUpdates = loaders.loadPublicWeixinHelpers ? await loaders.loadPublicWeixinHelpers() : undefined;
37
+ const maybeSend = loaders.loadPublicWeixinSendHelper ? await loaders.loadPublicWeixinSendHelper() : undefined;
38
+ return {
39
+ getUpdates: loaders.loadPublicWeixinHelpers ? maybeUpdates?.getUpdates : defaults.getUpdates,
40
+ sendMessageWeixin: loaders.loadPublicWeixinSendHelper ? maybeSend?.sendMessageWeixin : defaults.sendMessageWeixin,
41
+ };
42
+ })();
43
+ const syncBufHelpers = await (loaders.loadOpenClawSyncBufHelper ?? loadOpenClawSyncBufHelper)();
44
+ if (typeof qrGatewayResult?.gateway?.loginWithQrStart !== "function" || typeof qrGatewayResult?.gateway?.loginWithQrWait !== "function") {
45
+ throw missingHelperError("qrGateway");
46
+ }
47
+ if (typeof updatesSend?.getUpdates !== "function") {
48
+ throw missingHelperError("getUpdates");
49
+ }
50
+ if (typeof accountHelpers?.listAccountIds !== "function" ||
51
+ typeof accountHelpers?.resolveAccount !== "function" ||
52
+ typeof accountHelpers?.describeAccount !== "function") {
53
+ throw missingHelperError("accountHelpers");
54
+ }
55
+ if (typeof updatesSend?.sendMessageWeixin !== "function") {
56
+ throw missingHelperError("sendMessageWeixin");
57
+ }
58
+ return {
59
+ entry,
60
+ pluginId: typeof qrGatewayResult.pluginId === "string" && qrGatewayResult.pluginId.length > 0 ? qrGatewayResult.pluginId : "unknown",
61
+ qrGateway: qrGatewayResult.gateway,
62
+ accountHelpers,
63
+ latestAccountState,
64
+ getUpdates: updatesSend.getUpdates,
65
+ sendMessageWeixin: updatesSend.sendMessageWeixin,
66
+ persistGetUpdatesBuf: syncBufHelpers.persistGetUpdatesBuf,
67
+ };
68
+ }
@@ -0,0 +1,15 @@
1
+ export type WeixinQrGateway = {
2
+ loginWithQrStart: (input?: unknown) => unknown;
3
+ loginWithQrWait: (input?: unknown) => unknown;
4
+ };
5
+ type OpenClawQrGatewayPayload = {
6
+ plugin?: unknown;
7
+ };
8
+ export declare function createOpenClawQrGateway(source: WeixinQrGateway): WeixinQrGateway;
9
+ export declare function loadOpenClawQrGateway(payloads: OpenClawQrGatewayPayload[], options?: {
10
+ pluginId?: string;
11
+ }): Promise<{
12
+ gateway: WeixinQrGateway;
13
+ pluginId: string;
14
+ }>;
15
+ export {};
@@ -0,0 +1,39 @@
1
+ function toObjectInput(input) {
2
+ return input && typeof input === "object" ? input : {};
3
+ }
4
+ function hasQrLoginMethods(value) {
5
+ if (!value || typeof value !== "object") {
6
+ return false;
7
+ }
8
+ const candidate = value;
9
+ return typeof candidate.loginWithQrStart === "function" && typeof candidate.loginWithQrWait === "function";
10
+ }
11
+ export function createOpenClawQrGateway(source) {
12
+ return {
13
+ async loginWithQrStart(input) {
14
+ return source.loginWithQrStart(toObjectInput(input));
15
+ },
16
+ async loginWithQrWait(input) {
17
+ return source.loginWithQrWait(toObjectInput(input));
18
+ },
19
+ };
20
+ }
21
+ export async function loadOpenClawQrGateway(payloads, options = {}) {
22
+ for (const payload of payloads) {
23
+ const payloadPlugin = payload?.plugin;
24
+ const resolvedPluginId = typeof options.pluginId === "string" && options.pluginId.trim().length > 0
25
+ ? options.pluginId
26
+ : typeof payloadPlugin?.id === "string" &&
27
+ String(payloadPlugin.id).trim().length > 0
28
+ ? String(payloadPlugin.id)
29
+ : "unknown";
30
+ const gateway = payloadPlugin && typeof payloadPlugin === "object" ? payloadPlugin.gateway : null;
31
+ if (hasQrLoginMethods(gateway)) {
32
+ return {
33
+ gateway: createOpenClawQrGateway(gateway),
34
+ pluginId: resolvedPluginId,
35
+ };
36
+ }
37
+ }
38
+ throw new Error("registerChannel did not expose weixin gateway loginWithQrStart/loginWithQrWait");
39
+ }
@@ -0,0 +1,48 @@
1
+ import { type SlashOnlyCommand } from "./slash-guard.js";
2
+ import { type OpenClawWeixinPublicHelpers, type OpenClawWeixinPublicHelpersLoaderOptions } from "./openclaw-public-helpers.js";
3
+ type SmokeMode = "self-test" | "real-account";
4
+ type OpenClawSmokeHarnessOptions = {
5
+ mode: SmokeMode;
6
+ };
7
+ type PublicHelpersLoader = (options?: OpenClawWeixinPublicHelpersLoaderOptions) => Promise<OpenClawWeixinPublicHelpers>;
8
+ type RunOpenClawSmokeOptions = {
9
+ loadOpenClawWeixinPublicHelpers?: PublicHelpersLoader;
10
+ publicHelpersOptions?: OpenClawWeixinPublicHelpersLoaderOptions;
11
+ inputs?: string[];
12
+ dryRun?: boolean;
13
+ argv?: string[];
14
+ };
15
+ type SmokeGuardRejectResult = {
16
+ route: "guard-reject";
17
+ message: string;
18
+ };
19
+ type SmokeHostSelfTestResult = {
20
+ route: "public-self-test";
21
+ status: "loaded";
22
+ pluginId: string;
23
+ };
24
+ type SmokeStubResult = {
25
+ route: "stub";
26
+ command: SlashOnlyCommand;
27
+ argument: string;
28
+ stubReason: "stage-a-command-stub";
29
+ mode: SmokeMode;
30
+ };
31
+ type SmokeRealAccountDryRunResult = {
32
+ route: "real-account-dry-run";
33
+ binding: "skipped";
34
+ requiredEnvVars: readonly string[];
35
+ missingEnvVars: readonly string[];
36
+ manualSteps: readonly string[];
37
+ artifactPaths: readonly string[];
38
+ };
39
+ export type OpenClawSmokeHandleResult = SmokeGuardRejectResult | SmokeHostSelfTestResult | SmokeStubResult | SmokeRealAccountDryRunResult;
40
+ export type OpenClawSmokeHarness = {
41
+ handleIncomingText(input: string): Promise<OpenClawSmokeHandleResult>;
42
+ };
43
+ export declare function resolveRealAccountDryRunFlag(options?: Pick<RunOpenClawSmokeOptions, "dryRun" | "argv">): boolean;
44
+ export declare function createRealAccountDryRunPreparation(): SmokeRealAccountDryRunResult;
45
+ export declare function sanitizeOpenClawEvidenceSample(input: string): string;
46
+ export declare function createOpenClawSmokeHarness(options: OpenClawSmokeHarnessOptions): OpenClawSmokeHarness;
47
+ export declare function runOpenClawSmoke(mode: SmokeMode, options?: RunOpenClawSmokeOptions): Promise<OpenClawSmokeHandleResult[]>;
48
+ export {};
@@ -0,0 +1,100 @@
1
+ import { guardSlashOnlyInput } from "./slash-guard.js";
2
+ import { loadOpenClawWeixinPublicHelpers, } from "./openclaw-public-helpers.js";
3
+ const REAL_ACCOUNT_REQUIRED_ENV_VARS = [
4
+ "WECHAT_REAL_ACCOUNT_ID",
5
+ "WECHAT_DEVICE_ID",
6
+ "WECHAT_CONTEXT_TOKEN",
7
+ "WECHAT_BOT_TOKEN",
8
+ ];
9
+ const REAL_ACCOUNT_MANUAL_STEPS = [
10
+ "执行 npm run wechat:smoke:real-account -- --dry-run,确认仅输出准备信息",
11
+ "确认所有必填环境变量已配置,再执行 npm run wechat:smoke:guided 完成真实账号手测",
12
+ "按 evidence/README.md 记录 blocked 或 known-unknown,并产出脱敏样本",
13
+ ];
14
+ const REAL_ACCOUNT_ARTIFACT_PATHS = [
15
+ "docs/superpowers/wechat-stage-a/evidence/README.md",
16
+ "docs/superpowers/wechat-stage-a/api-samples-sanitized.md",
17
+ "docs/superpowers/wechat-stage-a/go-no-go.md",
18
+ ];
19
+ export function resolveRealAccountDryRunFlag(options = {}) {
20
+ if (options.dryRun === true) {
21
+ return true;
22
+ }
23
+ const argv = options.argv ?? process.argv.slice(2);
24
+ return argv.includes("--dry-run");
25
+ }
26
+ export function createRealAccountDryRunPreparation() {
27
+ const missingEnvVars = REAL_ACCOUNT_REQUIRED_ENV_VARS.filter((name) => !process.env[name]?.trim());
28
+ return {
29
+ route: "real-account-dry-run",
30
+ binding: "skipped",
31
+ requiredEnvVars: [...REAL_ACCOUNT_REQUIRED_ENV_VARS],
32
+ missingEnvVars,
33
+ manualSteps: [...REAL_ACCOUNT_MANUAL_STEPS],
34
+ artifactPaths: [...REAL_ACCOUNT_ARTIFACT_PATHS],
35
+ };
36
+ }
37
+ export function sanitizeOpenClawEvidenceSample(input) {
38
+ return input
39
+ .replace(/(contextToken\s*[=:]\s*)([^\s\n]+)/g, "$1[REDACTED_CONTEXT_TOKEN]")
40
+ .replace(/("contextToken"\s*:\s*")([^"]+)(")/g, "$1[REDACTED_CONTEXT_TOKEN]$3")
41
+ .replace(/(context_token\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_CONTEXT_TOKEN]")
42
+ .replace(/("context_token"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_CONTEXT_TOKEN]$3")
43
+ .replace(/(bot_token\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_BOT_TOKEN]")
44
+ .replace(/("bot_token"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_BOT_TOKEN]$3")
45
+ .replace(/(authorization\s*:\s*bearer\s+)([^\s\n]+)/gi, "$1[REDACTED_AUTHORIZATION]")
46
+ .replace(/("authorization"\s*:\s*")Bearer\s+([^"]+)(")/gi, "$1Bearer [REDACTED_AUTHORIZATION]$3")
47
+ .replace(/(userId\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_USER_ID]")
48
+ .replace(/("userId"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_USER_ID]$3")
49
+ .replace(/(botId\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_BOT_ID]")
50
+ .replace(/("botId"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_BOT_ID]$3")
51
+ .replace(/(qrCode\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_QR_CODE]")
52
+ .replace(/("qrCode"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_QR_CODE]$3")
53
+ .replace(/(deviceId\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_DEVICE_ID]")
54
+ .replace(/("deviceId"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_DEVICE_ID]$3")
55
+ .replace(/(messageId\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_MESSAGE_ID]")
56
+ .replace(/("messageId"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_MESSAGE_ID]$3")
57
+ .replace(/(requestId\s*[=:]\s*)([^\s\n]+)/gi, "$1[REDACTED_REQUEST_ID]")
58
+ .replace(/("requestId"\s*:\s*")([^"]+)(")/gi, "$1[REDACTED_REQUEST_ID]$3");
59
+ }
60
+ export function createOpenClawSmokeHarness(options) {
61
+ return {
62
+ async handleIncomingText(input) {
63
+ const guarded = guardSlashOnlyInput(input);
64
+ if (!guarded.accepted) {
65
+ return {
66
+ route: "guard-reject",
67
+ message: guarded.message,
68
+ };
69
+ }
70
+ return {
71
+ route: "stub",
72
+ command: guarded.command,
73
+ argument: guarded.argument,
74
+ stubReason: "stage-a-command-stub",
75
+ mode: options.mode,
76
+ };
77
+ },
78
+ };
79
+ }
80
+ export async function runOpenClawSmoke(mode, options = {}) {
81
+ const results = [];
82
+ if (mode === "self-test") {
83
+ const helpers = await (options.loadOpenClawWeixinPublicHelpers ?? loadOpenClawWeixinPublicHelpers)(options.publicHelpersOptions);
84
+ results.push({
85
+ route: "public-self-test",
86
+ status: "loaded",
87
+ pluginId: helpers.pluginId,
88
+ });
89
+ }
90
+ if (mode === "real-account") {
91
+ results.push(createRealAccountDryRunPreparation());
92
+ return results;
93
+ }
94
+ const harness = createOpenClawSmokeHarness({ mode });
95
+ const inputs = options.inputs ?? ["hello", "/status", "/reply smoke", "/allow once"];
96
+ for (const input of inputs) {
97
+ results.push(await harness.handleIncomingText(input));
98
+ }
99
+ return results;
100
+ }
@@ -0,0 +1,24 @@
1
+ export type PublicWeixinPersistGetUpdatesBuf = (params: {
2
+ accountId: string;
3
+ getUpdatesBuf: string;
4
+ }) => Promise<void>;
5
+ export declare function createOpenClawSyncBufHelper(input: {
6
+ getSyncBufFilePath: (accountId: string) => string;
7
+ saveGetUpdatesBuf: (filePath: string, getUpdatesBuf: string) => void;
8
+ }): {
9
+ persistGetUpdatesBuf: PublicWeixinPersistGetUpdatesBuf;
10
+ };
11
+ export declare function loadOpenClawSyncBufHelper(options?: {
12
+ syncBufModulePath?: string;
13
+ }): Promise<{
14
+ persistGetUpdatesBuf: PublicWeixinPersistGetUpdatesBuf;
15
+ }>;
16
+ export declare function loadLatestWeixinAccountState(options?: {
17
+ stateDirModulePath?: string;
18
+ syncBufModulePath?: string;
19
+ }): Promise<{
20
+ accountId: string;
21
+ token: string;
22
+ baseUrl: string;
23
+ getUpdatesBuf?: string;
24
+ } | null>;
@@ -0,0 +1,80 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import { loadModuleWithTsFallback } from "./jiti-loader.js";
5
+ import { readWechatLatestAccountState } from "../latest-account-state-store.js";
6
+ const OPENCLAW_SYNC_BUF_MODULE = "@tencent-weixin/openclaw-weixin/src/storage/sync-buf.ts";
7
+ const OPENCLAW_STATE_DIR_MODULE = "@tencent-weixin/openclaw-weixin/src/storage/state-dir.ts";
8
+ export function createOpenClawSyncBufHelper(input) {
9
+ return {
10
+ async persistGetUpdatesBuf({ accountId, getUpdatesBuf }) {
11
+ const filePath = input.getSyncBufFilePath(accountId);
12
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
13
+ throw new Error("[wechat-compat] sync-buf helper returned invalid file path");
14
+ }
15
+ input.saveGetUpdatesBuf(filePath, getUpdatesBuf);
16
+ },
17
+ };
18
+ }
19
+ export async function loadOpenClawSyncBufHelper(options = {}) {
20
+ const require = createRequire(import.meta.url);
21
+ const syncBufModulePath = require.resolve(options.syncBufModulePath ?? OPENCLAW_SYNC_BUF_MODULE);
22
+ const syncBufModule = await loadModuleWithTsFallback(syncBufModulePath, { parentURL: import.meta.url });
23
+ if (typeof syncBufModule.getSyncBufFilePath !== "function" || typeof syncBufModule.saveGetUpdatesBuf !== "function") {
24
+ throw new Error("[wechat-compat] sync-buf source helper unavailable");
25
+ }
26
+ return createOpenClawSyncBufHelper({
27
+ getSyncBufFilePath: syncBufModule.getSyncBufFilePath,
28
+ saveGetUpdatesBuf: syncBufModule.saveGetUpdatesBuf,
29
+ });
30
+ }
31
+ export async function loadLatestWeixinAccountState(options = {}) {
32
+ const pluginOwned = await readWechatLatestAccountState();
33
+ if (pluginOwned) {
34
+ return pluginOwned;
35
+ }
36
+ const require = createRequire(import.meta.url);
37
+ const stateDirModulePath = require.resolve(options.stateDirModulePath ?? OPENCLAW_STATE_DIR_MODULE);
38
+ const stateDirModule = await loadModuleWithTsFallback(stateDirModulePath, { parentURL: import.meta.url });
39
+ const stateDir = stateDirModule.resolveStateDir?.();
40
+ if (!stateDir) {
41
+ return null;
42
+ }
43
+ const accountsIndexPath = path.join(stateDir, "openclaw-weixin", "accounts.json");
44
+ let accountIds = [];
45
+ try {
46
+ const raw = await readFile(accountsIndexPath, "utf8");
47
+ const parsed = JSON.parse(raw);
48
+ if (Array.isArray(parsed)) {
49
+ accountIds = parsed.filter((item) => typeof item === "string" && item.trim().length > 0);
50
+ }
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ const accountId = accountIds.at(-1);
56
+ if (!accountId) {
57
+ return null;
58
+ }
59
+ try {
60
+ const accountFilePath = path.join(stateDir, "openclaw-weixin", "accounts", `${accountId}.json`);
61
+ const accountRaw = await readFile(accountFilePath, "utf8");
62
+ const account = JSON.parse(accountRaw);
63
+ const syncBufModulePath = require.resolve(options.syncBufModulePath ?? OPENCLAW_SYNC_BUF_MODULE);
64
+ const syncBufModule = await loadModuleWithTsFallback(syncBufModulePath, { parentURL: import.meta.url });
65
+ if (typeof account.token !== "string" || account.token.trim().length === 0) {
66
+ return null;
67
+ }
68
+ const syncBufFilePath = syncBufModule.getSyncBufFilePath?.(accountId);
69
+ const persistedGetUpdatesBuf = syncBufFilePath ? syncBufModule.loadGetUpdatesBuf?.(syncBufFilePath) : undefined;
70
+ return {
71
+ accountId,
72
+ token: account.token,
73
+ baseUrl: typeof account.baseUrl === "string" && account.baseUrl.trim().length > 0 ? account.baseUrl : "https://ilinkai.weixin.qq.com",
74
+ getUpdatesBuf: typeof persistedGetUpdatesBuf === "string" ? persistedGetUpdatesBuf : undefined,
75
+ };
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ }
@@ -0,0 +1,47 @@
1
+ type PublicWeixinMessageItem = {
2
+ type?: number;
3
+ text_item?: {
4
+ text?: string;
5
+ };
6
+ };
7
+ export type PublicWeixinMessage = {
8
+ message_id?: number;
9
+ from_user_id?: string;
10
+ context_token?: string;
11
+ create_time_ms?: number;
12
+ item_list?: PublicWeixinMessageItem[];
13
+ };
14
+ export type PublicWeixinSendMessage = (params: {
15
+ to: string;
16
+ text: string;
17
+ opts: {
18
+ baseUrl: string;
19
+ token: string;
20
+ contextToken?: string;
21
+ };
22
+ }) => Promise<{
23
+ messageId: string;
24
+ }>;
25
+ type PublicGetUpdates = (params: {
26
+ baseUrl: string;
27
+ token?: string;
28
+ get_updates_buf?: string;
29
+ timeoutMs?: number;
30
+ }) => Promise<{
31
+ msgs?: PublicWeixinMessage[];
32
+ get_updates_buf?: string;
33
+ }>;
34
+ export declare function createOpenClawUpdatesHelper(getUpdates: PublicGetUpdates): {
35
+ getUpdates: PublicGetUpdates;
36
+ };
37
+ export declare function createOpenClawSendHelper(sendMessageWeixin: PublicWeixinSendMessage): {
38
+ sendMessageWeixin: PublicWeixinSendMessage;
39
+ };
40
+ export declare function loadOpenClawUpdatesAndSendHelpers(options?: {
41
+ getUpdatesModulePath?: string;
42
+ sendMessageWeixinModulePath?: string;
43
+ }): Promise<{
44
+ getUpdates: PublicGetUpdates;
45
+ sendMessageWeixin: PublicWeixinSendMessage;
46
+ }>;
47
+ export {};
@@ -0,0 +1,38 @@
1
+ import { createRequire } from "node:module";
2
+ import { loadModuleWithTsFallback } from "./jiti-loader.js";
3
+ const OPENCLAW_UPDATES_MODULE = "@tencent-weixin/openclaw-weixin/src/api/api.ts";
4
+ const OPENCLAW_SEND_MODULE = "@tencent-weixin/openclaw-weixin/src/messaging/send.ts";
5
+ function toObjectInput(input) {
6
+ return input && typeof input === "object" ? input : {};
7
+ }
8
+ export function createOpenClawUpdatesHelper(getUpdates) {
9
+ return {
10
+ async getUpdates(input) {
11
+ return getUpdates(toObjectInput(input));
12
+ },
13
+ };
14
+ }
15
+ export function createOpenClawSendHelper(sendMessageWeixin) {
16
+ return {
17
+ async sendMessageWeixin(input) {
18
+ return sendMessageWeixin(toObjectInput(input));
19
+ },
20
+ };
21
+ }
22
+ export async function loadOpenClawUpdatesAndSendHelpers(options = {}) {
23
+ const require = createRequire(import.meta.url);
24
+ const getUpdatesModulePath = require.resolve(options.getUpdatesModulePath ?? OPENCLAW_UPDATES_MODULE);
25
+ const sendModulePath = require.resolve(options.sendMessageWeixinModulePath ?? OPENCLAW_SEND_MODULE);
26
+ const getUpdatesModule = await loadModuleWithTsFallback(getUpdatesModulePath, { parentURL: import.meta.url });
27
+ const sendModule = await loadModuleWithTsFallback(sendModulePath, { parentURL: import.meta.url });
28
+ if (typeof getUpdatesModule.getUpdates !== "function") {
29
+ throw new Error("public getUpdates helper unavailable");
30
+ }
31
+ if (typeof sendModule.sendMessageWeixin !== "function") {
32
+ throw new Error("public sendMessageWeixin helper unavailable");
33
+ }
34
+ return {
35
+ ...createOpenClawUpdatesHelper(getUpdatesModule.getUpdates),
36
+ ...createOpenClawSendHelper(sendModule.sendMessageWeixin),
37
+ };
38
+ }
@@ -0,0 +1,12 @@
1
+ type QrCodeTerminal = {
2
+ generate(input: string, opts: {
3
+ small?: boolean;
4
+ }, cb?: (output: string) => void): void;
5
+ };
6
+ type QrCodeTerminalNamespace = {
7
+ default?: unknown;
8
+ generate?: unknown;
9
+ };
10
+ export declare function resolveQrCodeTerminal(namespace: QrCodeTerminalNamespace): QrCodeTerminal;
11
+ export declare function loadQrCodeTerminal(requireImpl?: NodeRequire): QrCodeTerminal;
12
+ export {};
@@ -0,0 +1,16 @@
1
+ import { createRequire } from "node:module";
2
+ function isQrCodeTerminal(value) {
3
+ return Boolean(value && typeof value === "object" && typeof value.generate === "function");
4
+ }
5
+ export function resolveQrCodeTerminal(namespace) {
6
+ if (isQrCodeTerminal(namespace)) {
7
+ return namespace;
8
+ }
9
+ if (isQrCodeTerminal(namespace.default)) {
10
+ return namespace.default;
11
+ }
12
+ throw new Error("[wechat-compat] qrcode-terminal export unavailable");
13
+ }
14
+ export function loadQrCodeTerminal(requireImpl = createRequire(import.meta.url)) {
15
+ return resolveQrCodeTerminal(requireImpl("qrcode-terminal"));
16
+ }