@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/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 { setMixinRuntime } from "./src/runtime.js";
5
-
6
- // 全局错误处理
7
- process.on('unhandledRejection', (reason, promise) => {
8
- console.error('[mixin] Unhandled Rejection at:', promise, 'reason:', reason);
9
- // 但不退出进程,让OpenClaw处理
10
- });
11
-
12
- process.on('uncaughtException', (error) => {
13
- console.error('[mixin] Uncaught Exception:', error);
14
- // 但不退出进程,让OpenClaw处理
15
- });
16
-
17
- const plugin = {
18
- id: "mixin",
19
- name: "Mixin Messenger Channel",
20
- description: "Mixin Messenger channel via Blaze WebSocket or HTTP Webhook",
21
- configSchema: emptyPluginConfigSchema(),
22
- register(api: OpenClawPluginApi): void {
23
- setMixinRuntime(api.runtime);
24
- api.registerChannel({ plugin: mixinPlugin });
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;
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "id": "mixin",
3
- "channels": ["mixin"],
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": true,
11
+ "additionalProperties": false,
7
12
  "properties": {}
8
13
  }
9
- }
14
+ }
package/package.json CHANGED
@@ -1 +1,79 @@
1
- {"name":"@invago/mixin","version":"1.0.9","description":"Mixin Messenger channel plugin for OpenClaw","type":"module","main":"index.ts","bin":{"mixin-plugin-onboard":"tools/mixin-plugin-onboard/bin/mixin-plugin-onboard.mjs"},"scripts":{"dev":"nodemon --exec \"node --import jiti/register index.ts\" --ext ts","lint":"eslint src/**/*.ts index.ts","typecheck":"tsc --noEmit","tool:info":"node --import jiti/register.js tools/mixin-plugin-onboard/src/index.ts info","tool:doctor":"node --import jiti/register.js tools/mixin-plugin-onboard/src/index.ts doctor"},"peerDependencies":{"openclaw":">=2026.2.0"},"dependencies":{"@mixin.dev/mixin-node-sdk":"^7.4.1","@noble/curves":"^2.0.1","@noble/hashes":"^2.0.1","axios":"^1.6.0","express":"^5.2.1","jiti":"^1.21.0","proxy-agent":"^6.5.0","ws":"^8.18.3","zod":"^4.3.6"},"devDependencies":{"@eslint/js":"^10.0.1","@types/node":"^20.0.0","eslint":"^10.0.3","globals":"^17.4.0","nodemon":"^3.0.0","typescript":"^5.3.0","typescript-eslint":"^8.56.1"},"keywords":["openclaw","mixin","messenger","plugin","channel"],"author":"invagao","license":"MIT","repository":{"type":"git","url":"git+https://github.com/invago/mixinclaw.git"},"openclaw":{"extensions":["./index.ts"],"channel":{"id":"mixin","label":"Mixin Messenger","selectionLabel":"Mixin Messenger (Blaze WebSocket)","docsPath":"/channels/mixin","order":70,"aliases":["mixin-messenger","mixin"],"quickstartAllowFrom":true},"install":{"npmSpec":"@invago/mixin","localPath":"extensions/mixin"}}}
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
+ }
@@ -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",