@fabioforest/openclaw 3.0.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 +88 -0
- package/bin/openclaw.js +152 -0
- package/lib/channels.js +84 -0
- package/lib/cli/debug.js +12 -0
- package/lib/cli/doctor.js +266 -0
- package/lib/cli/init.js +145 -0
- package/lib/cli/orchestrate.js +43 -0
- package/lib/cli/status.js +128 -0
- package/lib/cli/update.js +122 -0
- package/lib/config.js +68 -0
- package/lib/detect.js +49 -0
- package/lib/ops/audit.js +156 -0
- package/lib/ops/enroll.js +133 -0
- package/lib/ops/exec.js +140 -0
- package/lib/ops/healthcheck.js +167 -0
- package/lib/ops/policy.js +153 -0
- package/lib/ops/transfer.js +152 -0
- package/lib/ops/update-safe.js +173 -0
- package/lib/ops/vpn.js +131 -0
- package/lib/security.js +48 -0
- package/lib/setup/config_wizard.js +186 -0
- package/package.json +47 -0
- package/templates/.agent/agents/setup-specialist.md +24 -0
- package/templates/.agent/agents/sysadmin-proativo.md +31 -0
- package/templates/.agent/hooks/pre-tool-use.js +109 -0
- package/templates/.agent/rules/SECURITY.md +7 -0
- package/templates/.agent/skills/openclaw-installation-debugger/SKILL.md +37 -0
- package/templates/.agent/skills/openclaw-installation-debugger/scripts/debug.js +165 -0
- package/templates/.agent/skills/openclaw-ops/01-openclaw-vpn-wireguard/SKILL.md +20 -0
- package/templates/.agent/skills/openclaw-ops/02-openclaw-enroll-host/SKILL.md +14 -0
- package/templates/.agent/skills/openclaw-ops/03-openclaw-policy-baseline/SKILL.md +17 -0
- package/templates/.agent/skills/openclaw-ops/04-openclaw-remote-exec-runbooks/SKILL.md +13 -0
- package/templates/.agent/skills/openclaw-ops/05-openclaw-file-transfer-safe/SKILL.md +10 -0
- package/templates/.agent/skills/openclaw-ops/06-openclaw-audit-logging/SKILL.md +13 -0
- package/templates/.agent/skills/openclaw-ops/07-openclaw-safe-update/SKILL.md +9 -0
- package/templates/.agent/skills/openclaw-ops/08-openclaw-healthchecks/SKILL.md +10 -0
- package/templates/.agent/skills/universal-setup/SKILL.md +26 -0
- package/templates/.agent/workflows/doctor.md +20 -0
- package/templates/.agent/workflows/healthcheck.md +22 -0
- package/templates/.agent/workflows/healthcheck.runbook.md +9 -0
- package/templates/.agent/workflows/restart.md +20 -0
- package/templates/.agent/workflows/restart.runbook.md +8 -0
- package/templates/.agent/workflows/setup.md +18 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OpenClaw OS - Universal Setup Wizard
|
|
4
|
+
*
|
|
5
|
+
* Orquestrador principal que delega para os módulos lib/:
|
|
6
|
+
* - detect.js → detecção de ambiente (Docker/WSL2/Mac/Linux/VPS)
|
|
7
|
+
* - config.js → leitura/escrita JSON atômica + defaults
|
|
8
|
+
* - security.js → tokens, masking e verificação de porta
|
|
9
|
+
* - channels.js → validação e configuração de canais
|
|
10
|
+
*
|
|
11
|
+
* Princípios:
|
|
12
|
+
* - Seguro por padrão: bind localhost + auth token
|
|
13
|
+
* - Cross-platform: detecção automática de ambiente
|
|
14
|
+
* - Não destrutivo: nunca sobrescreve sem confirmação
|
|
15
|
+
*
|
|
16
|
+
* @module config_wizard
|
|
17
|
+
* @version 2.0.0
|
|
18
|
+
* @author OpenClaw DevOps
|
|
19
|
+
*/
|
|
20
|
+
const fs = require("fs");
|
|
21
|
+
const os = require("os");
|
|
22
|
+
const path = require("path");
|
|
23
|
+
const readline = require("readline");
|
|
24
|
+
|
|
25
|
+
// Módulos extraídos para lib/ — cada um com responsabilidade única
|
|
26
|
+
const { detectEnvironment } = require("../detect");
|
|
27
|
+
const { readJsonSafe, writeJsonSafe, ensureFile, initConfigDefaults } = require("../config");
|
|
28
|
+
const { mask, generateToken, portInUse } = require("../security");
|
|
29
|
+
const { supportedChannels, configureChannel } = require("../channels");
|
|
30
|
+
|
|
31
|
+
/** Interface readline para perguntas interativas */
|
|
32
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Faz uma pergunta ao usuário via stdin e retorna a resposta trimada.
|
|
36
|
+
* @param {string} q - A pergunta a ser exibida
|
|
37
|
+
* @returns {Promise<string>} Resposta do usuário (trimada)
|
|
38
|
+
*/
|
|
39
|
+
function ask(q) { return new Promise(res => rl.question(q, ans => res(ans.trim()))); }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Função principal do wizard de setup.
|
|
43
|
+
* Orquestra todo o fluxo interativo:
|
|
44
|
+
* 1. Detecta ambiente
|
|
45
|
+
* 2. Configura gateway (bind + auth)
|
|
46
|
+
* 3. Gera/solicita token de autenticação
|
|
47
|
+
* 4. Sugere sandbox em VPS
|
|
48
|
+
* 5. Configura canais (Telegram/Discord/WhatsApp)
|
|
49
|
+
* 6. Configura allowlist de filesystem
|
|
50
|
+
* 7. Cria arquivos de persistência (MEMORY.md, SOUL.md, AGENTS.md)
|
|
51
|
+
* 8. Verifica porta 18789
|
|
52
|
+
* 9. Sugere hardening em VPS
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
async function main() {
|
|
56
|
+
console.log("\n🧠 OpenClaw OS — Universal Setup Wizard\n");
|
|
57
|
+
|
|
58
|
+
// --- 1. Detecção de ambiente (delegado para lib/detect) ---
|
|
59
|
+
const env = detectEnvironment();
|
|
60
|
+
console.log(`Ambiente detectado: ${env}`);
|
|
61
|
+
|
|
62
|
+
const base = process.cwd();
|
|
63
|
+
const configPath = path.join(base, "openclaw.json");
|
|
64
|
+
|
|
65
|
+
// --- 2. Leitura da configuração existente (delegado para lib/config) ---
|
|
66
|
+
let config = {};
|
|
67
|
+
if (fs.existsSync(configPath)) {
|
|
68
|
+
const parsed = readJsonSafe(configPath);
|
|
69
|
+
if (!parsed) {
|
|
70
|
+
console.error("✖ openclaw.json existe mas não é um JSON válido. Corrija e rode novamente.");
|
|
71
|
+
process.exit(2);
|
|
72
|
+
}
|
|
73
|
+
config = parsed;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Inicializa seções padrão sem sobrescrever existentes
|
|
77
|
+
config = initConfigDefaults(config);
|
|
78
|
+
|
|
79
|
+
let needWrite = !fs.existsSync(configPath);
|
|
80
|
+
|
|
81
|
+
// --- 3. gateway.bind: segurança por padrão (localhost only) ---
|
|
82
|
+
const desiredBind = "127.0.0.1";
|
|
83
|
+
if (config.gateway.bind !== desiredBind) {
|
|
84
|
+
const ans = await ask(`gateway.bind está "${config.gateway.bind ?? "(vazio)"}". Ajustar para "${desiredBind}"? (y/n): `);
|
|
85
|
+
if (ans.toLowerCase() === "y") { config.gateway.bind = desiredBind; needWrite = true; }
|
|
86
|
+
} else {
|
|
87
|
+
console.log("✔ gateway.bind já está seguro (127.0.0.1)");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// --- 4. auth.mode: token obrigatório ---
|
|
91
|
+
const desiredAuthMode = "token";
|
|
92
|
+
if (config.auth.mode !== desiredAuthMode) {
|
|
93
|
+
const ans = await ask(`auth.mode está "${config.auth.mode ?? "(vazio)"}". Ajustar para "${desiredAuthMode}"? (y/n): `);
|
|
94
|
+
if (ans.toLowerCase() === "y") { config.auth.mode = desiredAuthMode; needWrite = true; }
|
|
95
|
+
} else {
|
|
96
|
+
console.log("✔ auth.mode já está em token");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- 5. Geração de token de autenticação (delegado para lib/security) ---
|
|
100
|
+
if (config.auth.mode === "token") {
|
|
101
|
+
config.auth.token = config.auth.token || "";
|
|
102
|
+
if (!config.auth.token) {
|
|
103
|
+
const ans = await ask("Nenhum token encontrado. Gerar um token seguro automaticamente? (y/n): ");
|
|
104
|
+
if (ans.toLowerCase() === "y") {
|
|
105
|
+
config.auth.token = generateToken();
|
|
106
|
+
console.log(`✔ Token gerado: ${mask(config.auth.token)} (salvo no openclaw.json)`);
|
|
107
|
+
needWrite = true;
|
|
108
|
+
} else {
|
|
109
|
+
const manual = await ask("Cole um token: ");
|
|
110
|
+
if (manual) { config.auth.token = manual; needWrite = true; }
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`✔ Token já configurado (${mask(config.auth.token)})`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// --- 6. Sandbox: sugestão para VPS rodando como root ---
|
|
118
|
+
if (env === "linux-vps-root") {
|
|
119
|
+
if (config.sandbox.mode !== "non-main") {
|
|
120
|
+
const ans = await ask(`Detectei VPS/root. Ativar sandbox mode "non-main" para isolar execuções? (y/n): `);
|
|
121
|
+
if (ans.toLowerCase() === "y") { config.sandbox.mode = "non-main"; needWrite = true; }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// --- 7. Configuração de canais (delegado para lib/channels) ---
|
|
126
|
+
console.log("\n📣 Canais (opcional)");
|
|
127
|
+
const channelList = supportedChannels().join("/");
|
|
128
|
+
const ch = await ask(`Ativar agora? (${channelList}/nenhum): `);
|
|
129
|
+
const channelChoice = ch.toLowerCase();
|
|
130
|
+
|
|
131
|
+
if (supportedChannels().includes(channelChoice)) {
|
|
132
|
+
// configureChannel recebe uma função ask injetável (testável)
|
|
133
|
+
const configured = await configureChannel(config, channelChoice, ask);
|
|
134
|
+
if (configured) needWrite = true;
|
|
135
|
+
} else {
|
|
136
|
+
console.log("↪ Pulando canais.");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// --- 8. Filesystem allowlist: princípio do menor privilégio ---
|
|
140
|
+
console.log("\n📁 Acesso a arquivos locais (mínimo necessário)");
|
|
141
|
+
console.log("Adicione apenas pastas que o OpenClaw realmente precisa acessar.");
|
|
142
|
+
const addPath = await ask("Adicionar uma pasta allowlist agora? (caminho ou ENTER para pular): ");
|
|
143
|
+
if (addPath) {
|
|
144
|
+
const resolved = addPath.replace(/^~\//, os.homedir() + path.sep);
|
|
145
|
+
config.filesystem.allowlist.push(resolved);
|
|
146
|
+
needWrite = true;
|
|
147
|
+
console.log(`✔ Allowlist adicionada: ${resolved}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --- 9. Arquivos de persistência (delegado para lib/config.ensureFile) ---
|
|
151
|
+
ensureFile(path.join(base, "MEMORY.md"), "# MEMORY.md\n\n- Preferências e notas persistentes do OpenClaw.\n");
|
|
152
|
+
ensureFile(path.join(base, "SOUL.md"), "# SOUL.md\n\n- Identidade e regras de comportamento (ver AGENTS.md).\n");
|
|
153
|
+
ensureFile(path.join(base, "AGENTS.md"), "# AGENTS.md\n\nVocê é um SysAdmin Proativo. Use VPN-first, bind localhost e token.\n");
|
|
154
|
+
|
|
155
|
+
// --- 10. Checagem de porta (delegado para lib/security.portInUse) ---
|
|
156
|
+
const port = 18789;
|
|
157
|
+
console.log("\n🔎 Checagens rápidas");
|
|
158
|
+
const inUse = await portInUse("127.0.0.1", port);
|
|
159
|
+
if (inUse) console.log(`ℹ Porta ${port} respondeu em 127.0.0.1 (ok se OpenClaw está rodando).`);
|
|
160
|
+
else console.log(`ℹ Porta ${port} não respondeu em 127.0.0.1 (ok se ainda não iniciou).`);
|
|
161
|
+
|
|
162
|
+
// --- 11. Hardening: recomendações para VPS ---
|
|
163
|
+
if (env === "linux-vps-root") {
|
|
164
|
+
console.log("\n🛡 Hardening (recomendado)");
|
|
165
|
+
console.log("- Crie um usuário não-root (ex: clawuser) e desative login por senha no SSH.");
|
|
166
|
+
console.log("- Ative firewall (UFW) e fail2ban.");
|
|
167
|
+
console.log("- Exponha publicamente apenas WireGuard (UDP) se usar VPN.");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// --- 12. Persistência da configuração (delegado para lib/config.writeJsonSafe) ---
|
|
171
|
+
if (needWrite) {
|
|
172
|
+
writeJsonSafe(configPath, config);
|
|
173
|
+
console.log("\n✔ openclaw.json atualizado/criado com segurança.");
|
|
174
|
+
} else {
|
|
175
|
+
console.log("\n✔ Nenhuma alteração necessária no openclaw.json.");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log("\n✅ Setup finalizado.");
|
|
179
|
+
console.log("Próximo passo: configurar VPN (WireGuard) e aplicar policies (skills/openclaw-ops).");
|
|
180
|
+
rl.close();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
main().catch((e) => {
|
|
184
|
+
console.error("✖ Erro:", e && e.message ? e.message : e);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fabioforest/openclaw",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "CLI e starter kit para configuração segura do OpenClaw em VPS, Mac, Windows e Docker",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"main": "bin/openclaw.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"openclaw": "./bin/openclaw.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/",
|
|
14
|
+
"lib/",
|
|
15
|
+
"templates/"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"test:coverage": "vitest run --coverage",
|
|
21
|
+
"setup": "node lib/setup/config_wizard.js",
|
|
22
|
+
"docker:build": "docker build -t openclaw -f docker/Dockerfile ."
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"openclaw",
|
|
26
|
+
"agent",
|
|
27
|
+
"security",
|
|
28
|
+
"devops",
|
|
29
|
+
"cli",
|
|
30
|
+
"wireguard",
|
|
31
|
+
"vpn",
|
|
32
|
+
"sysadmin"
|
|
33
|
+
],
|
|
34
|
+
"author": "Fabio Figueiredo",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/fabioffigueiredo/openclaw-agents"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
42
|
+
"vitest": "^4.0.18"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup-specialist
|
|
3
|
+
role: Especialista em Instalação e Troubleshooting
|
|
4
|
+
description: Agente focado em resolver problemas de ambiente, rede e dependências durante a instalação do OpenClaw.
|
|
5
|
+
skills:
|
|
6
|
+
- openclaw-installation-debugger
|
|
7
|
+
personality:
|
|
8
|
+
tone: Técnico, direto e solícito.
|
|
9
|
+
style: Prioriza a resolução de erros antes de qualquer outra tarefa.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Setup Specialist
|
|
13
|
+
|
|
14
|
+
Você é o especialista designado para garantir que o OpenClaw seja instalado e configurado corretamente. Sua prioridade máxima é identificar e corrigir bloqueios.
|
|
15
|
+
|
|
16
|
+
## Responsabilidades
|
|
17
|
+
1. **Diagnosticar**: Usar a skill `openclaw-installation-debugger` para verificar rede, permissões e versões.
|
|
18
|
+
2. **Corrigir**: Sugerir comandos exatos para resolver problemas (ex: instalar Docker, configurar proxy npm, corrigir permissões).
|
|
19
|
+
3. **Verificar**: Após a correção, rodar novamente o diagnóstico para confirmar o sucesso.
|
|
20
|
+
|
|
21
|
+
## Instruções
|
|
22
|
+
- Se o usuário relatar "erro na instalação", execute imediatamente o script `debug.js` da skill `openclaw-installation-debugger`.
|
|
23
|
+
- Analise a saída do script. Se a rede falhar, verifique proxy/firewall. Se permisão falhar, sugira `sudo` ou `chown`.
|
|
24
|
+
- Não tente configurar VPN ou Policies até que o ambiente base esteja saudável (Node, NPM, Docker, Rede).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sysadmin-proativo
|
|
3
|
+
description: Operador padrão (seguro) para manter OpenClaw saudável e auditável.
|
|
4
|
+
model: default
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Persona
|
|
8
|
+
Você é um SysAdmin proativo e cauteloso. Sua prioridade é **segurança, auditabilidade e estabilidade**.
|
|
9
|
+
|
|
10
|
+
## Modo de Operação
|
|
11
|
+
- Trabalhe em passos curtos e verificáveis.
|
|
12
|
+
- Prefira runbooks e skills catalogadas.
|
|
13
|
+
- Peça confirmação antes de ações arriscadas ou irreversíveis.
|
|
14
|
+
- Nunca execute comandos fora do escopo do runbook ativo.
|
|
15
|
+
|
|
16
|
+
## Tools Permitidas
|
|
17
|
+
- `run_command` — apenas para comandos listados nos runbooks ou allowlist
|
|
18
|
+
- `read_file` / `write_file` — dentro dos diretórios autorizados
|
|
19
|
+
- `list_dir` — sem restrição
|
|
20
|
+
- `view_file` — sem restrição
|
|
21
|
+
|
|
22
|
+
## Limites
|
|
23
|
+
- **Timeout por comando**: 30s (padrão), 120s (operações longas com `--long`)
|
|
24
|
+
- **Retry máximo**: 3 tentativas antes de escalar para humano
|
|
25
|
+
- **Circuit breaker**: Suspende execuções se 2+ falhas consecutivas
|
|
26
|
+
- **Break-glass**: Requer aprovação explícita + auditoria completa
|
|
27
|
+
|
|
28
|
+
## Comandos Bloqueados (ver hooks/pre-tool-use.js)
|
|
29
|
+
- `rm -rf /`, `mkfs`, `dd if=`, `shutdown`, `reboot`
|
|
30
|
+
- Qualquer comando com `> /dev/sda` ou pipe para dispositivos de bloco
|
|
31
|
+
- `chmod 777`, `chown root` sem aprovação
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook PreToolUse — bloqueia comandos destrutivos antes da execução.
|
|
6
|
+
*
|
|
7
|
+
* Este hook é chamado pelo agente antes de executar qualquer tool.
|
|
8
|
+
* Se o comando for considerado perigoso, retorna { blocked: true, reason }.
|
|
9
|
+
*
|
|
10
|
+
* Integra-se com a policy de break-glass: se o break-glass estiver ativo
|
|
11
|
+
* e aprovado, o comando é liberado com auditoria completa.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Padrões de comandos destrutivos que requerem bloqueio
|
|
15
|
+
const BLOCKED_PATTERNS = [
|
|
16
|
+
{ pattern: /rm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+)?\/(\s|$)/, reason: "rm -rf / detectado — remoção da raiz bloqueada" },
|
|
17
|
+
{ pattern: /rm\s+-[a-zA-Z]*r[a-zA-Z]*f/, reason: "rm recursivo com force — requer aprovação" },
|
|
18
|
+
{ pattern: /mkfs/, reason: "mkfs — formatação de disco bloqueada" },
|
|
19
|
+
{ pattern: /dd\s+if=/, reason: "dd if= — escrita direta em dispositivo bloqueada" },
|
|
20
|
+
{ pattern: /shutdown/, reason: "shutdown — desligamento requer aprovação humana" },
|
|
21
|
+
{ pattern: /reboot/, reason: "reboot — reinicialização requer aprovação humana" },
|
|
22
|
+
{ pattern: />\s*\/dev\/[sh]d[a-z]/, reason: "Redirecionamento para dispositivo de bloco bloqueado" },
|
|
23
|
+
{ pattern: /chmod\s+777/, reason: "chmod 777 — permissão aberta demais, requer aprovação" },
|
|
24
|
+
{ pattern: /chown\s+root/, reason: "chown root — mudança de proprietário para root requer aprovação" },
|
|
25
|
+
{ pattern: /:(){ :\|:& };:/, reason: "Fork bomb detectada — execução bloqueada" },
|
|
26
|
+
{ pattern: /curl\s+.*\|\s*(ba)?sh/, reason: "Pipe de URL para shell — execução remota bloqueada" },
|
|
27
|
+
{ pattern: /wget\s+.*\|\s*(ba)?sh/, reason: "Pipe de URL para shell — execução remota bloqueada" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// Lista de diretórios protegidos (não podem ser alvo de write/delete)
|
|
31
|
+
const PROTECTED_PATHS = [
|
|
32
|
+
"/etc/passwd",
|
|
33
|
+
"/etc/shadow",
|
|
34
|
+
"/etc/sudoers",
|
|
35
|
+
"/boot",
|
|
36
|
+
"/usr/bin",
|
|
37
|
+
"/usr/sbin",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Verifica se um comando deve ser bloqueado.
|
|
42
|
+
* @param {string} command — o comando a ser verificado
|
|
43
|
+
* @param {object} [options] — opções adicionais
|
|
44
|
+
* @param {boolean} [options.breakGlassActive] — se break-glass está ativo
|
|
45
|
+
* @returns {{ blocked: boolean, reason?: string, requiresApproval?: boolean }}
|
|
46
|
+
*/
|
|
47
|
+
function checkCommand(command, options = {}) {
|
|
48
|
+
if (!command || typeof command !== "string") {
|
|
49
|
+
return { blocked: false };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const normalized = command.trim().toLowerCase();
|
|
53
|
+
|
|
54
|
+
// Verifica padrões destrutivos
|
|
55
|
+
for (const { pattern, reason } of BLOCKED_PATTERNS) {
|
|
56
|
+
if (pattern.test(command)) {
|
|
57
|
+
// Se break-glass ativo, permite mas marca como requiring approval
|
|
58
|
+
if (options.breakGlassActive) {
|
|
59
|
+
return {
|
|
60
|
+
blocked: false,
|
|
61
|
+
requiresApproval: true,
|
|
62
|
+
reason: `[BREAK-GLASS] ${reason} — liberado com auditoria`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { blocked: true, reason };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Verifica caminhos protegidos
|
|
70
|
+
for (const protectedPath of PROTECTED_PATHS) {
|
|
71
|
+
if (command.includes(protectedPath) && /write|delete|rm|mv|cp.*>/.test(normalized)) {
|
|
72
|
+
return {
|
|
73
|
+
blocked: true,
|
|
74
|
+
reason: `Operação em caminho protegido: ${protectedPath}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { blocked: false };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Verifica se um caminho de arquivo é seguro para operações de escrita.
|
|
84
|
+
* @param {string} filePath — caminho do arquivo
|
|
85
|
+
* @returns {{ allowed: boolean, reason?: string }}
|
|
86
|
+
*/
|
|
87
|
+
function checkFilePath(filePath) {
|
|
88
|
+
if (!filePath || typeof filePath !== "string") {
|
|
89
|
+
return { allowed: true };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const protectedPath of PROTECTED_PATHS) {
|
|
93
|
+
if (filePath.startsWith(protectedPath)) {
|
|
94
|
+
return {
|
|
95
|
+
allowed: false,
|
|
96
|
+
reason: `Escrita em caminho protegido bloqueada: ${protectedPath}`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { allowed: true };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
checkCommand,
|
|
106
|
+
checkFilePath,
|
|
107
|
+
BLOCKED_PATTERNS,
|
|
108
|
+
PROTECTED_PATHS,
|
|
109
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Guardrails de Segurança (OpenClaw OS)
|
|
2
|
+
- bind localhost (127.0.0.1) por padrão
|
|
3
|
+
- auth token obrigatório
|
|
4
|
+
- VPN-first para acesso remoto
|
|
5
|
+
- bloquear comandos destrutivos (rm -rf, mkfs, dd if=, shutdown/reboot) sem aprovação
|
|
6
|
+
|
|
7
|
+
Este arquivo é uma referência de políticas para sua integração em hooks (VS Code/Cursor) ou no Policy Engine do OpenClaw core.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openclaw-installation-debugger
|
|
3
|
+
description: Diagnóstico profundo de falhas na instalação, conectividade e integridade do OpenClaw. Verifica rede (NPM/GitHub), proxy, versões e integridade de arquivos.
|
|
4
|
+
version: 1.0
|
|
5
|
+
author: Fabioforest
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# OpenClaw Installation Debugger
|
|
9
|
+
|
|
10
|
+
Esta skill fornece ferramentas avançadas para diagnosticar por que uma instalação falhou ou por que o agente não está operando corretamente.
|
|
11
|
+
|
|
12
|
+
## Scripts Disponíveis
|
|
13
|
+
|
|
14
|
+
### `debug.js`
|
|
15
|
+
Executa uma bateria de testes detalhados e gera um relatório JSON/Texto.
|
|
16
|
+
|
|
17
|
+
**Verificações:**
|
|
18
|
+
1. **Ambiente**: SO, Node.js version, NPM version, Docker (se disponível).
|
|
19
|
+
2. **Rede**:
|
|
20
|
+
- Ping para `registry.npmjs.org` (verifica bloqueio de firewall/proxy).
|
|
21
|
+
- Ping para `github.com`.
|
|
22
|
+
- Resolução DNS.
|
|
23
|
+
3. **Instalação**:
|
|
24
|
+
- Integridade da pasta `.agent/` (arquivos esperados vs encontrados).
|
|
25
|
+
- Permissões de escrita no diretório atual.
|
|
26
|
+
- Validade do `openclaw.json` (se existir).
|
|
27
|
+
|
|
28
|
+
## Como usar
|
|
29
|
+
Execute via CLI (se disponível):
|
|
30
|
+
```bash
|
|
31
|
+
npx @fabioforest/openclaw debug
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Ou diretamente via node se estiver debugando o pacote local:
|
|
35
|
+
```bash
|
|
36
|
+
node templates/.agent/skills/openclaw-installation-debugger/scripts/debug.js
|
|
37
|
+
```
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const dns = require("dns").promises;
|
|
7
|
+
const https = require("https");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Utilitário de log colorido
|
|
12
|
+
*/
|
|
13
|
+
const colors = {
|
|
14
|
+
reset: "\x1b[0m",
|
|
15
|
+
red: "\x1b[31m",
|
|
16
|
+
green: "\x1b[32m",
|
|
17
|
+
yellow: "\x1b[33m",
|
|
18
|
+
blue: "\x1b[34m",
|
|
19
|
+
bold: "\x1b[1m"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function log(msg, color = colors.reset) {
|
|
23
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function success(msg) { log(`✅ ${msg}`, colors.green); }
|
|
27
|
+
function warn(msg) { log(`⚠️ ${msg}`, colors.yellow); }
|
|
28
|
+
function fail(msg) { log(`❌ ${msg}`, colors.red); }
|
|
29
|
+
function info(msg) { log(`ℹ️ ${msg}`, colors.blue); }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Verifica conectividade HTTP
|
|
33
|
+
*/
|
|
34
|
+
function checkHttp(url) {
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
const req = https.get(url, { timeout: 5000 }, (res) => {
|
|
37
|
+
if (res.statusCode >= 200 && res.statusCode < 400) {
|
|
38
|
+
resolve({ ok: true, status: res.statusCode });
|
|
39
|
+
} else {
|
|
40
|
+
resolve({ ok: false, status: res.statusCode });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
req.on("error", (err) => resolve({ ok: false, error: err.message }));
|
|
44
|
+
req.on("timeout", () => { req.destroy(); resolve({ ok: false, error: "timeout" }); });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function runDebug() {
|
|
49
|
+
log("\n🔍 OpenClaw Installation Debugger\n", colors.bold);
|
|
50
|
+
|
|
51
|
+
const report = {
|
|
52
|
+
timestamp: new Date().toISOString(),
|
|
53
|
+
system: {},
|
|
54
|
+
network: {},
|
|
55
|
+
installation: {}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// 1. Sistema
|
|
59
|
+
info("Checando sistema...");
|
|
60
|
+
try {
|
|
61
|
+
report.system.platform = os.platform();
|
|
62
|
+
report.system.release = os.release();
|
|
63
|
+
report.system.arch = os.arch();
|
|
64
|
+
report.system.node = process.version;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
report.system.npm = execSync("npm -v").toString().trim();
|
|
68
|
+
} catch (e) { report.system.npm = "não encontrado"; }
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
execSync("docker -v", { stdio: "ignore" });
|
|
72
|
+
report.system.docker = "instalado";
|
|
73
|
+
} catch (e) { report.system.docker = "não encontrado"; }
|
|
74
|
+
|
|
75
|
+
success(`Node: ${report.system.node}, NPM: ${report.system.npm}, OS: ${report.system.platform}`);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
fail(`Erro ao ler sistema: ${err.message}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 2. Rede
|
|
81
|
+
info("\nChecando rede...");
|
|
82
|
+
|
|
83
|
+
// DNS
|
|
84
|
+
try {
|
|
85
|
+
await dns.lookup("google.com");
|
|
86
|
+
success("DNS Resolution: OK");
|
|
87
|
+
report.network.dns = "ok";
|
|
88
|
+
} catch (err) {
|
|
89
|
+
fail(`DNS Resolution falhou: ${err.message}`);
|
|
90
|
+
report.network.dns = "falhou";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// NPM Registry
|
|
94
|
+
const npmCheck = await checkHttp("https://registry.npmjs.org/");
|
|
95
|
+
if (npmCheck.ok) {
|
|
96
|
+
success("NPM Registry (https): OK");
|
|
97
|
+
report.network.npm = "ok";
|
|
98
|
+
} else {
|
|
99
|
+
fail(`NPM Registry inacessível: ${npmCheck.error || npmCheck.status}`);
|
|
100
|
+
report.network.npm = "falhou";
|
|
101
|
+
warn(" -> Verifique se há proxy ou firewall bloqueando o NPM.");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// GitHub
|
|
105
|
+
const githubCheck = await checkHttp("https://github.com/");
|
|
106
|
+
if (githubCheck.ok) {
|
|
107
|
+
success("GitHub (https): OK");
|
|
108
|
+
report.network.github = "ok";
|
|
109
|
+
} else {
|
|
110
|
+
fail(`GitHub inacessível: ${githubCheck.error || githubCheck.status}`);
|
|
111
|
+
report.network.github = "falhou";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 3. Instalação Local
|
|
115
|
+
info("\nChecando instalação local (.agent/)...");
|
|
116
|
+
const agentDir = path.resolve(".agent");
|
|
117
|
+
if (fs.existsSync(agentDir)) {
|
|
118
|
+
success(".agent/ encontrado.");
|
|
119
|
+
report.installation.found = true;
|
|
120
|
+
|
|
121
|
+
// Verificar permissões de escrita
|
|
122
|
+
try {
|
|
123
|
+
const testFile = path.join(agentDir, ".perm_check");
|
|
124
|
+
fs.writeFileSync(testFile, "ok");
|
|
125
|
+
fs.unlinkSync(testFile);
|
|
126
|
+
success("Permissão de escrita em .agent/: OK");
|
|
127
|
+
report.installation.write_perm = "ok";
|
|
128
|
+
} catch (err) {
|
|
129
|
+
fail(`Sem permissão de escrita em .agent/: ${err.message}`);
|
|
130
|
+
report.installation.write_perm = "falhou";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
} else {
|
|
134
|
+
warn(".agent/ NÃO encontrado no diretório atual.");
|
|
135
|
+
report.installation.found = false;
|
|
136
|
+
info(" -> Execute 'npx @fabioforest/openclaw init' para instalar.");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 4. Configuração Global (npm)
|
|
140
|
+
info("\nChecando configuração global do NPM...");
|
|
141
|
+
try {
|
|
142
|
+
const proxy = execSync("npm config get proxy").toString().trim();
|
|
143
|
+
const httpsProxy = execSync("npm config get https-proxy").toString().trim();
|
|
144
|
+
const registry = execSync("npm config get registry").toString().trim();
|
|
145
|
+
|
|
146
|
+
if (proxy !== "null") warn(`Proxy HTTP configurado: ${proxy}`);
|
|
147
|
+
if (httpsProxy !== "null") warn(`Proxy HTTPS configurado: ${httpsProxy}`);
|
|
148
|
+
success(`Registry atual: ${registry}`);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
warn("Não foi possível ler configurações do NPM.");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
log("\n🏁 Debug concluído.", colors.bold);
|
|
154
|
+
return report;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Executar se chamado diretamente
|
|
158
|
+
if (require.main === module) {
|
|
159
|
+
runDebug().catch(err => {
|
|
160
|
+
console.error("Erro fatal no debugger:", err);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = { runDebug };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openclaw-vpn-wireguard
|
|
3
|
+
description: Provisiona WireGuard entre VPS e hosts para que o OpenClaw opere somente via rede privada. Inclui hardening, checklist e validação.
|
|
4
|
+
version: 1.0
|
|
5
|
+
---
|
|
6
|
+
# Objetivo
|
|
7
|
+
Criar VPN WireGuard (VPN-first). Sem VPN, sem acesso remoto.
|
|
8
|
+
|
|
9
|
+
# Checklist (alto nível)
|
|
10
|
+
- Instalar WireGuard (VPS e host)
|
|
11
|
+
- Gerar chaves por host (nunca reutilizar)
|
|
12
|
+
- Configurar wg0 na VPS (10.60.0.1/24)
|
|
13
|
+
- Adicionar peers com AllowedIPs /32
|
|
14
|
+
- Validar handshake e ping dentro da VPN
|
|
15
|
+
- Definir procedimento de revogação (remover peer)
|
|
16
|
+
|
|
17
|
+
# Hardening
|
|
18
|
+
- Expor publicamente apenas UDP do WireGuard
|
|
19
|
+
- Não usar 0.0.0.0/0 por padrão
|
|
20
|
+
- Registrar auditoria de add/remove peer
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openclaw-enroll-host
|
|
3
|
+
description: Onboarding seguro de host na malha WireGuard + OpenClaw com aprovação humana, identidade e revogação rápida.
|
|
4
|
+
version: 1.0
|
|
5
|
+
---
|
|
6
|
+
# Objetivo
|
|
7
|
+
Registrar host com aprovação e aplicar policy baseline.
|
|
8
|
+
|
|
9
|
+
# Checklist
|
|
10
|
+
- Gerar host_id + fingerprint
|
|
11
|
+
- Registrar host (pending)
|
|
12
|
+
- Aprovar manualmente
|
|
13
|
+
- Aplicar policy baseline
|
|
14
|
+
- Testar revogação (remover peer WireGuard)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openclaw-policy-baseline
|
|
3
|
+
description: Define RBAC e allowlists (deny-by-default) para execução remota via VPN com break-glass expirável.
|
|
4
|
+
version: 1.0
|
|
5
|
+
---
|
|
6
|
+
# Objetivo
|
|
7
|
+
Políticas antes do poder. Deny-by-default.
|
|
8
|
+
|
|
9
|
+
# Perfis
|
|
10
|
+
- viewer: leitura
|
|
11
|
+
- operator: runbooks permitidos
|
|
12
|
+
- admin: ações elevadas com confirmação extra e auditoria
|
|
13
|
+
|
|
14
|
+
# Break-glass
|
|
15
|
+
- janela curta
|
|
16
|
+
- expiração automática
|
|
17
|
+
- alerta e auditoria
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openclaw-remote-exec-runbooks
|
|
3
|
+
description: Estrutura execução remota via VPN usando runbooks idempotentes, com timeout, cancel e trilha auditável.
|
|
4
|
+
version: 1.0
|
|
5
|
+
---
|
|
6
|
+
# Objetivo
|
|
7
|
+
Evitar 'shell solto'. Preferir runbooks nomeados.
|
|
8
|
+
|
|
9
|
+
# Regras
|
|
10
|
+
- validar entradas
|
|
11
|
+
- capturar stdout/stderr
|
|
12
|
+
- timeout/cancel
|
|
13
|
+
- assinar request (request_id)
|