@invago/mixin 1.0.9 → 1.0.11
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/README.md +690 -421
- package/README.zh-CN.md +693 -431
- package/index.ts +104 -27
- package/openclaw.plugin.json +8 -3
- package/package.json +79 -1
- package/src/blaze-service.ts +24 -7
- package/src/channel.ts +524 -411
- package/src/config-schema.ts +16 -0
- package/src/config.ts +58 -4
- package/src/crypto.ts +5 -0
- package/src/inbound-handler.ts +1205 -637
- package/src/mixpay-service.ts +211 -0
- package/src/mixpay-store.ts +205 -0
- package/src/mixpay-worker.ts +353 -0
- package/src/onboarding.ts +342 -0
- package/src/outbound-plan.ts +26 -7
- package/src/plugin-admin.ts +161 -0
- package/src/reply-format.ts +52 -1
- package/src/runtime.ts +26 -0
- package/src/send-service.ts +24 -27
- package/src/shared.ts +25 -0
- package/src/status.ts +14 -0
- package/src/decrypt.ts +0 -126
- package/tools/mixin-plugin-onboard/README.md +0 -98
- package/tools/mixin-plugin-onboard/bin/mixin-plugin-onboard.mjs +0 -3
- package/tools/mixin-plugin-onboard/src/commands/doctor.ts +0 -28
- package/tools/mixin-plugin-onboard/src/commands/info.ts +0 -23
- package/tools/mixin-plugin-onboard/src/commands/install.ts +0 -5
- package/tools/mixin-plugin-onboard/src/commands/update.ts +0 -5
- package/tools/mixin-plugin-onboard/src/index.ts +0 -49
- package/tools/mixin-plugin-onboard/src/utils.ts +0 -189
package/index.ts
CHANGED
|
@@ -1,28 +1,105 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
-
import { mixinPlugin } from "./src/channel.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
import type { OpenClawPluginApi, OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
+
import { mixinPlugin } from "./src/channel.js";
|
|
4
|
+
import {
|
|
5
|
+
buildMixinAccountsText,
|
|
6
|
+
buildMixinPluginDiagnostics,
|
|
7
|
+
formatMixinHelpText,
|
|
8
|
+
formatMixinSetupText,
|
|
9
|
+
formatMixinStatusText,
|
|
10
|
+
normalizeMixinSetupMode,
|
|
11
|
+
} from "./src/plugin-admin.js";
|
|
12
|
+
import { setMixinRuntime } from "./src/runtime.js";
|
|
13
|
+
|
|
14
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
15
|
+
console.error("[mixin] Unhandled Rejection at:", promise, "reason:", reason);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
process.on("uncaughtException", (error) => {
|
|
19
|
+
console.error("[mixin] Uncaught Exception:", error);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
async function buildStatusText(cfg: OpenClawConfig): Promise<string> {
|
|
23
|
+
const diagnostics = await buildMixinPluginDiagnostics(cfg);
|
|
24
|
+
return formatMixinStatusText(cfg, diagnostics);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function buildSetupText(cfg: OpenClawConfig, mode?: string): Promise<string> {
|
|
28
|
+
const diagnostics = await buildMixinPluginDiagnostics(cfg);
|
|
29
|
+
return formatMixinSetupText(cfg, diagnostics, normalizeMixinSetupMode(mode ?? ""));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const plugin = {
|
|
33
|
+
id: "mixin",
|
|
34
|
+
name: "Mixin Messenger Channel",
|
|
35
|
+
description: "Mixin Messenger channel via Blaze WebSocket",
|
|
36
|
+
configSchema: emptyPluginConfigSchema(),
|
|
37
|
+
register(api: OpenClawPluginApi): void {
|
|
38
|
+
setMixinRuntime(api.runtime);
|
|
39
|
+
api.registerChannel({ plugin: mixinPlugin });
|
|
40
|
+
api.registerService({
|
|
41
|
+
id: "mixin-diagnostics",
|
|
42
|
+
start: () => api.logger.info("[mixin] diagnostics service ready"),
|
|
43
|
+
stop: () => api.logger.info("[mixin] diagnostics service stopped"),
|
|
44
|
+
});
|
|
45
|
+
api.registerGatewayMethod("mixin.status", async ({ respond }) => {
|
|
46
|
+
const cfg = api.config as OpenClawConfig;
|
|
47
|
+
const diagnostics = await buildMixinPluginDiagnostics(cfg);
|
|
48
|
+
respond(true, {
|
|
49
|
+
status: formatMixinStatusText(cfg, diagnostics),
|
|
50
|
+
diagnostics,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
api.registerGatewayMethod("mixin.accounts", async ({ respond }) => {
|
|
54
|
+
const cfg = api.config as OpenClawConfig;
|
|
55
|
+
respond(true, {
|
|
56
|
+
accounts: buildMixinAccountsText(cfg),
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
api.registerGatewayMethod("mixin.setup", async ({ respond }) => {
|
|
60
|
+
const cfg = api.config as OpenClawConfig;
|
|
61
|
+
respond(true, {
|
|
62
|
+
setup: await buildSetupText(cfg),
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
api.registerCommand({
|
|
66
|
+
name: "setup",
|
|
67
|
+
description: "Show Mixin setup guide",
|
|
68
|
+
acceptsArgs: true,
|
|
69
|
+
handler: async (ctx: { config: OpenClawConfig; args?: string }) => ({
|
|
70
|
+
text: await buildSetupText(ctx.config, ctx.args),
|
|
71
|
+
}),
|
|
72
|
+
});
|
|
73
|
+
api.registerCommand({
|
|
74
|
+
name: "mixin-setup",
|
|
75
|
+
description: "Show Mixin setup guide",
|
|
76
|
+
acceptsArgs: true,
|
|
77
|
+
handler: async (ctx: { config: OpenClawConfig; args?: string }) => ({
|
|
78
|
+
text: await buildSetupText(ctx.config, ctx.args),
|
|
79
|
+
}),
|
|
80
|
+
});
|
|
81
|
+
api.registerCommand({
|
|
82
|
+
name: "mixin-status",
|
|
83
|
+
description: "Show Mixin plugin status",
|
|
84
|
+
handler: async (ctx: { config: OpenClawConfig }) => ({
|
|
85
|
+
text: await buildStatusText(ctx.config),
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
api.registerCommand({
|
|
89
|
+
name: "mixin-accounts",
|
|
90
|
+
description: "List configured Mixin accounts",
|
|
91
|
+
handler: async (ctx: { config: OpenClawConfig }) => ({
|
|
92
|
+
text: buildMixinAccountsText(ctx.config),
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
api.registerCommand({
|
|
96
|
+
name: "mixin-help",
|
|
97
|
+
description: "Show Mixin plugin help",
|
|
98
|
+
handler: async () => ({
|
|
99
|
+
text: formatMixinHelpText(),
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
28
105
|
export default plugin;
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "mixin",
|
|
3
|
-
"
|
|
3
|
+
"name": "Mixin Messenger Channel",
|
|
4
|
+
"description": "Mixin Messenger channel via Blaze WebSocket",
|
|
5
|
+
"version": "1.0.10",
|
|
6
|
+
"channels": [
|
|
7
|
+
"mixin"
|
|
8
|
+
],
|
|
4
9
|
"configSchema": {
|
|
5
10
|
"type": "object",
|
|
6
|
-
"additionalProperties":
|
|
11
|
+
"additionalProperties": false,
|
|
7
12
|
"properties": {}
|
|
8
13
|
}
|
|
9
|
-
}
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +1,79 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "@invago/mixin",
|
|
3
|
+
"version": "1.0.11",
|
|
4
|
+
"description": "Mixin Messenger channel plugin for OpenClaw",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "nodemon --exec \"node --import jiti/register index.ts\" --ext ts",
|
|
9
|
+
"lint": "eslint src/**/*.ts index.ts",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"openclaw": ">=2026.2.0"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@mixin.dev/mixin-node-sdk": "^7.4.1",
|
|
17
|
+
"@noble/curves": "^2.0.1",
|
|
18
|
+
"@noble/hashes": "^2.0.1",
|
|
19
|
+
"axios": "^1.13.6",
|
|
20
|
+
"express": "^5.2.1",
|
|
21
|
+
"jiti": "^1.21.0",
|
|
22
|
+
"proxy-agent": "^6.5.0",
|
|
23
|
+
"ws": "^8.18.3",
|
|
24
|
+
"zod": "^4.3.6"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@eslint/js": "^10.0.1",
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"eslint": "^10.0.3",
|
|
30
|
+
"globals": "^17.4.0",
|
|
31
|
+
"nodemon": "^3.0.0",
|
|
32
|
+
"typescript": "^5.3.0",
|
|
33
|
+
"typescript-eslint": "^8.56.1"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"openclaw",
|
|
37
|
+
"mixin",
|
|
38
|
+
"messenger",
|
|
39
|
+
"plugin",
|
|
40
|
+
"channel"
|
|
41
|
+
],
|
|
42
|
+
"author": "invagao",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/invago/mixinclaw.git"
|
|
47
|
+
},
|
|
48
|
+
"openclaw": {
|
|
49
|
+
"extensions": [
|
|
50
|
+
"./index.ts"
|
|
51
|
+
],
|
|
52
|
+
"channel": {
|
|
53
|
+
"id": "mixin",
|
|
54
|
+
"label": "Mixin Messenger",
|
|
55
|
+
"selectionLabel": "Mixin Messenger (Blaze WebSocket)",
|
|
56
|
+
"docsPath": "/channels/mixin",
|
|
57
|
+
"order": 70,
|
|
58
|
+
"aliases": [
|
|
59
|
+
"mixin-messenger",
|
|
60
|
+
"mixin"
|
|
61
|
+
],
|
|
62
|
+
"quickstartAllowFrom": true
|
|
63
|
+
},
|
|
64
|
+
"install": {
|
|
65
|
+
"npmSpec": "@invago/mixin",
|
|
66
|
+
"localPath": "extensions/mixin"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"files": [
|
|
70
|
+
"README.md",
|
|
71
|
+
"README.zh-CN.md",
|
|
72
|
+
"index.ts",
|
|
73
|
+
"openclaw.plugin.json",
|
|
74
|
+
"package.json",
|
|
75
|
+
"src/",
|
|
76
|
+
"tsconfig.json",
|
|
77
|
+
"eslint.config.mjs"
|
|
78
|
+
]
|
|
79
|
+
}
|
package/src/blaze-service.ts
CHANGED
|
@@ -9,12 +9,8 @@ import WebSocket from "ws";
|
|
|
9
9
|
import crypto from "crypto";
|
|
10
10
|
import type { MixinAccountConfig } from "./config-schema.js";
|
|
11
11
|
import { createProxyAgent } from "./proxy.js";
|
|
12
|
-
|
|
13
|
-
type SendLog
|
|
14
|
-
info: (msg: string) => void;
|
|
15
|
-
error: (msg: string, err?: unknown) => void;
|
|
16
|
-
warn: (msg: string) => void;
|
|
17
|
-
};
|
|
12
|
+
import type { MixinBlazeOutboundMessage } from "./runtime.js";
|
|
13
|
+
import type { SendLog } from "./shared.js";
|
|
18
14
|
|
|
19
15
|
function buildKeystore(config: MixinAccountConfig) {
|
|
20
16
|
return {
|
|
@@ -50,8 +46,9 @@ export async function runBlazeLoop(params: {
|
|
|
50
46
|
handler: BlazeHandler;
|
|
51
47
|
log: SendLog;
|
|
52
48
|
abortSignal?: AbortSignal;
|
|
49
|
+
onSenderReady?: ((sender: ((message: MixinBlazeOutboundMessage) => Promise<void>) | null) => void) | undefined;
|
|
53
50
|
}): Promise<void> {
|
|
54
|
-
const { config, options, handler, log, abortSignal } = params;
|
|
51
|
+
const { config, options, handler, log, abortSignal, onSenderReady } = params;
|
|
55
52
|
const keystore = buildKeystore(config);
|
|
56
53
|
const jwtToken = signAccessToken("GET", "/", "", crypto.randomUUID(), keystore) || "";
|
|
57
54
|
const agent = createProxyAgent(config.proxy);
|
|
@@ -67,6 +64,7 @@ export async function runBlazeLoop(params: {
|
|
|
67
64
|
clearTimeout(pingTimeout);
|
|
68
65
|
pingTimeout = null;
|
|
69
66
|
}
|
|
67
|
+
onSenderReady?.(null);
|
|
70
68
|
abortSignal?.removeEventListener("abort", onAbort);
|
|
71
69
|
};
|
|
72
70
|
|
|
@@ -118,6 +116,25 @@ export async function runBlazeLoop(params: {
|
|
|
118
116
|
ws.on("open", () => {
|
|
119
117
|
opened = true;
|
|
120
118
|
heartbeat();
|
|
119
|
+
onSenderReady?.(async (message) => {
|
|
120
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
121
|
+
throw new Error("blaze sender unavailable: socket not open");
|
|
122
|
+
}
|
|
123
|
+
const ok = await sendRaw(ws, {
|
|
124
|
+
id: crypto.randomUUID(),
|
|
125
|
+
action: "CREATE_MESSAGE",
|
|
126
|
+
params: {
|
|
127
|
+
conversation_id: message.conversationId,
|
|
128
|
+
status: "SENT",
|
|
129
|
+
message_id: message.messageId,
|
|
130
|
+
category: message.category,
|
|
131
|
+
data: message.dataBase64,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
if (!ok) {
|
|
135
|
+
throw new Error("blaze sender timeout");
|
|
136
|
+
}
|
|
137
|
+
});
|
|
121
138
|
void sendRaw(ws!, {
|
|
122
139
|
id: crypto.randomUUID(),
|
|
123
140
|
action: "LIST_PENDING_MESSAGES",
|