@colinlu50/openclaw-lark-stream 2026.3.30 → 2026.323.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/bin/openclaw-lark.js +149 -76
- package/package.json +1 -1
package/bin/openclaw-lark.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { createInterface } from "node:readline";
|
|
4
5
|
import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
-
import {
|
|
6
|
+
import { join } from "node:path";
|
|
6
7
|
|
|
7
8
|
const SELF_PACKAGE = "@colinlu50/openclaw-lark-stream";
|
|
8
9
|
const STATE_DIR = process.env.OPENCLAW_STATE_DIR || join(process.env.HOME || process.env.USERPROFILE || "", ".openclaw");
|
|
9
10
|
const EXTENSIONS_DIR = join(STATE_DIR, "extensions");
|
|
10
11
|
const CONFIG_FILE = join(STATE_DIR, "openclaw.json");
|
|
11
|
-
// Tools installs official plugin as "openclaw-lark"; our manifest uses "openclaw-lark-stream"
|
|
12
12
|
const OFFICIAL_DIR = join(EXTENSIONS_DIR, "openclaw-lark");
|
|
13
13
|
const SELF_DIR = join(EXTENSIONS_DIR, "openclaw-lark-stream");
|
|
14
14
|
|
|
@@ -16,57 +16,172 @@ const args = process.argv.slice(2);
|
|
|
16
16
|
const subcommand = args[0];
|
|
17
17
|
|
|
18
18
|
// ── install / update ──
|
|
19
|
-
// 1) Clean existing plugin state so tools gets a fresh environment
|
|
20
|
-
// 2) Let @larksuite/openclaw-lark-tools run the interactive setup (bot config)
|
|
21
|
-
// 3) Clean again (tools installs official code), then install our fork
|
|
22
19
|
if (subcommand === "install" || subcommand === "update") {
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
await runInstall();
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
// ── All other commands: show help ──
|
|
25
|
+
console.log(`Usage: npx ${SELF_PACKAGE} install`);
|
|
26
|
+
console.log(` npx ${SELF_PACKAGE} update`);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Install flow
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
async function runInstall() {
|
|
34
|
+
// 1. Version check
|
|
35
|
+
checkOpenClawVersion();
|
|
34
36
|
|
|
35
|
-
//
|
|
37
|
+
// 2. Clean stale state
|
|
36
38
|
cleanPluginState();
|
|
39
|
+
|
|
40
|
+
// 3. Install our plugin
|
|
41
|
+
console.log(`\nInstalling ${SELF_PACKAGE}...`);
|
|
37
42
|
try {
|
|
38
|
-
|
|
39
|
-
execSync(`openclaw plugins install ${SELF_PACKAGE}`, {
|
|
40
|
-
stdio: "inherit",
|
|
41
|
-
});
|
|
42
|
-
console.log(`\n✅ ${SELF_PACKAGE} installed successfully.`);
|
|
43
|
-
console.log("Run: openclaw gateway restart");
|
|
43
|
+
execSync(`openclaw plugins install ${SELF_PACKAGE}`, { stdio: "inherit" });
|
|
44
44
|
} catch (error) {
|
|
45
45
|
console.error(`\n❌ Failed to install ${SELF_PACKAGE}.`);
|
|
46
46
|
console.error(error.message || error);
|
|
47
|
-
console.error(
|
|
47
|
+
console.error(`\nYou can retry with: openclaw plugins install ${SELF_PACKAGE}`);
|
|
48
48
|
process.exit(error.status ?? 1);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
console.log(`\n✅ Plugin installed successfully.`);
|
|
51
|
+
|
|
52
|
+
// 4. Ensure plugins.allow includes our plugin ID
|
|
53
|
+
ensurePluginAllowed("openclaw-lark-stream");
|
|
54
|
+
|
|
55
|
+
// 5. Bot configuration (interactive)
|
|
56
|
+
await configureBotIfNeeded();
|
|
57
|
+
|
|
58
|
+
// 6. Restart gateway
|
|
59
|
+
console.log("\nRestarting gateway...");
|
|
60
|
+
try {
|
|
61
|
+
execSync("openclaw gateway restart", { stdio: "inherit" });
|
|
62
|
+
} catch {
|
|
63
|
+
console.log("Gateway restart failed. You can manually run: openclaw gateway restart");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log("\n🎉 All done!");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Version check
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
function checkOpenClawVersion() {
|
|
74
|
+
try {
|
|
75
|
+
const ver = execSync("openclaw -v", { encoding: "utf8" }).trim();
|
|
76
|
+
console.log(`OpenClaw version: ${ver}`);
|
|
77
|
+
} catch {
|
|
78
|
+
console.error("❌ OpenClaw not found. Install it first: npm install -g openclaw");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
51
81
|
}
|
|
52
82
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Bot configuration
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
async function configureBotIfNeeded() {
|
|
88
|
+
const cfg = readConfig();
|
|
89
|
+
const existing = cfg.channels?.feishu;
|
|
90
|
+
|
|
91
|
+
if (existing?.appId) {
|
|
92
|
+
console.log(`\nFound existing bot config (App ID: ${existing.appId}).`);
|
|
93
|
+
const reuse = await ask("Use existing bot config? (Y/n): ");
|
|
94
|
+
if (reuse.toLowerCase() !== "n") {
|
|
95
|
+
console.log("Keeping existing config.");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log("\n── Feishu Bot Setup ──");
|
|
101
|
+
console.log("You need a Feishu bot app. Create one at: https://open.feishu.cn/app\n");
|
|
102
|
+
|
|
103
|
+
const appId = await ask("App ID: ");
|
|
104
|
+
const appSecret = await ask("App Secret: ");
|
|
105
|
+
|
|
106
|
+
if (!appId || !appSecret) {
|
|
107
|
+
console.log("Skipped. You can configure manually in ~/.openclaw/openclaw.json");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Ask for domain
|
|
112
|
+
const domainChoice = await ask("Domain - feishu or lark? (feishu): ");
|
|
113
|
+
const domain = domainChoice === "lark" ? "lark" : "feishu";
|
|
114
|
+
|
|
115
|
+
// Write config
|
|
116
|
+
if (!cfg.channels) cfg.channels = {};
|
|
117
|
+
cfg.channels.feishu = {
|
|
118
|
+
...(cfg.channels.feishu || {}),
|
|
119
|
+
enabled: true,
|
|
120
|
+
appId,
|
|
121
|
+
appSecret,
|
|
122
|
+
connectionMode: "websocket",
|
|
123
|
+
domain,
|
|
124
|
+
streaming: true,
|
|
125
|
+
defaultAccount: "main",
|
|
126
|
+
replyMode: {
|
|
127
|
+
direct: "streaming",
|
|
128
|
+
group: "streaming",
|
|
129
|
+
default: "streaming",
|
|
130
|
+
},
|
|
131
|
+
accounts: {
|
|
132
|
+
...(cfg.channels?.feishu?.accounts || {}),
|
|
133
|
+
main: { appId, appSecret },
|
|
134
|
+
},
|
|
135
|
+
dmPolicy: cfg.channels?.feishu?.dmPolicy || "pairing",
|
|
136
|
+
groupPolicy: cfg.channels?.feishu?.groupPolicy || "open",
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
140
|
+
console.log(`\n✅ Bot configured (App ID: ${appId}).`);
|
|
58
141
|
}
|
|
59
142
|
|
|
60
143
|
// ---------------------------------------------------------------------------
|
|
61
144
|
// Helpers
|
|
62
145
|
// ---------------------------------------------------------------------------
|
|
63
146
|
|
|
147
|
+
function ask(prompt) {
|
|
148
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
rl.question(prompt, (answer) => {
|
|
151
|
+
rl.close();
|
|
152
|
+
resolve(answer.trim());
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readConfig() {
|
|
158
|
+
if (!existsSync(CONFIG_FILE)) return {};
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
|
|
161
|
+
} catch {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
64
166
|
/**
|
|
65
167
|
* Remove all plugin directories, staging leftovers, and stale config
|
|
66
168
|
* references so that openclaw has a clean state for the next install.
|
|
67
169
|
*/
|
|
170
|
+
/**
|
|
171
|
+
* Ensure the plugin ID is in plugins.allow so openclaw doesn't warn.
|
|
172
|
+
*/
|
|
173
|
+
function ensurePluginAllowed(pluginId) {
|
|
174
|
+
const cfg = readConfig();
|
|
175
|
+
if (!cfg.plugins) cfg.plugins = {};
|
|
176
|
+
if (!Array.isArray(cfg.plugins.allow)) cfg.plugins.allow = [];
|
|
177
|
+
if (!cfg.plugins.allow.includes(pluginId)) {
|
|
178
|
+
cfg.plugins.allow.push(pluginId);
|
|
179
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
180
|
+
console.log(`Added "${pluginId}" to plugins.allow.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
68
184
|
function cleanPluginState() {
|
|
69
|
-
// Remove plugin directories
|
|
70
185
|
for (const dir of [OFFICIAL_DIR, SELF_DIR]) {
|
|
71
186
|
if (existsSync(dir)) {
|
|
72
187
|
console.log(`Removing ${dir}...`);
|
|
@@ -83,17 +198,12 @@ function cleanPluginState() {
|
|
|
83
198
|
rmSync(p, { recursive: true, force: true });
|
|
84
199
|
}
|
|
85
200
|
}
|
|
86
|
-
} catch { /* ignore
|
|
201
|
+
} catch { /* ignore */ }
|
|
87
202
|
}
|
|
88
|
-
// Clean config references for both plugin IDs
|
|
89
203
|
cleanConfigReferences("openclaw-lark");
|
|
90
204
|
cleanConfigReferences("openclaw-lark-stream");
|
|
91
205
|
}
|
|
92
206
|
|
|
93
|
-
/**
|
|
94
|
-
* Remove stale plugin references from openclaw.json so that
|
|
95
|
-
* `openclaw plugins install` doesn't fail config validation.
|
|
96
|
-
*/
|
|
97
207
|
function cleanConfigReferences(pluginId) {
|
|
98
208
|
if (!existsSync(CONFIG_FILE)) return;
|
|
99
209
|
try {
|
|
@@ -116,44 +226,7 @@ function cleanConfigReferences(pluginId) {
|
|
|
116
226
|
}
|
|
117
227
|
if (changed) {
|
|
118
228
|
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
119
|
-
console.log(`Cleaned "${pluginId}" references from
|
|
229
|
+
console.log(`Cleaned "${pluginId}" references from config.`);
|
|
120
230
|
}
|
|
121
|
-
} catch {
|
|
122
|
-
// Config parse failure — let openclaw handle it
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function runTools(fwdArgs) {
|
|
127
|
-
let version = "latest";
|
|
128
|
-
const vIdx = fwdArgs.indexOf("--tools-version");
|
|
129
|
-
if (vIdx !== -1) {
|
|
130
|
-
version = fwdArgs[vIdx + 1];
|
|
131
|
-
fwdArgs.splice(vIdx, 2);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const allArgs = ["--yes", `@larksuite/openclaw-lark-tools@${version}`, ...fwdArgs];
|
|
135
|
-
|
|
136
|
-
if (process.platform === "win32") {
|
|
137
|
-
const npxCli = join(
|
|
138
|
-
dirname(process.execPath),
|
|
139
|
-
"node_modules",
|
|
140
|
-
"npm",
|
|
141
|
-
"bin",
|
|
142
|
-
"npx-cli.js",
|
|
143
|
-
);
|
|
144
|
-
execFileSync(process.execPath, [npxCli, ...allArgs], {
|
|
145
|
-
stdio: "inherit",
|
|
146
|
-
env: {
|
|
147
|
-
...process.env,
|
|
148
|
-
NODE_OPTIONS: [
|
|
149
|
-
process.env.NODE_OPTIONS,
|
|
150
|
-
"--disable-warning=DEP0190",
|
|
151
|
-
]
|
|
152
|
-
.filter(Boolean)
|
|
153
|
-
.join(" "),
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
} else {
|
|
157
|
-
execFileSync("npx", allArgs, { stdio: "inherit" });
|
|
158
|
-
}
|
|
231
|
+
} catch { /* ignore */ }
|
|
159
232
|
}
|