@chbo297/infoflow 2026.5.4 → 2026.5.6-beta.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.
package/README.md CHANGED
@@ -37,7 +37,55 @@ bash scripts/deploy.sh
37
37
  BAIDU_NPM_REGISTRY=http://registry.npm.baidu-int.com bash scripts/deploy.sh
38
38
  ```
39
39
 
40
+ ### 通过 npx 一键更新安装
41
+
42
+ 发布到 npm 后,可直接通过 `npx` 安装/升级到指定版本:
43
+
44
+ ```bash
45
+ npm_config_registry=http://registry.npm.baidu-int.com npx -y @chbo297/infoflow update --version 2026.5.5
46
+ ```
47
+
48
+ 常用参数:
49
+
50
+ - `--version <version>`: 指定安装版本(默认 `latest`)
51
+ - `--registry <url>`: 指定 npm 源(默认读取 `npm_config_registry`,否则回退 `http://registry.npm.baidu-int.com`)
52
+ - `--channel-id <id>`: 目标插件目录名(默认 `infoflow`,安装到 `~/.openclaw/extensions/<id>`)
53
+ - `--dry-run`: 仅打印命令,不写入系统
54
+
55
+ 说明:
56
+
57
+ - `npx ... update` 与 `bash scripts/deploy.sh` 复用同一套部署核心逻辑(依赖安装、websocket 依赖校验、构建、配置写入、按状态重启 gateway)。
58
+ - 如果 gateway 未运行,脚本会跳过重启,仅完成插件安装与构建。
59
+
40
60
  ### WebSocket 模式(可选)
41
61
 
42
62
  当 `connectionMode="websocket"` 时,插件会动态 `import("@baidu/infoflow-sdk-nodejs")`。
43
- 该依赖在 `peerDependencies` 中标记为 optional:不使用 websocket 模式时无需安装。
63
+ 该依赖在 `peerDependencies` 中标记为 optional:不使用 websocket 模式时无需安装。
64
+
65
+ ### 版本升级、打 tag、推送与 npm 发布流程
66
+
67
+ 每次发布新版本(例如 `2026.5.5`)建议按以下顺序执行:
68
+
69
+ ```bash
70
+ # 1) 修改版本号(会同步 package-lock.json)
71
+ npm version 2026.5.5 --no-git-tag-version
72
+
73
+ # 2) 发布前校验
74
+ npm run typecheck
75
+ npm run test
76
+ npm run build
77
+
78
+ # 3) 提交版本变更
79
+ git add package.json package-lock.json README.md scripts src
80
+ git commit -m "2026.5.5"
81
+
82
+ # 4) 打 tag 并推送代码与 tag
83
+ git tag 2026.5.5
84
+ git push origin main
85
+ git push origin 2026.5.5
86
+
87
+ # 5) 发布 npm(可按需指定 registry)
88
+ npm publish
89
+ # 或
90
+ # npm publish --registry https://registry.npmjs.org
91
+ ```
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { homedir, tmpdir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { spawnSync } from "node:child_process";
7
+ const DEFAULT_REGISTRY = "http://registry.npm.baidu-int.com";
8
+ function printHelp() {
9
+ console.log(`Usage:
10
+ npx -y @chbo297/infoflow update [options]
11
+
12
+ Commands:
13
+ update Download and install/update Infoflow plugin
14
+
15
+ Options:
16
+ --version <version> Package version (default: latest)
17
+ --registry <url> npm registry URL (default: npm_config_registry or ${DEFAULT_REGISTRY})
18
+ --channel-id <id> OpenClaw channel/plugin id (default: infoflow)
19
+ --dry-run Print commands without changing system
20
+ --source-dir <path> Internal use: deploy directly from local source directory
21
+ -h, --help Show help
22
+ `);
23
+ }
24
+ function runOrFail(command, args, cwd, dryRun) {
25
+ const line = [command, ...args].join(" ");
26
+ console.log(`$ (${cwd}) ${line}`);
27
+ if (dryRun)
28
+ return;
29
+ const result = spawnSync(command, args, { cwd, stdio: "inherit" });
30
+ if (result.status !== 0) {
31
+ process.exit(result.status ?? 1);
32
+ }
33
+ }
34
+ function runAndCollect(command, args, cwd) {
35
+ const result = spawnSync(command, args, { cwd, encoding: "utf8" });
36
+ if (result.status !== 0) {
37
+ if (result.stdout)
38
+ process.stdout.write(result.stdout);
39
+ if (result.stderr)
40
+ process.stderr.write(result.stderr);
41
+ process.exit(result.status ?? 1);
42
+ }
43
+ return result.stdout ?? "";
44
+ }
45
+ function parseArgs(argv) {
46
+ const args = [...argv];
47
+ const command = args.shift() ?? "";
48
+ const options = {
49
+ version: "latest",
50
+ registry: process.env.npm_config_registry || DEFAULT_REGISTRY,
51
+ channelId: "infoflow",
52
+ dryRun: false,
53
+ };
54
+ for (let i = 0; i < args.length; i += 1) {
55
+ const value = args[i];
56
+ if (value === "--version")
57
+ options.version = args[++i] ?? options.version;
58
+ else if (value === "--registry")
59
+ options.registry = args[++i] ?? options.registry;
60
+ else if (value === "--channel-id")
61
+ options.channelId = args[++i] ?? options.channelId;
62
+ else if (value === "--source-dir")
63
+ options.sourceDir = args[++i];
64
+ else if (value === "--dry-run")
65
+ options.dryRun = true;
66
+ else if (value === "-h" || value === "--help") {
67
+ printHelp();
68
+ process.exit(0);
69
+ }
70
+ else {
71
+ console.error(`Unknown option: ${value}`);
72
+ printHelp();
73
+ process.exit(1);
74
+ }
75
+ }
76
+ return { command, options };
77
+ }
78
+ function installFromRegistry(options, packageName, pluginDir) {
79
+ const tempRoot = mkdtempSync(join(tmpdir(), "infoflow-update-"));
80
+ try {
81
+ const spec = `${packageName}@${options.version}`;
82
+ let tarball = "";
83
+ if (options.dryRun) {
84
+ console.log(`$ (${tempRoot}) npm pack ${spec} --registry ${options.registry} --json`);
85
+ tarball = "<generated-by-npm-pack>.tgz";
86
+ }
87
+ else {
88
+ const output = runAndCollect("npm", ["pack", spec, "--registry", options.registry, "--json"], tempRoot);
89
+ const parsed = JSON.parse(output);
90
+ tarball = parsed[0]?.filename ?? "";
91
+ if (!tarball) {
92
+ console.error("Unable to resolve packed tarball filename from npm pack output.");
93
+ process.exit(1);
94
+ }
95
+ }
96
+ runOrFail("tar", ["-xzf", tarball], tempRoot, options.dryRun);
97
+ runOrFail("mkdir", ["-p", pluginDir], tempRoot, options.dryRun);
98
+ runOrFail("rsync", ["-av", "--delete", `${join(tempRoot, "package")}/`, `${pluginDir}/`, "--exclude", "node_modules", "--exclude", "dist"], tempRoot, options.dryRun);
99
+ }
100
+ finally {
101
+ if (!options.dryRun)
102
+ rmSync(tempRoot, { recursive: true, force: true });
103
+ }
104
+ }
105
+ function installFromSource(options, pluginDir) {
106
+ const sourceDir = resolve(options.sourceDir);
107
+ runOrFail("mkdir", ["-p", pluginDir], sourceDir, options.dryRun);
108
+ runOrFail("rsync", [
109
+ "-av",
110
+ "--delete",
111
+ `${sourceDir}/`,
112
+ `${pluginDir}/`,
113
+ "--exclude",
114
+ "node_modules",
115
+ "--exclude",
116
+ "dist",
117
+ "--exclude",
118
+ ".git",
119
+ ], sourceDir, options.dryRun);
120
+ }
121
+ function main() {
122
+ const { command, options } = parseArgs(process.argv.slice(2));
123
+ if (!command) {
124
+ printHelp();
125
+ process.exit(0);
126
+ }
127
+ if (command !== "update") {
128
+ console.error(`Unknown command: ${command}`);
129
+ printHelp();
130
+ process.exit(1);
131
+ }
132
+ const filePath = fileURLToPath(import.meta.url);
133
+ const packageDir = resolve(dirname(filePath), "..", "..");
134
+ const pkg = JSON.parse(readFileSync(resolve(packageDir, "package.json"), "utf8"));
135
+ const packageName = pkg.name || "@chbo297/infoflow";
136
+ const pluginDir = resolve(process.env.HOME || homedir(), ".openclaw", "extensions", options.channelId);
137
+ if (options.sourceDir) {
138
+ installFromSource(options, pluginDir);
139
+ }
140
+ else {
141
+ installFromRegistry(options, packageName, pluginDir);
142
+ }
143
+ const commonScriptPath = join(pluginDir, "scripts", "lib", "deploy-common.sh");
144
+ runOrFail("bash", [
145
+ commonScriptPath,
146
+ "--plugin-dir",
147
+ pluginDir,
148
+ "--plugin-id",
149
+ options.channelId,
150
+ "--config-file",
151
+ resolve(process.env.HOME || homedir(), ".openclaw", "openclaw.json"),
152
+ "--baidu-registry",
153
+ options.registry,
154
+ ...(options.dryRun ? ["--dry-run"] : []),
155
+ ], pluginDir, options.dryRun);
156
+ }
157
+ main();
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@chbo297/infoflow",
3
- "version": "2026.5.4",
3
+ "version": "2026.5.6-beta.0",
4
4
  "description": "OpenClaw Infoflow (如流) channel plugin for Baidu enterprise messaging",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
+ "bin": {
8
+ "infoflow-openclaw": "dist/src/cli.js"
9
+ },
7
10
  "license": "MIT",
8
11
  "keywords": [
9
12
  "openclaw",
@@ -29,7 +32,7 @@
29
32
  },
30
33
  "openclaw": {
31
34
  "extensions": [
32
- "./index.ts"
35
+ "./dist/index.js"
33
36
  ],
34
37
  "channel": {
35
38
  "id": "infoflow",
@@ -52,5 +55,12 @@
52
55
  "devDependencies": {
53
56
  "typescript": "^6.0.3",
54
57
  "vitest": "^4.1.5"
55
- }
58
+ },
59
+ "files": [
60
+ "dist/**",
61
+ "openclaw.plugin.json",
62
+ "scripts/deploy.sh",
63
+ "scripts/lib/**",
64
+ "README.md"
65
+ ]
56
66
  }
package/scripts/deploy.sh CHANGED
@@ -1,215 +1,29 @@
1
1
  #!/usr/bin/env bash
2
2
  # 部署 openclaw-infoflow 插件到本地 OpenClaw 并重启 gateway
3
3
 
4
- set -e
4
+ set -euo pipefail
5
5
 
6
6
  PLUGIN_ID="infoflow"
7
7
  PLUGIN_DIR="$HOME/.openclaw/extensions/$PLUGIN_ID"
8
8
  CONFIG_FILE="$HOME/.openclaw/openclaw.json"
9
9
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
10
  PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
11
+ COMMON_SCRIPT="$SCRIPT_DIR/lib/deploy-common.sh"
11
12
 
12
13
  echo "==> 同步插件文件到 $PLUGIN_DIR"
13
14
  mkdir -p "$PLUGIN_DIR"
14
15
  rsync -av --delete "$PROJECT_DIR/" "$PLUGIN_DIR/" \
15
16
  --exclude node_modules \
16
17
  --exclude dist \
17
- --exclude .git \
18
- --exclude scripts
18
+ --exclude .git
19
19
 
20
- echo "==> 链接 openclaw peer dependency(build 前确保可解析)"
21
- if [ -n "$OPENCLAW_DIR" ] && [ -d "$OPENCLAW_DIR" ]; then
22
- OPENCLAW_GLOBAL="$OPENCLAW_DIR"
23
- elif command -v pnpm >/dev/null 2>&1; then
24
- OPENCLAW_GLOBAL="$(pnpm root -g 2>/dev/null)/openclaw"
25
- else
26
- OPENCLAW_GLOBAL="$(npm root -g 2>/dev/null)/openclaw"
27
- fi
28
-
29
- if [ -d "$OPENCLAW_GLOBAL" ]; then
30
- mkdir -p "$PLUGIN_DIR/node_modules"
31
- rm -rf "$PLUGIN_DIR/node_modules/openclaw"
32
- ln -s "$OPENCLAW_GLOBAL" "$PLUGIN_DIR/node_modules/openclaw"
33
- echo " ✓ 已链接 $OPENCLAW_GLOBAL -> $PLUGIN_DIR/node_modules/openclaw"
34
- else
35
- echo " ✗ 找不到全局 openclaw 安装,尝试使用 which openclaw 推断..."
36
- OPENCLAW_BIN="$(which openclaw 2>/dev/null)"
37
- if [ -n "$OPENCLAW_BIN" ]; then
38
- # pnpm global shim (macOS): extract the openclaw.mjs path from the shim.
39
- OPENCLAW_SHIM_BASEDIR="$(cd "$(dirname "$OPENCLAW_BIN")" && pwd)"
40
- OPENCLAW_MJS_REL="$(grep -Eo 'global/[^ ]+/\\.pnpm/openclaw@[^ ]+/node_modules/openclaw/openclaw\\.mjs' "$OPENCLAW_BIN" | head -1)"
41
- if [ -n "$OPENCLAW_MJS_REL" ]; then
42
- OPENCLAW_MJS_ABS="$OPENCLAW_SHIM_BASEDIR/$OPENCLAW_MJS_REL"
43
- OPENCLAW_GLOBAL="$(dirname "$OPENCLAW_MJS_ABS")"
44
- else
45
- # npm layout fallback
46
- OPENCLAW_GLOBAL="$(cd "$(dirname "$OPENCLAW_BIN")/.." && pwd)/lib/node_modules/openclaw"
47
- fi
48
-
49
- if [ -d "$OPENCLAW_GLOBAL" ]; then
50
- mkdir -p "$PLUGIN_DIR/node_modules"
51
- rm -rf "$PLUGIN_DIR/node_modules/openclaw"
52
- ln -s "$OPENCLAW_GLOBAL" "$PLUGIN_DIR/node_modules/openclaw"
53
- echo " ✓ 已链接 $OPENCLAW_GLOBAL -> $PLUGIN_DIR/node_modules/openclaw"
54
- else
55
- echo " ✗ 无法找到 openclaw 安装目录,跳过链接(插件可能无法加载/无法编译)"
56
- fi
57
- else
58
- echo " ✗ openclaw 未安装,跳过链接(插件可能无法加载/无法编译)"
59
- fi
60
- fi
61
-
62
- echo "==> 安装依赖"
63
- cd "$PLUGIN_DIR" && npm install --silent
64
-
65
- WEBSOCKET_ENABLED="false"
66
- if [ -f "$CONFIG_FILE" ]; then
67
- WEBSOCKET_ENABLED="$(node -e "
68
- const fs = require('fs');
69
- try {
70
- const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
71
- const section = cfg?.channels?.infoflow ?? {};
72
- const topMode = section?.connectionMode;
73
- const accounts = section?.accounts && typeof section.accounts === 'object' ? Object.values(section.accounts) : [];
74
- const accountWebsocket = accounts.some((acc) => acc && typeof acc === 'object' && acc.connectionMode === 'websocket');
75
- const enabled = topMode === 'websocket' || accountWebsocket;
76
- process.stdout.write(enabled ? 'true' : 'false');
77
- } catch {
78
- process.stdout.write('false');
79
- }
80
- ")"
81
- fi
82
-
83
- if [ "$WEBSOCKET_ENABLED" = "true" ]; then
84
- echo "==> 检测到 websocket 模式,确保安装 @baidu/infoflow-sdk-nodejs"
85
- BAIDU_NPM_REGISTRY="${BAIDU_NPM_REGISTRY:-${NPM_CONFIG_REGISTRY_BAIDU:-http://registry.npm.baidu-int.com}}"
86
-
87
- # 校验方式用 import()(与插件运行时一致)。注意:一些包可能禁止访问 package.json 子路径。
88
- if node --input-type=module -e "import('@baidu/infoflow-sdk-nodejs').then(()=>process.exit(0)).catch(()=>process.exit(1))"; then
89
- echo " ✓ @baidu/infoflow-sdk-nodejs 已可用"
90
- else
91
- echo " - 使用私有源安装: $BAIDU_NPM_REGISTRY"
92
- # 重要:npm 11 对 peerDependencies + --no-save 的行为在某些场景下不会落盘安装。
93
- # 这里改为写入部署目录的 optionalDependencies(不会影响仓库,下一次 rsync 会覆盖),确保依赖真的安装到 node_modules。
94
- npm_config_registry="$BAIDU_NPM_REGISTRY" npm install --save-optional --registry "$BAIDU_NPM_REGISTRY" @baidu/infoflow-sdk-nodejs
95
-
96
- if node --input-type=module -e "import('@baidu/infoflow-sdk-nodejs').then(()=>process.exit(0)).catch(()=>process.exit(1))"; then
97
- echo " ✓ 运行时依赖校验通过:@baidu/infoflow-sdk-nodejs"
98
- else
99
- echo " ✗ @baidu/infoflow-sdk-nodejs 仍不可用(websocket 模式必须)。"
100
- echo " 请确认私有源、网络与鉴权后重试:"
101
- echo " npm_config_registry=$BAIDU_NPM_REGISTRY npm install --save-optional --registry $BAIDU_NPM_REGISTRY @baidu/infoflow-sdk-nodejs"
102
- exit 1
103
- fi
104
- fi
105
- else
106
- echo "==> 当前非 websocket 模式,跳过 @baidu/infoflow-sdk-nodejs 安装"
107
- fi
108
-
109
- echo "==> 构建插件(确保 build 完成)"
110
- cd "$PLUGIN_DIR"
111
-
112
- # 1) Prefer package.json scripts.build if present
113
- if node -e "const p=require('./package.json'); process.exit(p?.scripts?.build ? 0 : 1)"; then
114
- echo " - 检测到 scripts.build,执行 npm run build"
115
- npm run build
116
- else
117
- # 2) Fallback to tsc when tsconfig.json exists
118
- if [ -f "$PLUGIN_DIR/tsconfig.json" ]; then
119
- BUILD_TSCONFIG="tsconfig.json"
120
- if [ -f "$PLUGIN_DIR/tsconfig.build.json" ]; then
121
- BUILD_TSCONFIG="tsconfig.build.json"
122
- fi
123
- echo " - 未检测到 scripts.build,回退执行 TypeScript 编译(npx -p typescript tsc -p $BUILD_TSCONFIG)"
124
- npx -y -p typescript tsc -p "$BUILD_TSCONFIG"
125
- else
126
- echo " ✗ 未检测到 scripts.build 且不存在 tsconfig.json,无法确认已完成编译"
127
- exit 1
128
- fi
129
- fi
130
-
131
- # 3) Post-build sanity check: outDir defaults to dist, but honor tsconfig.json if present.
132
- OUT_DIR="dist"
133
- if [ -f "$PLUGIN_DIR/tsconfig.json" ]; then
134
- OUT_DIR_FROM_TSCONFIG="$(node -e "
135
- const fs = require('fs');
136
- const cfg = JSON.parse(fs.readFileSync('tsconfig.json','utf8'));
137
- const outDir = cfg?.compilerOptions?.outDir;
138
- if (typeof outDir === 'string' && outDir.trim()) process.stdout.write(outDir.trim());
139
- ")"
140
- if [ -n "$OUT_DIR_FROM_TSCONFIG" ]; then
141
- OUT_DIR="$OUT_DIR_FROM_TSCONFIG"
142
- fi
143
- fi
144
-
145
- if [ ! -d "$PLUGIN_DIR/$OUT_DIR" ]; then
146
- echo " ✗ 构建完成但未发现产物目录:$PLUGIN_DIR/$OUT_DIR"
147
- echo " 请检查 tsconfig.json 的 outDir 或 build 脚本是否输出到其它目录"
20
+ if [ ! -f "$COMMON_SCRIPT" ]; then
21
+ echo " 缺少公共部署脚本: $COMMON_SCRIPT"
148
22
  exit 1
149
23
  fi
150
- echo " ✓ 已检测到构建产物目录:$PLUGIN_DIR/$OUT_DIR"
151
-
152
- if [ ! -f "$CONFIG_FILE" ]; then
153
- echo " ✗ 未找到 $CONFIG_FILE,请先完成 OpenClaw 初始化后再部署"
154
- exit 1
155
- fi
156
-
157
- echo "==> 更新 OpenClaw 配置"
158
- node -e "
159
- const fs = require('fs');
160
- const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
161
- const id = '$PLUGIN_ID';
162
24
 
163
- // 1. plugins.entries: 确保插件已启用
164
- cfg.plugins = cfg.plugins ?? {};
165
- cfg.plugins.entries = cfg.plugins.entries ?? {};
166
- if (!cfg.plugins.entries[id]) {
167
- cfg.plugins.entries[id] = { enabled: true };
168
- console.log(' + 已添加 plugins.entries.' + id);
169
- } else if (!cfg.plugins.entries[id].enabled) {
170
- cfg.plugins.entries[id].enabled = true;
171
- console.log(' + 已启用 plugins.entries.' + id);
172
- } else {
173
- console.log(' ✓ plugins.entries.' + id + ' 已存在');
174
- }
175
-
176
- // 2. plugins.allow: 如果配置了白名单,确保插件在列表中
177
- if (Array.isArray(cfg.plugins.allow)) {
178
- if (!cfg.plugins.allow.includes(id)) {
179
- cfg.plugins.allow.push(id);
180
- console.log(' + 已添加到 plugins.allow');
181
- } else {
182
- console.log(' ✓ plugins.allow 已包含 ' + id);
183
- }
184
- } else {
185
- console.log(' - plugins.allow 未配置,跳过');
186
- }
187
-
188
- fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 2) + '\n');
189
- "
190
-
191
- echo "==> 检查 OpenClaw gateway 运行状态"
192
- GATEWAY_STATUS="$(openclaw gateway status 2>/dev/null || true)"
193
- DID_RESTART="false"
194
- if echo "$GATEWAY_STATUS" | rg -q "Runtime: running"; then
195
- echo "==> gateway 当前运行中,执行重启"
196
- openclaw gateway restart
197
- DID_RESTART="true"
198
- else
199
- echo "==> gateway 当前未运行,按要求跳过启动/重启"
200
- fi
201
-
202
- echo "==> 检查启动状态"
203
- sleep 2
204
- if [ "$DID_RESTART" = "true" ]; then
205
- # Restart 之后才做运行态确认,避免误匹配其它 openclaw 进程。
206
- if openclaw gateway status 2>/dev/null | rg -q "Runtime: running"; then
207
- echo "✓ gateway 重启完成"
208
- else
209
- echo "✗ gateway 重启后未处于 running 状态,请查看状态与日志:openclaw gateway status"
210
- exit 1
211
- fi
212
- else
213
- # 未重启/未启动时,仅输出当前状态
214
- openclaw gateway status 2>/dev/null || true
215
- fi
25
+ bash "$COMMON_SCRIPT" \
26
+ --plugin-dir "$PLUGIN_DIR" \
27
+ --plugin-id "$PLUGIN_ID" \
28
+ --config-file "$CONFIG_FILE" \
29
+ --baidu-registry "${BAIDU_NPM_REGISTRY:-${NPM_CONFIG_REGISTRY_BAIDU:-http://registry.npm.baidu-int.com}}"
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ PLUGIN_DIR=""
6
+ PLUGIN_ID="infoflow"
7
+ CONFIG_FILE="$HOME/.openclaw/openclaw.json"
8
+ BAIDU_NPM_REGISTRY="${BAIDU_NPM_REGISTRY:-${NPM_CONFIG_REGISTRY_BAIDU:-http://registry.npm.baidu-int.com}}"
9
+ DRY_RUN="false"
10
+
11
+ while [[ $# -gt 0 ]]; do
12
+ case "$1" in
13
+ --plugin-dir) PLUGIN_DIR="$2"; shift 2 ;;
14
+ --plugin-id) PLUGIN_ID="$2"; shift 2 ;;
15
+ --config-file) CONFIG_FILE="$2"; shift 2 ;;
16
+ --baidu-registry) BAIDU_NPM_REGISTRY="$2"; shift 2 ;;
17
+ --dry-run) DRY_RUN="true"; shift ;;
18
+ *) echo "Unknown argument: $1"; exit 1 ;;
19
+ esac
20
+ done
21
+
22
+ if [[ -z "$PLUGIN_DIR" ]]; then
23
+ echo "Missing --plugin-dir"
24
+ exit 1
25
+ fi
26
+
27
+ cd "$PLUGIN_DIR"
28
+
29
+ run_cmd() {
30
+ echo "$ $*"
31
+ if [[ "$DRY_RUN" == "true" ]]; then
32
+ return 0
33
+ fi
34
+ "$@"
35
+ }
36
+
37
+ echo "==> 链接 openclaw peer dependency(build 前确保可解析)"
38
+ if [ -n "${OPENCLAW_DIR:-}" ] && [ -d "${OPENCLAW_DIR:-}" ]; then
39
+ OPENCLAW_GLOBAL="$OPENCLAW_DIR"
40
+ elif command -v pnpm >/dev/null 2>&1; then
41
+ OPENCLAW_GLOBAL="$(pnpm root -g 2>/dev/null)/openclaw"
42
+ else
43
+ OPENCLAW_GLOBAL="$(npm root -g 2>/dev/null)/openclaw"
44
+ fi
45
+
46
+ if [ -d "${OPENCLAW_GLOBAL:-}" ]; then
47
+ run_cmd mkdir -p "$PLUGIN_DIR/node_modules"
48
+ run_cmd rm -rf "$PLUGIN_DIR/node_modules/openclaw"
49
+ run_cmd ln -s "$OPENCLAW_GLOBAL" "$PLUGIN_DIR/node_modules/openclaw"
50
+ echo " ✓ 已链接 $OPENCLAW_GLOBAL -> $PLUGIN_DIR/node_modules/openclaw"
51
+ else
52
+ echo " - 未检测到全局 openclaw,跳过链接"
53
+ fi
54
+
55
+ echo "==> 安装依赖"
56
+ run_cmd npm install --silent
57
+
58
+ WEBSOCKET_ENABLED="false"
59
+ if [ -f "$CONFIG_FILE" ]; then
60
+ WEBSOCKET_ENABLED="$(node -e "
61
+ const fs = require('fs');
62
+ try {
63
+ const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
64
+ const section = cfg?.channels?.$PLUGIN_ID ?? {};
65
+ const topMode = section?.connectionMode;
66
+ const accounts = section?.accounts && typeof section.accounts === 'object' ? Object.values(section.accounts) : [];
67
+ const accountWebsocket = accounts.some((acc) => acc && typeof acc === 'object' && acc.connectionMode === 'websocket');
68
+ const enabled = topMode === 'websocket' || accountWebsocket;
69
+ process.stdout.write(enabled ? 'true' : 'false');
70
+ } catch {
71
+ process.stdout.write('false');
72
+ }
73
+ ")"
74
+ fi
75
+
76
+ if [ "$WEBSOCKET_ENABLED" = "true" ]; then
77
+ echo "==> 检测到 websocket 模式,确保安装 @baidu/infoflow-sdk-nodejs"
78
+ if [[ "$DRY_RUN" == "true" ]]; then
79
+ echo " - dry-run 模式,跳过 websocket 依赖安装"
80
+ elif node --input-type=module -e "import('@baidu/infoflow-sdk-nodejs').then(()=>process.exit(0)).catch(()=>process.exit(1))"; then
81
+ echo " ✓ @baidu/infoflow-sdk-nodejs 已可用"
82
+ else
83
+ echo " - 使用私有源安装: $BAIDU_NPM_REGISTRY"
84
+ npm_config_registry="$BAIDU_NPM_REGISTRY" npm install --save-optional --registry "$BAIDU_NPM_REGISTRY" @baidu/infoflow-sdk-nodejs
85
+ if node --input-type=module -e "import('@baidu/infoflow-sdk-nodejs').then(()=>process.exit(0)).catch(()=>process.exit(1))"; then
86
+ echo " ✓ 运行时依赖校验通过:@baidu/infoflow-sdk-nodejs"
87
+ else
88
+ echo " ✗ @baidu/infoflow-sdk-nodejs 仍不可用(websocket 模式必须)"
89
+ exit 1
90
+ fi
91
+ fi
92
+ else
93
+ echo "==> 当前非 websocket 模式,跳过 @baidu/infoflow-sdk-nodejs 安装"
94
+ fi
95
+
96
+ echo "==> 构建插件(确保 build 完成)"
97
+ if [[ "$DRY_RUN" == "true" ]]; then
98
+ echo " - dry-run 模式,跳过实际构建"
99
+ else
100
+ if [ -d "$PLUGIN_DIR/dist" ] && [ ! -f "$PLUGIN_DIR/tsconfig.build.json" ] && [ ! -f "$PLUGIN_DIR/tsconfig.json" ]; then
101
+ echo " - 检测到预编译发布包(无 tsconfig),跳过构建"
102
+ elif node -e "const p=require('./package.json'); process.exit(p?.scripts?.build ? 0 : 1)"; then
103
+ if [ -f "$PLUGIN_DIR/tsconfig.build.json" ] || [ -f "$PLUGIN_DIR/tsconfig.json" ]; then
104
+ npm run build
105
+ elif [ -d "$PLUGIN_DIR/dist" ]; then
106
+ echo " - scripts.build 存在但缺少 tsconfig,使用已存在 dist 产物并跳过构建"
107
+ else
108
+ echo " ✗ scripts.build 存在但缺少 tsconfig 且无 dist 产物"
109
+ exit 1
110
+ fi
111
+ elif [ -f "$PLUGIN_DIR/tsconfig.build.json" ]; then
112
+ npx -y -p typescript tsc -p "$PLUGIN_DIR/tsconfig.build.json"
113
+ elif [ -f "$PLUGIN_DIR/tsconfig.json" ]; then
114
+ npx -y -p typescript tsc -p "$PLUGIN_DIR/tsconfig.json"
115
+ elif [ -d "$PLUGIN_DIR/dist" ]; then
116
+ echo " - 未检测到构建配置,使用已存在 dist 产物并跳过构建"
117
+ else
118
+ echo " ✗ 未检测到 scripts.build 或 tsconfig,无法构建"
119
+ exit 1
120
+ fi
121
+ fi
122
+
123
+ if [ ! -d "$PLUGIN_DIR/dist" ] && [[ "$DRY_RUN" != "true" ]]; then
124
+ echo " ✗ 构建完成但未发现产物目录:$PLUGIN_DIR/dist"
125
+ exit 1
126
+ fi
127
+ echo " ✓ 已检测到构建产物目录:$PLUGIN_DIR/dist"
128
+
129
+ if [ ! -f "$CONFIG_FILE" ]; then
130
+ echo " ✗ 未找到 $CONFIG_FILE,请先完成 OpenClaw 初始化后再部署"
131
+ exit 1
132
+ fi
133
+
134
+ if [[ "$DRY_RUN" == "true" ]]; then
135
+ echo "==> dry-run 模式,跳过配置写入与 gateway 操作"
136
+ exit 0
137
+ fi
138
+
139
+ echo "==> 更新 OpenClaw 配置"
140
+ node -e "
141
+ const fs = require('fs');
142
+ const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
143
+ const id = '$PLUGIN_ID';
144
+
145
+ cfg.plugins = cfg.plugins ?? {};
146
+ cfg.plugins.entries = cfg.plugins.entries ?? {};
147
+ if (!cfg.plugins.entries[id]) cfg.plugins.entries[id] = { enabled: true };
148
+ else if (!cfg.plugins.entries[id].enabled) cfg.plugins.entries[id].enabled = true;
149
+
150
+ if (Array.isArray(cfg.plugins.allow) && !cfg.plugins.allow.includes(id)) cfg.plugins.allow.push(id);
151
+ fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 2) + '\n');
152
+ "
153
+
154
+ echo "==> 检查 OpenClaw gateway 运行状态"
155
+ GATEWAY_STATUS="$(openclaw gateway status 2>/dev/null || true)"
156
+ if echo "$GATEWAY_STATUS" | rg -q "Runtime: running"; then
157
+ echo "==> gateway 当前运行中,执行重启"
158
+ openclaw gateway restart
159
+ sleep 2
160
+ if openclaw gateway status 2>/dev/null | rg -q "Runtime: running"; then
161
+ echo "✓ gateway 重启完成"
162
+ else
163
+ echo "✗ gateway 重启后未处于 running 状态,请查看 openclaw gateway status"
164
+ exit 1
165
+ fi
166
+ else
167
+ echo "==> gateway 当前未运行,按要求跳过启动/重启"
168
+ fi
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(git add:*)",
5
- "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat: add assistant watchMentions, followUp, replyMode and actions support\n\n- Add watchMentions config for assistant-style replies when watched users are @mentioned\n- Add @mention detection and resolution in group messages \\(user and agent\\)\n- Add followUp window for automatic follow-up replies after bot interaction\n- Add 5 replyMode options: ignore, record, mention-only, mention-and-watch, proactive\n- Add ChannelMessageActionAdapter for LLM-initiated message sends\n- Refactor logging: unified logVerbose\\(\\) and formatInfoflowError\\(\\)\n- Update openclaw.plugin.json config schema with new group config format \\(breaking change\\)\n- Add CHANGELOG.md\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>\nEOF\n\\)\")",
6
- "Bash(npm publish:*)"
7
- ]
8
- }
9
- }