@colinlu50/openclaw-lark-stream 2026.3.30 → 2026.3.31

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.
Files changed (2) hide show
  1. package/bin/openclaw-lark.js +132 -76
  2. package/package.json +1 -1
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { execFileSync, execSync } from "node:child_process";
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 { dirname, join } from "node:path";
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,155 @@ 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
- // Step 1: Clean existing state so tools doesn't choke on stale plugins
24
- cleanPluginState();
20
+ await runInstall();
21
+ process.exit(0);
22
+ }
25
23
 
26
- // Step 2: Run tools for interactive setup (bot config, version check, etc.)
27
- const toolsArgs = args.slice();
28
- try {
29
- runTools(toolsArgs);
30
- } catch {
31
- // Tools may fail on gateway restart / interactive prompt — that's OK,
32
- // bot config is already saved to openclaw.json at this point.
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);
34
28
 
35
- // Step 3: Clean official plugin + any staging leftovers, install ours
29
+ // ---------------------------------------------------------------------------
30
+ // Install flow
31
+ // ---------------------------------------------------------------------------
32
+
33
+ async function runInstall() {
34
+ // 1. Version check
35
+ checkOpenClawVersion();
36
+
37
+ // 2. Clean stale state
36
38
  cleanPluginState();
39
+
40
+ // 3. Install our plugin
41
+ console.log(`\nInstalling ${SELF_PACKAGE}...`);
37
42
  try {
38
- console.log(`\nInstalling ${SELF_PACKAGE}...`);
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("You can retry with: openclaw plugins install " + SELF_PACKAGE);
47
+ console.error(`\nYou can retry with: openclaw plugins install ${SELF_PACKAGE}`);
48
48
  process.exit(error.status ?? 1);
49
49
  }
50
- process.exit(0);
50
+ console.log(`\n✅ Plugin installed successfully.`);
51
+
52
+ // 4. Bot configuration (interactive)
53
+ await configureBotIfNeeded();
54
+
55
+ // 5. Restart gateway
56
+ console.log("\nRestarting gateway...");
57
+ try {
58
+ execSync("openclaw gateway restart", { stdio: "inherit" });
59
+ } catch {
60
+ console.log("Gateway restart failed. You can manually run: openclaw gateway restart");
61
+ }
62
+
63
+ console.log("\n🎉 All done!");
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Version check
68
+ // ---------------------------------------------------------------------------
69
+
70
+ function checkOpenClawVersion() {
71
+ try {
72
+ const ver = execSync("openclaw -v", { encoding: "utf8" }).trim();
73
+ console.log(`OpenClaw version: ${ver}`);
74
+ } catch {
75
+ console.error("❌ OpenClaw not found. Install it first: npm install -g openclaw");
76
+ process.exit(1);
77
+ }
51
78
  }
52
79
 
53
- // ── All other commands: delegate to @larksuite/openclaw-lark-tools ──
54
- try {
55
- runTools(args);
56
- } catch (error) {
57
- process.exit(error.status ?? 1);
80
+ // ---------------------------------------------------------------------------
81
+ // Bot configuration
82
+ // ---------------------------------------------------------------------------
83
+
84
+ async function configureBotIfNeeded() {
85
+ const cfg = readConfig();
86
+ const existing = cfg.channels?.feishu;
87
+
88
+ if (existing?.appId) {
89
+ console.log(`\nFound existing bot config (App ID: ${existing.appId}).`);
90
+ const reuse = await ask("Use existing bot config? (Y/n): ");
91
+ if (reuse.toLowerCase() !== "n") {
92
+ console.log("Keeping existing config.");
93
+ return;
94
+ }
95
+ }
96
+
97
+ console.log("\n── Feishu Bot Setup ──");
98
+ console.log("You need a Feishu bot app. Create one at: https://open.feishu.cn/app\n");
99
+
100
+ const appId = await ask("App ID: ");
101
+ const appSecret = await ask("App Secret: ");
102
+
103
+ if (!appId || !appSecret) {
104
+ console.log("Skipped. You can configure manually in ~/.openclaw/openclaw.json");
105
+ return;
106
+ }
107
+
108
+ // Ask for domain
109
+ const domainChoice = await ask("Domain - feishu or lark? (feishu): ");
110
+ const domain = domainChoice === "lark" ? "lark" : "feishu";
111
+
112
+ // Write config
113
+ if (!cfg.channels) cfg.channels = {};
114
+ cfg.channels.feishu = {
115
+ ...(cfg.channels.feishu || {}),
116
+ enabled: true,
117
+ appId,
118
+ appSecret,
119
+ connectionMode: "websocket",
120
+ domain,
121
+ streaming: true,
122
+ defaultAccount: "main",
123
+ replyMode: {
124
+ direct: "streaming",
125
+ group: "streaming",
126
+ default: "streaming",
127
+ },
128
+ accounts: {
129
+ ...(cfg.channels?.feishu?.accounts || {}),
130
+ main: { appId, appSecret },
131
+ },
132
+ dmPolicy: cfg.channels?.feishu?.dmPolicy || "pairing",
133
+ groupPolicy: cfg.channels?.feishu?.groupPolicy || "open",
134
+ };
135
+
136
+ writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n", "utf8");
137
+ console.log(`\n✅ Bot configured (App ID: ${appId}).`);
58
138
  }
59
139
 
60
140
  // ---------------------------------------------------------------------------
61
141
  // Helpers
62
142
  // ---------------------------------------------------------------------------
63
143
 
144
+ function ask(prompt) {
145
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
146
+ return new Promise((resolve) => {
147
+ rl.question(prompt, (answer) => {
148
+ rl.close();
149
+ resolve(answer.trim());
150
+ });
151
+ });
152
+ }
153
+
154
+ function readConfig() {
155
+ if (!existsSync(CONFIG_FILE)) return {};
156
+ try {
157
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
158
+ } catch {
159
+ return {};
160
+ }
161
+ }
162
+
64
163
  /**
65
164
  * Remove all plugin directories, staging leftovers, and stale config
66
165
  * references so that openclaw has a clean state for the next install.
67
166
  */
68
167
  function cleanPluginState() {
69
- // Remove plugin directories
70
168
  for (const dir of [OFFICIAL_DIR, SELF_DIR]) {
71
169
  if (existsSync(dir)) {
72
170
  console.log(`Removing ${dir}...`);
@@ -83,17 +181,12 @@ function cleanPluginState() {
83
181
  rmSync(p, { recursive: true, force: true });
84
182
  }
85
183
  }
86
- } catch { /* ignore readdir errors */ }
184
+ } catch { /* ignore */ }
87
185
  }
88
- // Clean config references for both plugin IDs
89
186
  cleanConfigReferences("openclaw-lark");
90
187
  cleanConfigReferences("openclaw-lark-stream");
91
188
  }
92
189
 
93
- /**
94
- * Remove stale plugin references from openclaw.json so that
95
- * `openclaw plugins install` doesn't fail config validation.
96
- */
97
190
  function cleanConfigReferences(pluginId) {
98
191
  if (!existsSync(CONFIG_FILE)) return;
99
192
  try {
@@ -116,44 +209,7 @@ function cleanConfigReferences(pluginId) {
116
209
  }
117
210
  if (changed) {
118
211
  writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n", "utf8");
119
- console.log(`Cleaned "${pluginId}" references from ${CONFIG_FILE}`);
212
+ console.log(`Cleaned "${pluginId}" references from config.`);
120
213
  }
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
- }
214
+ } catch { /* ignore */ }
159
215
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colinlu50/openclaw-lark-stream",
3
- "version": "2026.3.30",
3
+ "version": "2026.3.31",
4
4
  "description": "OpenClaw Lark/Feishu channel plugin",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",