feat: enhance ClawHub integration with new CLI paths and IPC handlers for config and skills directories (#41)

This commit is contained in:
Felix
2026-02-11 11:32:03 +08:00
committed by GitHub
Unverified
parent 177cf4c1ea
commit a0505490cd
6 changed files with 129 additions and 25 deletions

View File

@@ -6,7 +6,7 @@ import { spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import { app, shell } from 'electron';
import { getOpenClawConfigDir, ensureDir } from '../utils/paths';
import { getOpenClawConfigDir, ensureDir, getClawHubCliBinPath, getClawHubCliEntryPath } from '../utils/paths';
export interface ClawHubSearchParams {
query: string;
@@ -36,6 +36,8 @@ export interface ClawHubSkillResult {
export class ClawHubService {
private workDir: string;
private cliPath: string;
private cliEntryPath: string;
private useNodeRunner: boolean;
private ansiRegex: RegExp;
constructor() {
@@ -44,11 +46,17 @@ export class ClawHubService {
this.workDir = getOpenClawConfigDir();
ensureDir(this.workDir);
// In development, we use the locally installed clawhub CLI from node_modules
const isWin = process.platform === 'win32';
const binName = isWin ? 'clawhub.cmd' : 'clawhub';
const localCli = path.resolve(app.getAppPath(), 'node_modules', '.bin', binName);
this.cliPath = localCli;
const binPath = getClawHubCliBinPath();
const entryPath = getClawHubCliEntryPath();
this.cliEntryPath = entryPath;
if (!app.isPackaged && fs.existsSync(binPath)) {
this.cliPath = binPath;
this.useNodeRunner = false;
} else {
this.cliPath = process.execPath;
this.useNodeRunner = true;
}
const esc = String.fromCharCode(27);
const csi = String.fromCharCode(155);
const pattern = `(?:${esc}|${csi})[[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`;
@@ -64,17 +72,33 @@ export class ClawHubService {
*/
private async runCommand(args: string[]): Promise<string> {
return new Promise((resolve, reject) => {
console.log(`Running ClawHub command: ${this.cliPath} ${args.join(' ')}`);
if (this.useNodeRunner && !fs.existsSync(this.cliEntryPath)) {
reject(new Error(`ClawHub CLI entry not found at: ${this.cliEntryPath}`));
return;
}
if (!this.useNodeRunner && !fs.existsSync(this.cliPath)) {
reject(new Error(`ClawHub CLI not found at: ${this.cliPath}`));
return;
}
const commandArgs = this.useNodeRunner ? [this.cliEntryPath, ...args] : args;
const displayCommand = [this.cliPath, ...commandArgs].join(' ');
console.log(`Running ClawHub command: ${displayCommand}`);
const isWin = process.platform === 'win32';
const child = spawn(this.cliPath, args, {
const env = {
...process.env,
CI: 'true',
FORCE_COLOR: '0', // Disable colors for easier parsing
};
if (this.useNodeRunner) {
env.ELECTRON_RUN_AS_NODE = '1';
}
const child = spawn(this.cliPath, commandArgs, {
cwd: this.workDir,
shell: isWin,
env: {
...process.env,
CI: 'true',
FORCE_COLOR: '0', // Disable colors for easier parsing
},
shell: isWin && !this.useNodeRunner,
env,
});
let stdout = '';

View File

@@ -21,7 +21,7 @@ import {
isEncryptionAvailable,
type ProviderConfig,
} from '../utils/secure-storage';
import { getOpenClawStatus, getOpenClawDir } from '../utils/paths';
import { getOpenClawStatus, getOpenClawDir, getOpenClawConfigDir, getOpenClawSkillsDir } from '../utils/paths';
import { getOpenClawCliCommand, installOpenClawCliMac } from '../utils/openclaw-cli';
import { getSetting } from '../utils/store';
import { saveProviderKeyToOpenClaw, setOpenClawDefaultModel } from '../utils/openclaw-auth';
@@ -506,6 +506,16 @@ function registerOpenClawHandlers(): void {
return getOpenClawDir();
});
// Get the OpenClaw config directory (~/.openclaw)
ipcMain.handle('openclaw:getConfigDir', () => {
return getOpenClawConfigDir();
});
// Get the OpenClaw skills directory (~/.openclaw/skills)
ipcMain.handle('openclaw:getSkillsDir', () => {
return getOpenClawSkillsDir();
});
// Get a shell command to run OpenClaw CLI without modifying PATH
ipcMain.handle('openclaw:getCliCommand', () => {
try {

View File

@@ -117,6 +117,8 @@ const electronAPI = {
'log:listFiles',
// OpenClaw extras
'openclaw:getDir',
'openclaw:getConfigDir',
'openclaw:getSkillsDir',
'openclaw:getCliCommand',
'openclaw:installCliMac',
];

View File

@@ -25,6 +25,13 @@ export function getOpenClawConfigDir(): string {
return join(homedir(), '.openclaw');
}
/**
* Get OpenClaw skills directory
*/
export function getOpenClawSkillsDir(): string {
return join(getOpenClawConfigDir(), 'skills');
}
/**
* Get ClawX config directory
*/
@@ -108,6 +115,21 @@ export function getOpenClawEntryPath(): string {
return join(getOpenClawDir(), 'openclaw.mjs');
}
/**
* Get ClawHub CLI entry script path (clawdhub.js)
*/
export function getClawHubCliEntryPath(): string {
return join(app.getAppPath(), 'node_modules', 'clawhub', 'bin', 'clawdhub.js');
}
/**
* Get ClawHub CLI binary path (node_modules/.bin)
*/
export function getClawHubCliBinPath(): string {
const binName = process.platform === 'win32' ? 'clawhub.cmd' : 'clawhub';
return join(app.getAppPath(), 'node_modules', '.bin', binName);
}
/**
* Check if OpenClaw package exists
*/