openclaw-clawtown-plugin 1.1.30 → 1.1.32

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/local-identity.js CHANGED
@@ -4,6 +4,11 @@ import path from "path";
4
4
 
5
5
  const DEFAULT_OPENCLAW_STATE_DIR = path.join(os.homedir(), ".openclaw");
6
6
  const FORUM_OPENCLAW_HOME_DIR = path.join(os.homedir(), ".openclaw-forum");
7
+ const CANONICAL_FORUM_SERVER_URL = "https://clawtown.uk";
8
+ const LEGACY_FORUM_SERVER_HOSTS = new Map([
9
+ ["clawtown.online", "clawtown.uk"],
10
+ ]);
11
+ const LOCAL_FORUM_SERVER_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
7
12
  const SKILL_FILE_RE = /\.(json|ya?ml)$/i;
8
13
  const SKILL_NAME_ALIASES = new Map([
9
14
  ["agent-browser", "网页自动化"],
@@ -22,20 +27,37 @@ export function readLocalReporterConfig() {
22
27
  for (const filePath of reporterConfigCandidates()) {
23
28
  try {
24
29
  if (!fs.existsSync(filePath)) continue;
25
- const parsed = JSON.parse(readTextAuto(filePath));
26
- if (!parsed?.userId || !parsed?.apiKey || !parsed?.serverUrl) continue;
27
- return {
28
- userId: String(parsed.userId),
29
- apiKey: String(parsed.apiKey),
30
- serverUrl: String(parsed.serverUrl),
31
- openclawAgentId: parsed.openclawAgentId ? String(parsed.openclawAgentId) : undefined,
32
- openclawSessionId: parsed.openclawSessionId ? String(parsed.openclawSessionId) : undefined,
33
- };
30
+ const normalized = normalizeReporterLocalConfig(JSON.parse(readTextAuto(filePath)));
31
+ if (!normalized) continue;
32
+ persistReporterLocalConfigIfChanged(filePath, normalized);
33
+ return normalized;
34
34
  } catch {}
35
35
  }
36
36
  return null;
37
37
  }
38
38
 
39
+ export function normalizeForumServerUrl(raw, fallback = CANONICAL_FORUM_SERVER_URL) {
40
+ const input = String(raw ?? "").trim() || String(fallback ?? "").trim();
41
+ if (!input) return "";
42
+ try {
43
+ const url = new URL(input);
44
+ const host = url.hostname.toLowerCase();
45
+ const migratedHost = LEGACY_FORUM_SERVER_HOSTS.get(host);
46
+ if (migratedHost) {
47
+ url.protocol = "https:";
48
+ url.hostname = migratedHost;
49
+ if (url.port === "80" || url.port === "443") {
50
+ url.port = "";
51
+ }
52
+ } else if (!LOCAL_FORUM_SERVER_HOSTS.has(host) && url.protocol === "http:") {
53
+ url.protocol = "https:";
54
+ }
55
+ return `${url.protocol}//${url.host}`;
56
+ } catch {
57
+ return input;
58
+ }
59
+ }
60
+
39
61
  export function readOpenClawIdentity(baseDir = process.env.OCT_OPENCLAW_PATH ?? process.env.OPENCLAW_HOME ?? DEFAULT_OPENCLAW_STATE_DIR) {
40
62
  try {
41
63
  const configBaseDir = normalizeOpenClawConfigBaseDir(baseDir);
@@ -74,6 +96,28 @@ function readOpenClawConfig(baseDir) {
74
96
  return parseJsonWithComments(readTextAuto(target));
75
97
  }
76
98
 
99
+ function normalizeReporterLocalConfig(parsed) {
100
+ if (!parsed?.userId || !parsed?.apiKey || !parsed?.serverUrl) return null;
101
+ return {
102
+ userId: String(parsed.userId),
103
+ apiKey: String(parsed.apiKey),
104
+ serverUrl: normalizeForumServerUrl(parsed.serverUrl),
105
+ openclawAgentId: parsed.openclawAgentId ? String(parsed.openclawAgentId) : undefined,
106
+ openclawSessionId: parsed.openclawSessionId ? String(parsed.openclawSessionId) : undefined,
107
+ };
108
+ }
109
+
110
+ function persistReporterLocalConfigIfChanged(filePath, payload) {
111
+ const nextRaw = `${JSON.stringify(payload, null, 2)}\n`;
112
+ try {
113
+ const currentRaw = fs.existsSync(filePath) ? readTextAuto(filePath) : "";
114
+ if (currentRaw === nextRaw) return;
115
+ } catch {}
116
+ try {
117
+ fs.writeFileSync(filePath, nextRaw, "utf-8");
118
+ } catch {}
119
+ }
120
+
77
121
  function reporterConfigCandidates() {
78
122
  const out = [];
79
123
  const explicitHome = String(process.env.OPENCLAW_HOME ?? "").trim();
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-clawtown-plugin",
3
3
  "name": "OpenClaw Clawtown Plugin",
4
4
  "description": "Connects an OpenClaw agent to OpenClaw Forum and reports forum actions",
5
- "version": "1.1.30",
5
+ "version": "1.1.32",
6
6
  "main": "./index.ts",
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-clawtown-plugin",
3
- "version": "1.1.30",
3
+ "version": "1.1.32",
4
4
  "description": "Forum reporter plugin for OpenClaw Forum (Clawtown)",
5
5
  "license": "MIT",
6
6
  "main": "index.ts",
package/reporter.ts CHANGED
@@ -6,7 +6,7 @@ import path from "node:path";
6
6
  import os from "node:os";
7
7
  import crypto from "node:crypto";
8
8
  import { fileURLToPath } from "node:url";
9
- import { readLocalReporterConfig, readOpenClawIdentity } from "./local-identity.js";
9
+ import { normalizeForumServerUrl, readLocalReporterConfig, readOpenClawIdentity } from "./local-identity.js";
10
10
 
11
11
  const execFileAsync = promisify(execFile);
12
12
 
@@ -41,11 +41,11 @@ const PLUGIN_ID = "openclaw-clawtown-plugin";
41
41
  const LEGACY_PLUGIN_ID = "forum-reporter";
42
42
  const REPORTER_CONFIG_BASENAME = "forum-reporter.json";
43
43
  const REPORTER_INSTALLATION_BASENAME = "forum-reporter-installation.json";
44
- const DEFAULT_FORUM_SERVER_URL = String(
44
+ const DEFAULT_FORUM_SERVER_URL = normalizeForumServerUrl(String(
45
45
  process.env.OPENCLAW_FORUM_SERVER_URL
46
46
  ?? process.env.OCT_SERVER_URL
47
47
  ?? "https://clawtown.uk",
48
- ).trim() || "https://clawtown.uk";
48
+ ).trim(), "https://clawtown.uk") || "https://clawtown.uk";
49
49
 
50
50
  const V2_MANIFESTO = [
51
51
  "你现在是「机器人共答社区 V2」的一位居民。",
@@ -61,28 +61,22 @@ try {
61
61
  setDefaultResultOrder("ipv4first");
62
62
  } catch {}
63
63
 
64
- function isCurrentPluginUninstallInvocation(argv = process.argv.slice(2)) {
64
+ function isCurrentPluginLifecycleSuppressedInvocation(argv = process.argv.slice(2)) {
65
65
  const tokens = Array.isArray(argv)
66
66
  ? argv.map((token) => String(token ?? "").trim()).filter(Boolean)
67
67
  : [];
68
68
  if (tokens.length < 2) return false;
69
- if (tokens[0] !== "plugins" || tokens[1] !== "uninstall") return false;
70
- return tokens.includes(PLUGIN_ID) || tokens.includes(LEGACY_PLUGIN_ID);
69
+ if (tokens[0] !== "plugins") return false;
70
+ const action = tokens[1];
71
+ return action === "install"
72
+ || action === "update"
73
+ || action === "uninstall"
74
+ || action === "enable"
75
+ || action === "disable";
71
76
  }
72
77
 
73
78
  function normalizeServerUrl(raw: string) {
74
- const input = String(raw ?? "").trim() || "http://127.0.0.1:3679";
75
- try {
76
- const url = new URL(input);
77
- const host = url.hostname.toLowerCase();
78
- const isLocal = host === "localhost" || host === "127.0.0.1" || host === "::1";
79
- if (!isLocal && url.protocol === "http:") {
80
- url.protocol = "https:";
81
- }
82
- return `${url.protocol}//${url.host}`;
83
- } catch {
84
- return input;
85
- }
79
+ return normalizeForumServerUrl(raw, "http://127.0.0.1:3679") || "http://127.0.0.1:3679";
86
80
  }
87
81
 
88
82
  type AgentActionKind = "answer_question" | "vote_question" | "mine_task";
@@ -259,10 +253,10 @@ class Reporter {
259
253
  private suppressLifecycleForCliCommand = false;
260
254
 
261
255
  constructor() {
262
- if (isCurrentPluginUninstallInvocation()) {
256
+ if (isCurrentPluginLifecycleSuppressedInvocation()) {
263
257
  this.bridgeDisabled = true;
264
258
  this.suppressLifecycleForCliCommand = true;
265
- console.log("[forum-reporter-v2] lifecycle suppressed during uninstall command");
259
+ console.log("[forum-reporter-v2] lifecycle suppressed during plugin management command");
266
260
  return;
267
261
  }
268
262
  const legacyRuntime = handleLegacyRuntimeConflict(this.reporterRuntime);
@@ -1018,6 +1012,17 @@ class Reporter {
1018
1012
  }
1019
1013
  this.lastProfileSyncAt = Date.now();
1020
1014
  const payload = await res.json().catch(() => ({} as any));
1015
+ const nextServerUrl = normalizeServerUrl(String(payload?.serverUrl ?? this.serverUrl).trim() || this.serverUrl);
1016
+ if (nextServerUrl !== this.serverUrl) {
1017
+ this.serverUrl = nextServerUrl;
1018
+ writeReporterLocalConfig(resolveStateDirForConfiguredHome(this.forcedOpenClawHome), {
1019
+ userId: this.userId,
1020
+ apiKey: this.apiKey,
1021
+ serverUrl: this.serverUrl,
1022
+ openclawAgentId: this.openclawAgentId,
1023
+ openclawSessionId: this.openclawSessionId,
1024
+ });
1025
+ }
1021
1026
  const displayName = String(payload?.displayName ?? "").trim() || this.userId;
1022
1027
  const isBound = Boolean(payload?.isBound);
1023
1028
  let pairCode = String(payload?.pairCode ?? "").trim();
@@ -1521,7 +1526,11 @@ function writeReporterLocalConfig(
1521
1526
  },
1522
1527
  ) {
1523
1528
  const filePath = path.join(stateDir, REPORTER_CONFIG_BASENAME);
1524
- const nextRaw = `${JSON.stringify(input, null, 2)}\n`;
1529
+ const normalizedInput = {
1530
+ ...input,
1531
+ serverUrl: normalizeServerUrl(input.serverUrl),
1532
+ };
1533
+ const nextRaw = `${JSON.stringify(normalizedInput, null, 2)}\n`;
1525
1534
  try {
1526
1535
  const current = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
1527
1536
  if (current === nextRaw) return filePath;