@chbo297/infoflow 2026.5.4 → 2026.5.5
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 +49 -1
- package/dist/src/cli.js +157 -0
- package/package.json +13 -3
- package/scripts/deploy.sh +10 -196
- package/scripts/lib/deploy-common.sh +157 -0
- package/.claude/settings.local.json +0 -9
- package/CHANGELOG.md +0 -147
- package/index.ts +0 -23
- package/src/accounts.ts +0 -170
- package/src/actions.ts +0 -458
- package/src/bot.ts +0 -1235
- package/src/channel.ts +0 -443
- package/src/infoflow-req-parse.ts +0 -488
- package/src/infoflow-sdk.d.ts +0 -12
- package/src/logging.ts +0 -123
- package/src/markdown-local-images.ts +0 -75
- package/src/media.ts +0 -405
- package/src/monitor.ts +0 -188
- package/src/reply-dispatcher.ts +0 -364
- package/src/runtime.ts +0 -14
- package/src/send.ts +0 -986
- package/src/sent-message-store.ts +0 -267
- package/src/targets.ts +0 -109
- package/src/types.ts +0 -242
- package/src/ws-receiver.ts +0 -482
- package/tsconfig.build.json +0 -6
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
|
+
```
|
package/dist/src/cli.js
ADDED
|
@@ -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.
|
|
3
|
+
"version": "2026.5.5",
|
|
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.
|
|
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 -
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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,157 @@
|
|
|
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 node -e "const p=require('./package.json'); process.exit(p?.scripts?.build ? 0 : 1)"; then
|
|
101
|
+
npm run build
|
|
102
|
+
elif [ -f "$PLUGIN_DIR/tsconfig.build.json" ]; then
|
|
103
|
+
npx -y -p typescript tsc -p "$PLUGIN_DIR/tsconfig.build.json"
|
|
104
|
+
elif [ -f "$PLUGIN_DIR/tsconfig.json" ]; then
|
|
105
|
+
npx -y -p typescript tsc -p "$PLUGIN_DIR/tsconfig.json"
|
|
106
|
+
else
|
|
107
|
+
echo " ✗ 未检测到 scripts.build 或 tsconfig,无法构建"
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if [ ! -d "$PLUGIN_DIR/dist" ] && [[ "$DRY_RUN" != "true" ]]; then
|
|
113
|
+
echo " ✗ 构建完成但未发现产物目录:$PLUGIN_DIR/dist"
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
echo " ✓ 已检测到构建产物目录:$PLUGIN_DIR/dist"
|
|
117
|
+
|
|
118
|
+
if [ ! -f "$CONFIG_FILE" ]; then
|
|
119
|
+
echo " ✗ 未找到 $CONFIG_FILE,请先完成 OpenClaw 初始化后再部署"
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
124
|
+
echo "==> dry-run 模式,跳过配置写入与 gateway 操作"
|
|
125
|
+
exit 0
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
echo "==> 更新 OpenClaw 配置"
|
|
129
|
+
node -e "
|
|
130
|
+
const fs = require('fs');
|
|
131
|
+
const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
|
|
132
|
+
const id = '$PLUGIN_ID';
|
|
133
|
+
|
|
134
|
+
cfg.plugins = cfg.plugins ?? {};
|
|
135
|
+
cfg.plugins.entries = cfg.plugins.entries ?? {};
|
|
136
|
+
if (!cfg.plugins.entries[id]) cfg.plugins.entries[id] = { enabled: true };
|
|
137
|
+
else if (!cfg.plugins.entries[id].enabled) cfg.plugins.entries[id].enabled = true;
|
|
138
|
+
|
|
139
|
+
if (Array.isArray(cfg.plugins.allow) && !cfg.plugins.allow.includes(id)) cfg.plugins.allow.push(id);
|
|
140
|
+
fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 2) + '\n');
|
|
141
|
+
"
|
|
142
|
+
|
|
143
|
+
echo "==> 检查 OpenClaw gateway 运行状态"
|
|
144
|
+
GATEWAY_STATUS="$(openclaw gateway status 2>/dev/null || true)"
|
|
145
|
+
if echo "$GATEWAY_STATUS" | rg -q "Runtime: running"; then
|
|
146
|
+
echo "==> gateway 当前运行中,执行重启"
|
|
147
|
+
openclaw gateway restart
|
|
148
|
+
sleep 2
|
|
149
|
+
if openclaw gateway status 2>/dev/null | rg -q "Runtime: running"; then
|
|
150
|
+
echo "✓ gateway 重启完成"
|
|
151
|
+
else
|
|
152
|
+
echo "✗ gateway 重启后未处于 running 状态,请查看 openclaw gateway status"
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
155
|
+
else
|
|
156
|
+
echo "==> gateway 当前未运行,按要求跳过启动/重启"
|
|
157
|
+
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
|
-
}
|