Compare commits

...

5 Commits

4 changed files with 102 additions and 10 deletions

View File

@@ -343,12 +343,21 @@ async function configure() {
const plugins = getOpenclawConfig('plugins', {});
plugins.entries = plugins.entries || {};
const existingEntry = plugins.entries[PLUGIN_NAME] || {};
const existingConfig = existingEntry.config || {};
const defaultConfig = { enabled: true, secretMgrPath, openclawProfilePath: openclawPath };
plugins.entries[PLUGIN_NAME] = {
enabled: true,
config: { enabled: true, secretMgrPath, openclawProfilePath: openclawPath },
...existingEntry,
enabled: existingEntry.enabled ?? true,
config: {
...defaultConfig,
...existingConfig,
},
};
setOpenclawConfig('plugins', plugins);
logOk('Plugin entry configured');
logOk('Plugin entry configured (preserved existing config, added missing defaults)');
} catch (err) {
logWarn(`Config failed: ${err.message}`);
}

View File

@@ -12,14 +12,17 @@
### 2. 扩展 `openclaw.plugin.json`
需要在 `openclaw.plugin.json` 中新增配置字段:
- `config.proxy-allowlist`
- `config.proxyAllowlist`
兼容性说明:
- 如有需要,也可兼容读取 `proxy-allowlist` 作为别名
用途:
- 用于声明哪些 agent 允许调用 `proxy-pcexec`
- 只有在该 allowlist 中的 agent才具备调用该工具的权限
建议约束:
- `config.proxy-allowlist` 应为 agent 标识列表
- `config.proxyAllowlist` 应为 agent 标识列表
- `allowlist` 仅支持精确匹配,不支持通配、分组或模糊匹配
- 若调用方不在 allowlist 中,应直接拒绝调用
- 默认配置应偏保守;未配置时建议视为不允许任何 agent 调用
@@ -46,7 +49,7 @@
### 权限校验
调用 `proxy-pcexec` 时应至少进行以下校验:
1. 校验调用方 agent 是否在 `config.proxy-allowlist` 中(精确匹配)
1. 校验调用方 agent 是否在 `config.proxyAllowlist` 中(精确匹配)
2. 校验 `proxy-for` 是否存在且非空
3. 不要求 `proxy-for` 必须是已注册或已知 agent-id可自由填写
4. 通过校验后,再执行与 `pcexec` 等价的命令执行流程
@@ -76,5 +79,5 @@
## 已明确的设计结论
- `proxy-for` 可以随意填写,不要求必须是已注册 agent
- 日志需要记录 `executor``proxy-for`
- `config.proxy-allowlist` 仅支持精确匹配
- `config.proxyAllowlist` 仅支持精确匹配
- allowlist 中的 agent 可以代理任意 agent不需要额外的 `proxy-for` 限制

View File

@@ -26,13 +26,25 @@ function resolveOpenclawPath(config?: { openclawProfilePath?: string }): string
return require('path').join(home, '.openclaw');
}
function getPluginConfig(api: any): Record<string, unknown> {
return ((api?.pluginConfig as Record<string, unknown> | undefined) || {});
}
function resolveProxyAllowlist(config?: { proxyAllowlist?: unknown; 'proxy-allowlist'?: unknown }): string[] {
const value = config?.proxyAllowlist ?? config?.['proxy-allowlist'];
if (!Array.isArray(value)) return [];
return value.filter((item): item is string => typeof item === 'string');
}
// Plugin registration function
function register(api: any, config?: any) {
function register(api: any) {
const logger = api.logger || { info: console.log, error: console.error };
logger.info('PaddedCell plugin initializing...');
const openclawPath = resolveOpenclawPath(config);
const pluginConfig = getPluginConfig(api);
const openclawPath = resolveOpenclawPath(pluginConfig as { openclawProfilePath?: string });
const proxyAllowlist = resolveProxyAllowlist(pluginConfig as { proxyAllowlist?: unknown; 'proxy-allowlist'?: unknown });
const binDir = require('path').join(openclawPath, 'bin');
// Register pcexec tool — pass a FACTORY function that receives context
@@ -85,6 +97,69 @@ function register(api: any, config?: any) {
};
});
api.registerTool((ctx: any) => {
const agentId = ctx.agentId;
const workspaceDir = ctx.workspaceDir;
return {
name: 'proxy-pcexec',
description: 'Safe exec with password sanitization using a proxied AGENT_ID',
parameters: {
type: 'object',
properties: {
command: { type: 'string', description: 'Command to execute' },
cwd: { type: 'string', description: 'Working directory' },
timeout: { type: 'number', description: 'Timeout in milliseconds' },
'proxy-for': { type: 'string', description: 'AGENT_ID value to inject for the subprocess' },
},
required: ['command', 'proxy-for'],
},
async execute(_id: string, params: any) {
const command = params.command;
const proxyFor = params['proxy-for'];
if (!command) {
throw new Error('Missing required parameter: command');
}
if (!proxyFor) {
throw new Error('Missing required parameter: proxy-for');
}
if (!agentId || !proxyAllowlist.includes(agentId)) {
throw new Error('Current agent is not allowed to call proxy-pcexec');
}
logger.info('proxy-pcexec invoked', {
executor: agentId,
proxyFor,
command,
});
const currentPath = process.env.PATH || '';
const newPath = currentPath.includes(binDir)
? currentPath
: `${currentPath}:${binDir}`;
const result = await pcexec(command, {
cwd: params.cwd || workspaceDir,
timeout: params.timeout,
env: {
AGENT_ID: String(proxyFor),
AGENT_WORKSPACE: workspaceDir || '',
AGENT_VERIFY,
PROXY_PCEXEC_EXECUTOR: agentId || '',
PCEXEC_PROXIED: 'true',
PATH: newPath,
},
});
let output = result.stdout;
if (result.stderr) {
output += result.stderr;
}
return { content: [{ type: 'text', text: output }] };
},
};
});
// Register safe_restart tool
api.registerTool((ctx: any) => {
const agentId = ctx.agentId;

View File

@@ -9,7 +9,12 @@
"properties": {
"enabled": { "type": "boolean", "default": true },
"secretMgrPath": { "type": "string", "default": "" },
"openclawProfilePath": { "type": "string", "default": "" }
"openclawProfilePath": { "type": "string", "default": "" },
"proxyAllowlist": {
"type": "array",
"items": { "type": "string" },
"default": []
}
}
}
}