palz-connector 1.4.9 → 1.5.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.
package/index.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Palz Connector Channel Plugin for OpenClaw
3
+ *
4
+ * 入口文件,遵循 OpenClaw 插件规范:
5
+ * - register(api) 注册 channel
6
+ * - 通过 OpenClaw Runtime 完成 AI 对话(而非直接操作 Gateway WebSocket)
7
+ * - IM 通信协议保持不变(WebSocket 接收消息,HTTP POST 发送回复)
8
+ */
9
+ import { initTracing } from "./src/tracing.js";
10
+ import { palzPlugin } from "./src/channel.js";
11
+ import { setPalzRuntime } from "./src/runtime.js";
12
+ const plugin = {
13
+ id: "palz-connector",
14
+ name: "Palz Connector Channel",
15
+ description: "Palz IM 接入 OpenClaw",
16
+ configSchema: {
17
+ type: "object",
18
+ additionalProperties: false,
19
+ properties: {},
20
+ },
21
+ register(api) {
22
+ const log = typeof api.runtime?.log === "function" ? api.runtime.log : console.log;
23
+ initTracing();
24
+ log("palz-connector: register() called, saving runtime and registering channel");
25
+ setPalzRuntime(api.runtime);
26
+ api.registerChannel({ plugin: palzPlugin });
27
+ log("palz-connector: channel registered successfully");
28
+ },
29
+ };
30
+ export default plugin;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "palz-connector",
3
3
  "name": "Palz Connector Channel",
4
- "version": "1.4.9",
4
+ "version": "1.5.1",
5
5
  "description": "Palz IM 接入 OpenClaw",
6
6
  "channels": [
7
7
  "palz-connector"
package/package.json CHANGED
@@ -1,12 +1,20 @@
1
1
  {
2
2
  "name": "palz-connector",
3
- "version": "1.4.9",
3
+ "version": "1.5.1",
4
4
  "type": "module",
5
- "main": "index.ts",
5
+ "main": "index.js",
6
6
  "description": "Palz IM 接入 OpenClaw — 模块化架构,基于 OpenClaw Runtime 消息管道",
7
+ "scripts": {
8
+ "build": "tsc"
9
+ },
10
+ "files": [
11
+ "index.js",
12
+ "src/**/*.js",
13
+ "*.json"
14
+ ],
7
15
  "openclaw": {
8
16
  "extensions": [
9
- "./index.ts"
17
+ "./index.js"
10
18
  ],
11
19
  "channel": {
12
20
  "id": "palz-connector",
@@ -24,7 +32,11 @@
24
32
  "@opentelemetry/sdk-trace-node": "^2.6.1",
25
33
  "@opentelemetry/semantic-conventions": "^1.40.0",
26
34
  "ali-oss": "^6.21.0",
27
- "ws": "^8.18.0",
28
- "mcporter": "^0.8.1"
35
+ "mcporter": "^0.8.1",
36
+ "ws": "^8.18.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^25.7.0",
40
+ "typescript": "^6.0.3"
29
41
  }
30
42
  }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Palz IM 收到消息后的 claw-gateway 活动上报。
3
+ */
4
+ const ACTIVITY_REPORT_TIMEOUT_MS = 2000;
5
+ const ACTIVITY_REPORT_PATH_PREFIX = "/openclaw-gateway/be/deployments";
6
+ const warnedMissingConfig = new Set();
7
+ function formatDateTimeWithOffset(date) {
8
+ const pad = (n, width = 2) => String(n).padStart(width, "0");
9
+ const offsetMinutes = -date.getTimezoneOffset();
10
+ const sign = offsetMinutes >= 0 ? "+" : "-";
11
+ const absOffset = Math.abs(offsetMinutes);
12
+ const offsetHours = Math.floor(absOffset / 60);
13
+ const offsetRestMinutes = absOffset % 60;
14
+ return [
15
+ `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`,
16
+ "T",
17
+ `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`,
18
+ `${sign}${pad(offsetHours)}:${pad(offsetRestMinutes)}`,
19
+ ].join("");
20
+ }
21
+ function resolveActivityUrl(baseUrl, releaseName) {
22
+ const normalizedBase = baseUrl.replace(/\/+$/, "");
23
+ return `${normalizedBase}${ACTIVITY_REPORT_PATH_PREFIX}/${encodeURIComponent(releaseName)}/activity`;
24
+ }
25
+ export async function reportPalzActivity(params) {
26
+ const { config, msg, receivedAt, runtime, accountId } = params;
27
+ const log = typeof runtime?.log === "function" ? runtime.log : console.log;
28
+ const error = typeof runtime?.error === "function" ? runtime.error : console.error;
29
+ const tag = `palz[${accountId ?? config.botId ?? "default"}]`;
30
+ if (config.activityReportEnabled !== true)
31
+ return;
32
+ const baseUrl = config.clawGatewayUrl?.trim();
33
+ const releaseName = (process.env.botID || config.botId || "").trim();
34
+ if (!baseUrl || !releaseName) {
35
+ const warnKey = `${accountId ?? ""}:${baseUrl ? "hasBaseUrl" : "missingBaseUrl"}:${releaseName ? "hasRelease" : "missingRelease"}`;
36
+ if (!warnedMissingConfig.has(warnKey)) {
37
+ warnedMissingConfig.add(warnKey);
38
+ error(`${tag}: [ACTIVITY_REPORT] enabled but missing ${!baseUrl ? "clawGatewayUrl" : "botID/releaseName"}, skip`);
39
+ }
40
+ return;
41
+ }
42
+ const payload = {
43
+ source: "palz-connector",
44
+ eventType: "im_message_received",
45
+ messageId: msg.msg_id,
46
+ conversationId: msg.conversation_id,
47
+ conversationType: msg.conversation_type || "direct",
48
+ userId: msg.owner_id ?? "",
49
+ senderId: msg.sender_id,
50
+ receivedAt: formatDateTimeWithOffset(receivedAt),
51
+ };
52
+ if (msg.traceparent) {
53
+ payload.traceId = msg.traceparent;
54
+ }
55
+ const url = resolveActivityUrl(baseUrl, releaseName);
56
+ const body = JSON.stringify(payload);
57
+ const controller = new AbortController();
58
+ const timeout = setTimeout(() => controller.abort(), ACTIVITY_REPORT_TIMEOUT_MS);
59
+ const startMs = Date.now();
60
+ try {
61
+ const response = await fetch(url, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body,
65
+ signal: controller.signal,
66
+ });
67
+ const elapsedMs = Date.now() - startMs;
68
+ const responseText = await response.text().catch(() => "");
69
+ if (!response.ok) {
70
+ error(`${tag}: [ACTIVITY_REPORT] failed status=${response.status} elapsed=${elapsedMs}ms msg_id=${msg.msg_id} response=${responseText.slice(0, 300)}`);
71
+ return;
72
+ }
73
+ log(`${tag}: [ACTIVITY_REPORT] ok status=${response.status} elapsed=${elapsedMs}ms msg_id=${msg.msg_id}`);
74
+ }
75
+ catch (err) {
76
+ const elapsedMs = Date.now() - startMs;
77
+ const reason = err?.name === "AbortError" ? `timeout ${ACTIVITY_REPORT_TIMEOUT_MS}ms` : (err?.message ?? String(err));
78
+ error(`${tag}: [ACTIVITY_REPORT] error elapsed=${elapsedMs}ms msg_id=${msg.msg_id} reason=${reason}`);
79
+ }
80
+ finally {
81
+ clearTimeout(timeout);
82
+ }
83
+ }