@long_88/openclaw-aicc-channel-cli 0.1.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 +34 -0
- package/cli.mjs +237 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @long_88/openclaw-aicc-channel-cli
|
|
2
|
+
|
|
3
|
+
CLI installer for the `@long_88/openclaw-aicc-channel` plugin.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx -y @long_88/openclaw-aicc-channel-cli install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Configure a bridge account while installing:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx -y @long_88/openclaw-aicc-channel-cli install \
|
|
15
|
+
--bridge-url http://127.0.0.1:9090 \
|
|
16
|
+
--secret bridge-secret \
|
|
17
|
+
--name "Default Bridge"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Configure a named account:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx -y @long_88/openclaw-aicc-channel-cli install \
|
|
24
|
+
--account cn \
|
|
25
|
+
--bridge-url http://127.0.0.1:9091 \
|
|
26
|
+
--secret bridge-secret-cn \
|
|
27
|
+
--name "China Bridge"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Preview commands without executing them:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx -y @long_88/openclaw-aicc-channel-cli install --dry-run
|
|
34
|
+
```
|
package/cli.mjs
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
const CLI_SPEC = "@long_88/openclaw-aicc-channel-cli";
|
|
6
|
+
const PLUGIN_SPEC = "@long_88/openclaw-aicc-channel";
|
|
7
|
+
const CHANNEL_ID = "aicc-channel";
|
|
8
|
+
|
|
9
|
+
function formatValue(value) {
|
|
10
|
+
if (/^[A-Za-z0-9_]+$/.test(value)) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return JSON.stringify(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function formatCommand(args) {
|
|
18
|
+
if (args.length === 0) {
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (args.length === 1) {
|
|
23
|
+
return formatValue(args[0]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return `${args.slice(0, -1).join(" ")} ${formatValue(args.at(-1))}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function log(message) {
|
|
30
|
+
console.log(`[aicc-channel] ${message}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function fail(message) {
|
|
34
|
+
console.error(`[aicc-channel] ${message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseArgs(argv) {
|
|
39
|
+
const options = {
|
|
40
|
+
account: null,
|
|
41
|
+
bridgeUrl: null,
|
|
42
|
+
dryRun: false,
|
|
43
|
+
name: null,
|
|
44
|
+
restart: true,
|
|
45
|
+
secret: null
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
49
|
+
const arg = argv[index];
|
|
50
|
+
|
|
51
|
+
switch (arg) {
|
|
52
|
+
case "--account":
|
|
53
|
+
options.account = argv[index + 1] ?? null;
|
|
54
|
+
index += 1;
|
|
55
|
+
break;
|
|
56
|
+
case "--bridge-url":
|
|
57
|
+
options.bridgeUrl = argv[index + 1] ?? null;
|
|
58
|
+
index += 1;
|
|
59
|
+
break;
|
|
60
|
+
case "--dry-run":
|
|
61
|
+
options.dryRun = true;
|
|
62
|
+
break;
|
|
63
|
+
case "--name":
|
|
64
|
+
options.name = argv[index + 1] ?? null;
|
|
65
|
+
index += 1;
|
|
66
|
+
break;
|
|
67
|
+
case "--no-restart":
|
|
68
|
+
options.restart = false;
|
|
69
|
+
break;
|
|
70
|
+
case "--secret":
|
|
71
|
+
options.secret = argv[index + 1] ?? null;
|
|
72
|
+
index += 1;
|
|
73
|
+
break;
|
|
74
|
+
default:
|
|
75
|
+
fail(`unknown option: ${arg}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return options;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function channelConfigPath(options) {
|
|
83
|
+
if (!options.account) {
|
|
84
|
+
return `channels.${CHANNEL_ID}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return `channels.${CHANNEL_ID}.accounts.${options.account}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function buildCommands(options) {
|
|
91
|
+
const commands = [
|
|
92
|
+
["openclaw", "plugins", "install", PLUGIN_SPEC],
|
|
93
|
+
["openclaw", "config", "set", `plugins.entries.${CHANNEL_ID}.enabled`, "true"]
|
|
94
|
+
];
|
|
95
|
+
const configBase = channelConfigPath(options);
|
|
96
|
+
const shouldWriteConfig =
|
|
97
|
+
options.account !== null ||
|
|
98
|
+
options.bridgeUrl !== null ||
|
|
99
|
+
options.secret !== null ||
|
|
100
|
+
options.name !== null;
|
|
101
|
+
|
|
102
|
+
if (options.bridgeUrl) {
|
|
103
|
+
commands.push(["openclaw", "config", "set", `${configBase}.bridgeUrl`, options.bridgeUrl]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (options.secret) {
|
|
107
|
+
commands.push(["openclaw", "config", "set", `${configBase}.secret`, options.secret]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (options.name) {
|
|
111
|
+
commands.push(["openclaw", "config", "set", `${configBase}.name`, options.name]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (shouldWriteConfig) {
|
|
115
|
+
commands.push(["openclaw", "config", "set", `${configBase}.enabled`, "true"]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (options.restart) {
|
|
119
|
+
commands.push(["openclaw", "gateway", "restart"]);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return commands;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function renderHelp() {
|
|
126
|
+
return `
|
|
127
|
+
Usage:
|
|
128
|
+
npx -y ${CLI_SPEC} install [options]
|
|
129
|
+
npx -y ${CLI_SPEC} help
|
|
130
|
+
|
|
131
|
+
Options:
|
|
132
|
+
--bridge-url <url> Set channels.aicc-channel(.accounts.<id>).bridgeUrl
|
|
133
|
+
--secret <value> Set channels.aicc-channel(.accounts.<id>).secret
|
|
134
|
+
--name <value> Set channels.aicc-channel(.accounts.<id>).name
|
|
135
|
+
--account <id> Write config under channels.aicc-channel.accounts.<id>
|
|
136
|
+
--no-restart Skip openclaw gateway restart
|
|
137
|
+
--dry-run Print commands without executing them
|
|
138
|
+
`.trim();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function runAndCapture(args) {
|
|
142
|
+
return spawnSync(args[0], args.slice(1), {
|
|
143
|
+
encoding: "utf8"
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function ensureOpenclawAvailable() {
|
|
148
|
+
const result = runAndCapture(["openclaw", "--version"]);
|
|
149
|
+
|
|
150
|
+
if (result.error) {
|
|
151
|
+
fail("openclaw CLI was not found. Install OpenClaw first.");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (result.status !== 0) {
|
|
155
|
+
fail(result.stderr.trim() || "unable to read openclaw version");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const version = result.stdout.trim();
|
|
159
|
+
if (version) {
|
|
160
|
+
log(`detected ${version}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function runCommand(args) {
|
|
165
|
+
log(`running: ${formatCommand(args)}`);
|
|
166
|
+
const result = runAndCapture(args);
|
|
167
|
+
|
|
168
|
+
if (result.status === 0) {
|
|
169
|
+
if (result.stdout.trim()) {
|
|
170
|
+
process.stdout.write(result.stdout);
|
|
171
|
+
if (!result.stdout.endsWith("\n")) {
|
|
172
|
+
process.stdout.write("\n");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (result.stderr.trim()) {
|
|
179
|
+
process.stderr.write(result.stderr);
|
|
180
|
+
if (!result.stderr.endsWith("\n")) {
|
|
181
|
+
process.stderr.write("\n");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function install(options) {
|
|
189
|
+
const commands = buildCommands(options);
|
|
190
|
+
|
|
191
|
+
if (options.dryRun) {
|
|
192
|
+
for (const command of commands) {
|
|
193
|
+
console.log(formatCommand(command));
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
ensureOpenclawAvailable();
|
|
199
|
+
|
|
200
|
+
const installResult = runCommand(commands[0]);
|
|
201
|
+
if (installResult.status !== 0) {
|
|
202
|
+
const combinedOutput = `${installResult.stdout ?? ""}\n${installResult.stderr ?? ""}`;
|
|
203
|
+
|
|
204
|
+
if (combinedOutput.includes("already exists")) {
|
|
205
|
+
const updateResult = runCommand(["openclaw", "plugins", "update", CHANNEL_ID]);
|
|
206
|
+
if (updateResult.status !== 0) {
|
|
207
|
+
fail("plugin update failed");
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
fail("plugin install failed");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const command of commands.slice(1)) {
|
|
215
|
+
const result = runCommand(command);
|
|
216
|
+
if (result.status !== 0) {
|
|
217
|
+
fail(`command failed: ${formatCommand(command)}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const command = process.argv[2];
|
|
223
|
+
|
|
224
|
+
switch (command) {
|
|
225
|
+
case "help":
|
|
226
|
+
case "--help":
|
|
227
|
+
case "-h":
|
|
228
|
+
case undefined:
|
|
229
|
+
console.log(renderHelp());
|
|
230
|
+
process.exit(0);
|
|
231
|
+
break;
|
|
232
|
+
case "install":
|
|
233
|
+
install(parseArgs(process.argv.slice(3)));
|
|
234
|
+
break;
|
|
235
|
+
default:
|
|
236
|
+
fail(`unknown command: ${command}`);
|
|
237
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@long_88/openclaw-aicc-channel-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI installer for the OpenClaw AICC channel plugin.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"aicc-channel-installer": "./cli.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"cli.mjs",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "vitest run"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"openclaw": "*"
|
|
22
|
+
},
|
|
23
|
+
"peerDependenciesMeta": {
|
|
24
|
+
"openclaw": {
|
|
25
|
+
"optional": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^24.5.2",
|
|
30
|
+
"typescript": "^5.9.2",
|
|
31
|
+
"vitest": "^3.2.4"
|
|
32
|
+
}
|
|
33
|
+
}
|