claw-control-center 0.1.8 → 0.1.9
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/dist/index.cjs
CHANGED
|
@@ -8886,9 +8886,17 @@ var import_promises5 = require("fs/promises");
|
|
|
8886
8886
|
var import_node_fs4 = require("fs");
|
|
8887
8887
|
var import_node_os2 = require("os");
|
|
8888
8888
|
var import_node_path6 = require("path");
|
|
8889
|
+
var import_yaml = require("yaml");
|
|
8889
8890
|
var PLUGIN_ID = "claw-control-center";
|
|
8890
8891
|
var LEGACY_PLUGIN_ID = "53ai-openclaw";
|
|
8891
8892
|
var COPY_ITEMS = ["dist", "openclaw.plugin.json", "package.json", "bin", "web-dist"];
|
|
8893
|
+
var HERMES_PLATFORM_ID = "53aihub";
|
|
8894
|
+
var HERMES_PLUGIN_KEY = `platforms/${HERMES_PLATFORM_ID}`;
|
|
8895
|
+
var HERMES_ENV_KEYS = {
|
|
8896
|
+
botId: "HUB53AI_BOT_ID",
|
|
8897
|
+
secret: "HUB53AI_SECRET",
|
|
8898
|
+
wsUrl: "HUB53AI_WS_URL"
|
|
8899
|
+
};
|
|
8892
8900
|
var SUPPORTED_ARGS = /* @__PURE__ */ new Set([
|
|
8893
8901
|
"gateway",
|
|
8894
8902
|
"bot-id",
|
|
@@ -8910,6 +8918,31 @@ async function installIntoQClaw(input) {
|
|
|
8910
8918
|
async function installIntoOpenClaw(input) {
|
|
8911
8919
|
return installIntoHost(input, "OpenClaw");
|
|
8912
8920
|
}
|
|
8921
|
+
async function installIntoHermes(input) {
|
|
8922
|
+
const hubWsUrl = input.hubWsUrl?.trim();
|
|
8923
|
+
const hubBotId = input.hubBotId?.trim();
|
|
8924
|
+
const hubSecret = input.hubSecret?.trim();
|
|
8925
|
+
if (!hubWsUrl || !hubBotId || !hubSecret) {
|
|
8926
|
+
throw new Error("Hermes install requires --hub-ws-url, --hub-bot-id, and --hub-secret");
|
|
8927
|
+
}
|
|
8928
|
+
const platformsDir = normalizeHermesPlatformsDir(input.extensionsDir);
|
|
8929
|
+
const destination = (0, import_node_path6.join)(platformsDir, HERMES_PLATFORM_ID);
|
|
8930
|
+
await (0, import_promises5.mkdir)(destination, { recursive: true });
|
|
8931
|
+
await copyHermesPlatformPackage(input.packageRoot, destination);
|
|
8932
|
+
await updateHermesConfig(input.configPath);
|
|
8933
|
+
await updateHermesEnv(input.configPath, {
|
|
8934
|
+
botId: hubBotId,
|
|
8935
|
+
secret: hubSecret,
|
|
8936
|
+
wsUrl: hubWsUrl
|
|
8937
|
+
});
|
|
8938
|
+
return {
|
|
8939
|
+
configPath: input.configPath,
|
|
8940
|
+
extensionsDir: platformsDir,
|
|
8941
|
+
destination,
|
|
8942
|
+
hub53aiConfigured: true,
|
|
8943
|
+
pluginBuild: await readHermesPluginBuildInfo(destination)
|
|
8944
|
+
};
|
|
8945
|
+
}
|
|
8913
8946
|
async function installIntoHost(input, hostLabel) {
|
|
8914
8947
|
await (0, import_promises5.mkdir)(input.extensionsDir, { recursive: true });
|
|
8915
8948
|
const destination = (0, import_node_path6.join)(input.extensionsDir, PLUGIN_ID);
|
|
@@ -9027,13 +9060,14 @@ async function runInstallCommand(input) {
|
|
|
9027
9060
|
throw new Error("expected subcommand: install");
|
|
9028
9061
|
}
|
|
9029
9062
|
const args = parseArgs(argv.slice(1));
|
|
9030
|
-
const
|
|
9063
|
+
const destinations = await resolveInstallDestinations(args, {
|
|
9031
9064
|
hostDefinitions: input.hostDefinitions,
|
|
9065
|
+
selectHosts: input.selectHosts,
|
|
9032
9066
|
selectHost: input.selectHost,
|
|
9033
9067
|
ttyPath: input.ttyPath
|
|
9034
9068
|
});
|
|
9035
|
-
const
|
|
9036
|
-
{
|
|
9069
|
+
for (const destination of destinations) {
|
|
9070
|
+
const installInput = {
|
|
9037
9071
|
packageRoot: input.packageRoot,
|
|
9038
9072
|
extensionsDir: destination.extensionsDir,
|
|
9039
9073
|
configPath: destination.configPath,
|
|
@@ -9048,41 +9082,45 @@ async function runInstallCommand(input) {
|
|
|
9048
9082
|
hubEnabled: parseOptionalBoolean(args["hub-enabled"]),
|
|
9049
9083
|
consoleHost: args["console-host"],
|
|
9050
9084
|
consolePort: args["console-port"] ? Number(args["console-port"]) : void 0
|
|
9051
|
-
}
|
|
9052
|
-
destination.label
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9085
|
+
};
|
|
9086
|
+
const result = destination.installKind === "hermes" ? await installIntoHermes(installInput) : await installIntoHost(installInput, destination.label);
|
|
9087
|
+
process.stdout.write(
|
|
9088
|
+
[
|
|
9089
|
+
`Installed ${PLUGIN_ID} into ${destination.label}.`,
|
|
9090
|
+
`Extensions: ${result.extensionsDir}`,
|
|
9091
|
+
`Config: ${result.configPath}`,
|
|
9092
|
+
...destination.installKind === "hermes" ? [] : [`Gateway: ${result.gatewayBaseUrl}`],
|
|
9093
|
+
`53AIHub: ${result.hub53aiConfigured ? "configured" : "not configured"}`,
|
|
9094
|
+
`Plugin build: ${result.pluginBuild}`,
|
|
9095
|
+
`Restart ${destination.label} to load the plugin.`
|
|
9096
|
+
].join("\n") + "\n"
|
|
9097
|
+
);
|
|
9098
|
+
}
|
|
9065
9099
|
}
|
|
9066
|
-
async function
|
|
9100
|
+
async function resolveInstallDestinations(args, options = {}) {
|
|
9067
9101
|
const explicitConfigPath = args["config-path"] ? (0, import_node_path6.resolve)(args["config-path"]) : void 0;
|
|
9068
9102
|
const explicitExtensionsDir = args["extensions-dir"] ? (0, import_node_path6.resolve)(args["extensions-dir"]) : void 0;
|
|
9069
9103
|
if (explicitConfigPath && explicitExtensionsDir) {
|
|
9070
|
-
|
|
9104
|
+
const hermes = isHermesDestination(explicitConfigPath, explicitExtensionsDir);
|
|
9105
|
+
return [{
|
|
9071
9106
|
configPath: explicitConfigPath,
|
|
9072
|
-
extensionsDir: explicitExtensionsDir,
|
|
9073
|
-
label: "Claw"
|
|
9074
|
-
|
|
9107
|
+
extensionsDir: hermes ? normalizeHermesPlatformsDir(explicitExtensionsDir) : explicitExtensionsDir,
|
|
9108
|
+
label: hermes ? "Hermes" : "Claw",
|
|
9109
|
+
installKind: hermes ? "hermes" : "openclaw"
|
|
9110
|
+
}];
|
|
9075
9111
|
}
|
|
9076
9112
|
if (explicitConfigPath || explicitExtensionsDir) {
|
|
9077
9113
|
throw new Error("pass --config-path and --extensions-dir together, or omit both to auto-detect Claw");
|
|
9078
9114
|
}
|
|
9079
9115
|
const detected = detectInstallHosts(options.hostDefinitions ?? getDefaultHostDefinitions());
|
|
9080
|
-
|
|
9081
|
-
|
|
9116
|
+
const compatible = detected;
|
|
9117
|
+
const incompatible = [];
|
|
9118
|
+
if (compatible.length === 1) {
|
|
9119
|
+
return [toInstallDestination(compatible[0])];
|
|
9082
9120
|
}
|
|
9083
|
-
if (
|
|
9084
|
-
const selected = options.selectHost ? await options.selectHost(
|
|
9085
|
-
return
|
|
9121
|
+
if (compatible.length > 1) {
|
|
9122
|
+
const selected = options.selectHosts ? await options.selectHosts(compatible, incompatible) : options.selectHost ? [await options.selectHost(compatible)] : await promptForInstallHosts(compatible, incompatible, options.ttyPath ?? "/dev/tty");
|
|
9123
|
+
return validateSelectedHosts(selected, compatible).map(toInstallDestination);
|
|
9086
9124
|
}
|
|
9087
9125
|
throw new Error(
|
|
9088
9126
|
[
|
|
@@ -9098,6 +9136,7 @@ function getDefaultHostDefinitions() {
|
|
|
9098
9136
|
const home = (0, import_node_os2.homedir)();
|
|
9099
9137
|
const qclawHome = (0, import_node_path6.resolve)(home, ".qclaw");
|
|
9100
9138
|
const openClawHome = (0, import_node_path6.resolve)(home, ".openclaw");
|
|
9139
|
+
const hermesHome = (0, import_node_path6.resolve)(home, ".hermes");
|
|
9101
9140
|
return [
|
|
9102
9141
|
{
|
|
9103
9142
|
id: "qclaw",
|
|
@@ -9110,6 +9149,13 @@ function getDefaultHostDefinitions() {
|
|
|
9110
9149
|
label: "OpenClaw",
|
|
9111
9150
|
configPath: (0, import_node_path6.join)(openClawHome, "openclaw.json"),
|
|
9112
9151
|
extensionsDir: (0, import_node_path6.resolve)(openClawHome, "extensions")
|
|
9152
|
+
},
|
|
9153
|
+
{
|
|
9154
|
+
id: "hermes",
|
|
9155
|
+
label: "Hermes",
|
|
9156
|
+
configPath: (0, import_node_path6.join)(hermesHome, "config.yaml"),
|
|
9157
|
+
extensionsDir: (0, import_node_path6.resolve)(hermesHome, "plugins", "platforms"),
|
|
9158
|
+
installKind: "hermes"
|
|
9113
9159
|
}
|
|
9114
9160
|
];
|
|
9115
9161
|
}
|
|
@@ -9124,7 +9170,7 @@ function detectInstallHosts(hosts) {
|
|
|
9124
9170
|
return true;
|
|
9125
9171
|
});
|
|
9126
9172
|
}
|
|
9127
|
-
async function
|
|
9173
|
+
async function promptForInstallHosts(hosts, incompatibleHosts, ttyPath) {
|
|
9128
9174
|
let handle;
|
|
9129
9175
|
try {
|
|
9130
9176
|
handle = await (0, import_promises5.open)(ttyPath, "r+");
|
|
@@ -9133,7 +9179,7 @@ async function promptForInstallHost(hosts, ttyPath) {
|
|
|
9133
9179
|
const readline = (0, import_promises4.createInterface)({ input, output });
|
|
9134
9180
|
try {
|
|
9135
9181
|
output.write("Multiple Claw installations were detected.\n");
|
|
9136
|
-
output.write("Choose
|
|
9182
|
+
output.write("Choose one or more locations to install claw-control-center:\n");
|
|
9137
9183
|
hosts.forEach((host, index) => {
|
|
9138
9184
|
output.write(`${index + 1}. ${host.label}
|
|
9139
9185
|
`);
|
|
@@ -9142,12 +9188,19 @@ async function promptForInstallHost(hosts, ttyPath) {
|
|
|
9142
9188
|
output.write(` Extensions: ${host.extensionsDir}
|
|
9143
9189
|
`);
|
|
9144
9190
|
});
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9191
|
+
if (incompatibleHosts.length > 0) {
|
|
9192
|
+
output.write("\nDetected but not installable by this OpenClaw plugin installer:\n");
|
|
9193
|
+
for (const host of incompatibleHosts) {
|
|
9194
|
+
output.write(`- ${host.label}: ${host.incompatibilityReason ?? "incompatible plugin format"}
|
|
9195
|
+
`);
|
|
9196
|
+
}
|
|
9197
|
+
}
|
|
9198
|
+
const answer = await readline.question(`Install location(s) [1-${hosts.length}, comma-separated, or all]: `);
|
|
9199
|
+
const selectedIndexes = parseInstallSelection(answer, hosts.length);
|
|
9200
|
+
if (selectedIndexes.length === 0) {
|
|
9148
9201
|
throw new Error(`invalid install location: ${answer}`);
|
|
9149
9202
|
}
|
|
9150
|
-
return hosts[
|
|
9203
|
+
return selectedIndexes.map((index) => hosts[index - 1]);
|
|
9151
9204
|
} finally {
|
|
9152
9205
|
readline.close();
|
|
9153
9206
|
input.destroy();
|
|
@@ -9174,14 +9227,39 @@ function toInstallDestination(host) {
|
|
|
9174
9227
|
return {
|
|
9175
9228
|
configPath: (0, import_node_path6.resolve)(host.configPath),
|
|
9176
9229
|
extensionsDir: (0, import_node_path6.resolve)(host.extensionsDir),
|
|
9177
|
-
label: host.label
|
|
9230
|
+
label: host.label,
|
|
9231
|
+
installKind: host.installKind ?? "openclaw"
|
|
9178
9232
|
};
|
|
9179
9233
|
}
|
|
9234
|
+
function validateSelectedHosts(selected, compatible) {
|
|
9235
|
+
const compatibleIds = new Set(compatible.map((host) => host.id));
|
|
9236
|
+
const invalid = selected.find((host) => !compatibleIds.has(host.id));
|
|
9237
|
+
if (invalid) {
|
|
9238
|
+
throw new Error(`selected host is not installable: ${invalid.label}`);
|
|
9239
|
+
}
|
|
9240
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9241
|
+
return selected.filter((host) => {
|
|
9242
|
+
if (seen.has(host.id)) {
|
|
9243
|
+
return false;
|
|
9244
|
+
}
|
|
9245
|
+
seen.add(host.id);
|
|
9246
|
+
return true;
|
|
9247
|
+
});
|
|
9248
|
+
}
|
|
9249
|
+
function parseInstallSelection(answer, hostCount) {
|
|
9250
|
+
const trimmed = answer.trim().toLowerCase();
|
|
9251
|
+
if (trimmed === "all" || trimmed === "*") {
|
|
9252
|
+
return Array.from({ length: hostCount }, (_, index) => index + 1);
|
|
9253
|
+
}
|
|
9254
|
+
const indexes = trimmed.split(",").map((part) => Number(part.trim())).filter((index) => Number.isInteger(index) && index >= 1 && index <= hostCount);
|
|
9255
|
+
return Array.from(new Set(indexes));
|
|
9256
|
+
}
|
|
9180
9257
|
function formatHostList(hosts) {
|
|
9181
9258
|
return hosts.flatMap((host) => [
|
|
9182
9259
|
`- ${host.label}`,
|
|
9183
9260
|
` Config: ${host.configPath}`,
|
|
9184
|
-
` Extensions: ${host.extensionsDir}
|
|
9261
|
+
` Extensions: ${host.extensionsDir}`,
|
|
9262
|
+
...host.incompatibilityReason ? [` Note: ${host.incompatibilityReason}`] : []
|
|
9185
9263
|
]);
|
|
9186
9264
|
}
|
|
9187
9265
|
function parseArgs(argv) {
|
|
@@ -9222,6 +9300,15 @@ async function copyPublishablePackage(packageRoot, destination) {
|
|
|
9222
9300
|
await (0, import_promises5.cp)(source, target, { recursive: true, force: true });
|
|
9223
9301
|
}
|
|
9224
9302
|
}
|
|
9303
|
+
async function copyHermesPlatformPackage(packageRoot, destination) {
|
|
9304
|
+
const source = (0, import_node_path6.join)(packageRoot, "hermes", "platforms", HERMES_PLATFORM_ID);
|
|
9305
|
+
if (!(0, import_node_fs4.existsSync)(source)) {
|
|
9306
|
+
throw new Error(`Hermes platform package does not exist: ${source}`);
|
|
9307
|
+
}
|
|
9308
|
+
await (0, import_promises5.rm)(destination, { recursive: true, force: true });
|
|
9309
|
+
await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(destination), { recursive: true });
|
|
9310
|
+
await (0, import_promises5.cp)(source, destination, { recursive: true, force: true });
|
|
9311
|
+
}
|
|
9225
9312
|
async function readPluginBuildInfo(destination) {
|
|
9226
9313
|
const packagePath = (0, import_node_path6.join)(destination, "package.json");
|
|
9227
9314
|
const entryPath = (0, import_node_path6.join)(destination, "dist", "index.cjs");
|
|
@@ -9242,12 +9329,86 @@ async function readPluginBuildInfo(destination) {
|
|
|
9242
9329
|
return `${PLUGIN_ID}@${version} sha256:missing`;
|
|
9243
9330
|
}
|
|
9244
9331
|
}
|
|
9332
|
+
async function readHermesPluginBuildInfo(destination) {
|
|
9333
|
+
const manifestPath = (0, import_node_path6.join)(destination, "plugin.yaml");
|
|
9334
|
+
try {
|
|
9335
|
+
const manifest = (0, import_yaml.parse)(await (0, import_promises5.readFile)(manifestPath, "utf8"));
|
|
9336
|
+
const version = typeof manifest?.version === "string" || typeof manifest?.version === "number" ? String(manifest.version) : "unknown";
|
|
9337
|
+
const adapter = await (0, import_promises5.readFile)((0, import_node_path6.join)(destination, "adapter.py"));
|
|
9338
|
+
const digest = (0, import_node_crypto4.createHash)("sha256").update(adapter).digest("hex").slice(0, 12);
|
|
9339
|
+
return `${PLUGIN_ID}/hermes@${version} sha256:${digest}`;
|
|
9340
|
+
} catch {
|
|
9341
|
+
return `${PLUGIN_ID}/hermes@unknown sha256:missing`;
|
|
9342
|
+
}
|
|
9343
|
+
}
|
|
9245
9344
|
async function readOpenClawConfig(configPath) {
|
|
9246
9345
|
if (!(0, import_node_fs4.existsSync)(configPath)) {
|
|
9247
9346
|
return {};
|
|
9248
9347
|
}
|
|
9249
9348
|
return JSON.parse(await (0, import_promises5.readFile)(configPath, "utf8"));
|
|
9250
9349
|
}
|
|
9350
|
+
async function updateHermesConfig(configPath) {
|
|
9351
|
+
await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(configPath), { recursive: true });
|
|
9352
|
+
const config = await readHermesConfig(configPath);
|
|
9353
|
+
const plugins = ensureObject(config, "plugins");
|
|
9354
|
+
plugins.enabled = dedupeStrings([...Array.isArray(plugins.enabled) ? plugins.enabled : [], HERMES_PLUGIN_KEY, HERMES_PLATFORM_ID]);
|
|
9355
|
+
const platforms = ensureObject(config, "platforms");
|
|
9356
|
+
const platform = ensureObject(platforms, HERMES_PLATFORM_ID);
|
|
9357
|
+
platform.enabled = true;
|
|
9358
|
+
ensureObject(platform, "extra");
|
|
9359
|
+
await (0, import_promises5.writeFile)(configPath, (0, import_yaml.stringify)(config));
|
|
9360
|
+
}
|
|
9361
|
+
async function readHermesConfig(configPath) {
|
|
9362
|
+
if (!(0, import_node_fs4.existsSync)(configPath)) {
|
|
9363
|
+
return {};
|
|
9364
|
+
}
|
|
9365
|
+
const parsed = (0, import_yaml.parse)(await (0, import_promises5.readFile)(configPath, "utf8"));
|
|
9366
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
9367
|
+
}
|
|
9368
|
+
async function updateHermesEnv(configPath, values) {
|
|
9369
|
+
const envPath = (0, import_node_path6.join)((0, import_node_path6.dirname)(configPath), ".env");
|
|
9370
|
+
await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(envPath), { recursive: true });
|
|
9371
|
+
const existing = (0, import_node_fs4.existsSync)(envPath) ? await (0, import_promises5.readFile)(envPath, "utf8") : "";
|
|
9372
|
+
const updates = /* @__PURE__ */ new Map([
|
|
9373
|
+
[HERMES_ENV_KEYS.botId, values.botId],
|
|
9374
|
+
[HERMES_ENV_KEYS.secret, values.secret],
|
|
9375
|
+
[HERMES_ENV_KEYS.wsUrl, values.wsUrl]
|
|
9376
|
+
]);
|
|
9377
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9378
|
+
const lines = existing.split(/\r?\n/).map((line) => {
|
|
9379
|
+
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=/);
|
|
9380
|
+
if (!match || !updates.has(match[1])) {
|
|
9381
|
+
return line;
|
|
9382
|
+
}
|
|
9383
|
+
const key = match[1];
|
|
9384
|
+
seen.add(key);
|
|
9385
|
+
return `${key}=${quoteEnvValue(updates.get(key))}`;
|
|
9386
|
+
});
|
|
9387
|
+
for (const [key, value] of updates) {
|
|
9388
|
+
if (!seen.has(key)) {
|
|
9389
|
+
lines.push(`${key}=${quoteEnvValue(value)}`);
|
|
9390
|
+
}
|
|
9391
|
+
}
|
|
9392
|
+
await (0, import_promises5.writeFile)(envPath, `${lines.filter((line, index, all) => line || index < all.length - 1).join("\n")}
|
|
9393
|
+
`);
|
|
9394
|
+
}
|
|
9395
|
+
function quoteEnvValue(value) {
|
|
9396
|
+
return JSON.stringify(value);
|
|
9397
|
+
}
|
|
9398
|
+
function isHermesDestination(configPath, extensionsDir) {
|
|
9399
|
+
const tail = extensionsDir.split(/[\\/]/).filter(Boolean).slice(-2).join("/");
|
|
9400
|
+
return configPath.endsWith("config.yaml") && (tail === "plugins/platforms" || tail.endsWith("/plugins"));
|
|
9401
|
+
}
|
|
9402
|
+
function normalizeHermesPlatformsDir(extensionsDir) {
|
|
9403
|
+
const parts = extensionsDir.split(/[\\/]/).filter(Boolean);
|
|
9404
|
+
if (parts.at(-1) === "platforms" && parts.at(-2) === "plugins") {
|
|
9405
|
+
return extensionsDir;
|
|
9406
|
+
}
|
|
9407
|
+
if (parts.at(-1) === "plugins") {
|
|
9408
|
+
return (0, import_node_path6.join)(extensionsDir, "platforms");
|
|
9409
|
+
}
|
|
9410
|
+
return extensionsDir;
|
|
9411
|
+
}
|
|
9251
9412
|
function inferGatewaySettings(config) {
|
|
9252
9413
|
const gateway = config.gateway ?? {};
|
|
9253
9414
|
const host = typeof gateway.host === "string" && gateway.host.trim() ? gateway.host.trim() : "127.0.0.1";
|