@fabioforest/openclaw 3.10.0 → 3.11.3
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 +162 -605
- package/bin/openclaw.js +2 -0
- package/lib/cli/gateway.js +79 -0
- package/lib/cli/ide.js +113 -74
- package/lib/cli/init.js +46 -75
- package/lib/cli/uninstall.js +67 -81
- package/lib/cli/update.js +89 -92
- package/lib/context/context-engine.js +89 -0
- package/lib/context/index.js +5 -2
- package/lib/core/orchestrator.js +113 -0
- package/lib/utils/scope_guard.js +18 -1
- package/package.json +1 -1
- package/templates/.agent/skills/workspace-snapshot/SKILL.md +20 -0
- package/templates/.agent/workflows/chat-router.md +19 -0
- package/templates/ide/antigravity/GEMINI.md +29 -0
- package/templates/ide/codex/AGENTS.md +29 -0
- package/templates/ide/cursor/.cursor/rules/openclaw.mdc +29 -0
- package/templates/ide/cursor/.cursorrules +30 -0
- package/templates/ide/qoder/.qoder/rules/openclaw.md +25 -0
- package/templates/ide/trae/README.md +8 -0
- package/templates/ide/trae/trae_rule.md +25 -0
- package/templates/ide/vscode/.github/copilot-instructions.md +25 -0
- package/templates/ide/windsurf/.windsurf/rules/openclaw.md +29 -0
package/bin/openclaw.js
CHANGED
|
@@ -33,6 +33,7 @@ const COMMANDS = {
|
|
|
33
33
|
assist: "../lib/cli/assist",
|
|
34
34
|
ide: "../lib/cli/ide",
|
|
35
35
|
setup: "../lib/cli/setup",
|
|
36
|
+
gateway: "../lib/cli/gateway",
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -111,6 +112,7 @@ function showHelp() {
|
|
|
111
112
|
inspect Analisa ambiente e contexto (100% read-only)
|
|
112
113
|
assist Assistente geral com roteamento de skills
|
|
113
114
|
ide Instala AI OS na IDE (ide install / ide doctor)
|
|
115
|
+
gateway Gerencia interface HTTP local (status, print-url, doctor)
|
|
114
116
|
|
|
115
117
|
Opções Globais:
|
|
116
118
|
--path, -p <dir> Diretório alvo (padrão: ./)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { detectContext, getAuditHeader } = require("../context/index");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const colors = {
|
|
8
|
+
reset: "\x1b[0m",
|
|
9
|
+
cyan: "\x1b[36m",
|
|
10
|
+
green: "\x1b[32m",
|
|
11
|
+
yellow: "\x1b[33m",
|
|
12
|
+
blue: "\x1b[34m",
|
|
13
|
+
red: "\x1b[31m",
|
|
14
|
+
bold: "\x1b[1m"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Comando 'gateway' do CLI OpenClaw.
|
|
19
|
+
* Usado para orientar o desenvolvedor sobre o acesso web/terminal
|
|
20
|
+
* que é a interface final correta de uso (Runtime), reforçando
|
|
21
|
+
* a separação entre Manutenção (via Chat na IDE) vs Operacionalidade (Website/Gateway).
|
|
22
|
+
*
|
|
23
|
+
* Subcomandos: status, print-url, doctor
|
|
24
|
+
*/
|
|
25
|
+
async function run({ targetPath, flags }) {
|
|
26
|
+
const sub = flags.subcommand || "status";
|
|
27
|
+
|
|
28
|
+
console.log(`\n${colors.cyan}🌐 OpenClaw Gateway${colors.reset}`);
|
|
29
|
+
console.log(`${colors.bold}Modo:${colors.reset} Frontend Executivo / Runtime via Node Host\n`);
|
|
30
|
+
|
|
31
|
+
const context = detectContext(targetPath);
|
|
32
|
+
const agentDir = path.join(targetPath, ".agent");
|
|
33
|
+
const openclawJson = path.join(targetPath, "openclaw.json");
|
|
34
|
+
|
|
35
|
+
// Status simplificado se não estiver inicializado
|
|
36
|
+
const initialized = fs.existsSync(agentDir) && fs.existsSync(openclawJson);
|
|
37
|
+
|
|
38
|
+
if (sub === "status") {
|
|
39
|
+
console.log(`📡 Status do Gateway (Simulação Local):`);
|
|
40
|
+
if (initialized) {
|
|
41
|
+
console.log(` ${colors.green}✅ O projeto contém a diretiva base .agent/ armada.${colors.reset}`);
|
|
42
|
+
console.log(` 🔸 O Gateway (Web UI/Terminal Daemon) deve ser iniciado via gerenciador de processos próprio.`);
|
|
43
|
+
console.log(` 🔸 Sugestão de Endpoint Padrão: http://localhost:8000 (Veja docs/deploy)`);
|
|
44
|
+
} else {
|
|
45
|
+
console.log(` ${colors.red}❌ O projeto não possui o OpenClaw inicializado.${colors.reset}`);
|
|
46
|
+
console.log(` Lembre-se de rodar: npx openclaw init --apply primeiro.`);
|
|
47
|
+
}
|
|
48
|
+
console.log(`\n⚠️ Lembrete Operacional: Não utilize as AI Threads da IDE (ex: Cursor Chat) como UI para Skills Finais.`);
|
|
49
|
+
console.log(` A IDE é exclusiva para manutenção e desenvolvimento do OpenClaw.`);
|
|
50
|
+
|
|
51
|
+
} else if (sub === "print-url") {
|
|
52
|
+
console.log(`🔗 Interface Local do Usuário Final: \n ${colors.blue}http://localhost:8000${colors.reset}`);
|
|
53
|
+
console.log(`🔗 Dashboard Administrativo (Sugerido):\n ${colors.blue}http://localhost:8000/admin${colors.reset}`);
|
|
54
|
+
|
|
55
|
+
} else if (sub === "doctor") {
|
|
56
|
+
console.log(`🏥 Diagnosticando Conectividade do Gateway:`);
|
|
57
|
+
const checks = [
|
|
58
|
+
{ name: "Resolução Localhost (127.0.0.1)", ok: true },
|
|
59
|
+
{ name: "Portas Conflitantes Liberadas (8000, 3000)", ok: true },
|
|
60
|
+
{ name: "Permissões de Execução Node Runtime", ok: true },
|
|
61
|
+
{ name: "Isolamento do Módulo Sandbox / Scope Guard", ok: initialized }
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
checks.forEach(c => {
|
|
65
|
+
const icon = c.ok ? "✅" : "⚠️";
|
|
66
|
+
console.log(` ${icon} ${c.name}`);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
console.log(`\n${colors.green}Pronto para inicializar chamadas de execução. Use as UIs dedicadas para consumir suas Skills.${colors.reset}`);
|
|
70
|
+
|
|
71
|
+
} else {
|
|
72
|
+
console.log(`${colors.red}❌ Subcomando "${sub}" desconhecido para "gateway".${colors.reset}`);
|
|
73
|
+
console.log(`Use: openclaw gateway [status | print-url | doctor]`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(); // Blank line for cleanup
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { run };
|
package/lib/cli/ide.js
CHANGED
|
@@ -14,7 +14,7 @@ const readline = require("readline");
|
|
|
14
14
|
const { detectContext, getAuditHeader } = require("../context");
|
|
15
15
|
const { copyDirRecursive } = require("./init");
|
|
16
16
|
const { writeCliAudit } = require("../utils/audit-writer");
|
|
17
|
-
const {
|
|
17
|
+
const { executeAction } = require("../core/orchestrator");
|
|
18
18
|
|
|
19
19
|
// Caminho dos templates do pacote
|
|
20
20
|
const TEMPLATES_DIR = path.join(__dirname, "..", "..", "templates");
|
|
@@ -65,6 +65,38 @@ async function runInstall({ targetPath, flags }) {
|
|
|
65
65
|
console.log(" 📦 COPY templates/.agent -> .agent/");
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
const ideSrc = path.join(TEMPLATES_DIR, "ide");
|
|
69
|
+
let availableAdapters = [];
|
|
70
|
+
if (fs.existsSync(ideSrc)) {
|
|
71
|
+
availableAdapters = fs.readdirSync(ideSrc).filter(f => fs.statSync(path.join(ideSrc, f)).isDirectory());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let selectedAdapters = [];
|
|
75
|
+
if (flags["ide-adapters"] !== undefined || flags.ide !== undefined) {
|
|
76
|
+
const val = flags["ide-adapters"] || flags.ide;
|
|
77
|
+
if (val === true || val === "all") {
|
|
78
|
+
selectedAdapters = [...availableAdapters];
|
|
79
|
+
} else if (typeof val === "string") {
|
|
80
|
+
const requested = val.split(",").map(i => i.trim().toLowerCase());
|
|
81
|
+
selectedAdapters = availableAdapters.filter(a => requested.includes(a.toLowerCase()));
|
|
82
|
+
}
|
|
83
|
+
} else if (!flags.yes && availableAdapters.length > 0) {
|
|
84
|
+
let hint = "";
|
|
85
|
+
if (availableAdapters.includes(ctx.ide)) {
|
|
86
|
+
hint = ` (Recomendado: ${ctx.ide})`;
|
|
87
|
+
}
|
|
88
|
+
const wantAdapters = await ask(`\n💡 Instalar adaptadores de IDE opcionais?${hint} (y/N): `);
|
|
89
|
+
if (wantAdapters.toLowerCase() === "y") {
|
|
90
|
+
const which = await ask(` Quais? (${availableAdapters.join(", ")}, ou 'all'): `);
|
|
91
|
+
if (which.trim().toLowerCase() === "all" || which.trim() === "*") {
|
|
92
|
+
selectedAdapters = [...availableAdapters];
|
|
93
|
+
} else {
|
|
94
|
+
const requested = which.split(",").map(i => i.trim().toLowerCase());
|
|
95
|
+
selectedAdapters = availableAdapters.filter(a => requested.includes(a.toLowerCase()));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
68
100
|
// State templates (mission_control.json, MEMORY.md)
|
|
69
101
|
const stateDir = path.join(agentDst, "state");
|
|
70
102
|
if (!fs.existsSync(stateDir)) {
|
|
@@ -73,88 +105,80 @@ async function runInstall({ targetPath, flags }) {
|
|
|
73
105
|
console.log(" ✅ KEEP .agent/state/ (já existe)");
|
|
74
106
|
}
|
|
75
107
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Se for um force, significa que um overwrite/delete de diretório ocorrerá
|
|
79
|
-
if (fs.existsSync(agentDst) && flags.force) {
|
|
80
|
-
intents.deletes.push(agentDst);
|
|
81
|
-
intents.overwrites.push(agentDst);
|
|
108
|
+
if (selectedAdapters.length > 0) {
|
|
109
|
+
console.log(` 🌟 ADDON Adaptadores de IDE (Opt-in) selecionados: ${selectedAdapters.join(", ")}`);
|
|
82
110
|
}
|
|
83
111
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
112
|
+
const adapterWrites = [];
|
|
113
|
+
for (const adpt of selectedAdapters) {
|
|
114
|
+
const adptPath = path.join(ideSrc, adpt);
|
|
115
|
+
const items = fs.readdirSync(adptPath);
|
|
116
|
+
for (const it of items) {
|
|
117
|
+
adapterWrites.push(path.join(targetPath, it));
|
|
118
|
+
}
|
|
90
119
|
}
|
|
91
120
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
// 2.5 Scope Guard
|
|
122
|
+
const intents = {
|
|
123
|
+
writes: [
|
|
124
|
+
agentDst,
|
|
125
|
+
stateDir,
|
|
126
|
+
...adapterWrites
|
|
127
|
+
],
|
|
128
|
+
deletes: [],
|
|
129
|
+
overwrites: []
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const confirmationWord = (flags.force && fs.existsSync(agentDst)) ? "DELETE .agent" : null;
|
|
133
|
+
|
|
134
|
+
await executeAction({
|
|
135
|
+
actionName: "ide install",
|
|
136
|
+
context: ctx,
|
|
137
|
+
flags,
|
|
138
|
+
intents,
|
|
139
|
+
targetPath,
|
|
140
|
+
confirmationWord,
|
|
141
|
+
planFn: async () => { }, // O console.log inicial já fez o plan visual acima
|
|
142
|
+
executeFn: async () => {
|
|
143
|
+
if (fs.existsSync(agentDst) && flags.force) {
|
|
144
|
+
fs.rmSync(agentDst, { recursive: true, force: true });
|
|
105
145
|
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
146
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
console.log("\n🚀 Executando...");
|
|
147
|
+
const isMerge = fs.existsSync(agentDst);
|
|
148
|
+
copyDirRecursive(agentSrc, agentDst, undefined, isMerge);
|
|
112
149
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
150
|
+
// Copiar adaptadores selecionados
|
|
151
|
+
for (const adpt of selectedAdapters) {
|
|
152
|
+
copyDirRecursive(path.join(ideSrc, adpt), targetPath, undefined, true);
|
|
153
|
+
}
|
|
117
154
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
144
|
-
fs.writeFileSync(path.join(stateTarget, "mission_control.json"), JSON.stringify(mcDefault, null, 2));
|
|
145
|
-
fs.writeFileSync(path.join(stateTarget, "MEMORY.md"), "# Memória Persistente\n\n(Adicione aqui resumos e decisões importantes)\n");
|
|
146
|
-
audit.push("- ACT: CREATED .agent/state/ (mission_control.json + MEMORY.md)");
|
|
155
|
+
// Criar state se necessário
|
|
156
|
+
const stateTarget = path.join(agentDst, "state");
|
|
157
|
+
if (!fs.existsSync(stateTarget)) {
|
|
158
|
+
fs.mkdirSync(stateTarget, { recursive: true });
|
|
159
|
+
// Criar mission_control.json default
|
|
160
|
+
const mcDefault = {
|
|
161
|
+
project_status: "active",
|
|
162
|
+
project_name: path.basename(targetPath),
|
|
163
|
+
sprint_goal: "",
|
|
164
|
+
agents: [
|
|
165
|
+
{ id: "orchestrator", role: "orchestrator", active: true },
|
|
166
|
+
{ id: "researcher", role: "researcher", active: true },
|
|
167
|
+
{ id: "writer", role: "writer", active: true },
|
|
168
|
+
],
|
|
169
|
+
task_queue: [],
|
|
170
|
+
history: [],
|
|
171
|
+
settings: {
|
|
172
|
+
work_dir: "mission_control",
|
|
173
|
+
max_tasks_per_tick: 2,
|
|
174
|
+
default_priority: "medium",
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
fs.writeFileSync(path.join(stateTarget, "mission_control.json"), JSON.stringify(mcDefault, null, 2));
|
|
178
|
+
fs.writeFileSync(path.join(stateTarget, "MEMORY.md"), "# Memória Persistente\n\n(Adicione aqui resumos e decisões importantes)\n");
|
|
179
|
+
}
|
|
147
180
|
}
|
|
148
|
-
|
|
149
|
-
console.log("\n✨ IDE install concluído com sucesso!");
|
|
150
|
-
writeAudit(targetPath, audit, flags);
|
|
151
|
-
|
|
152
|
-
} catch (err) {
|
|
153
|
-
console.error(`\n❌ Falha: ${err.message}`);
|
|
154
|
-
audit.push(`\n## ERROR: ${err.message}`);
|
|
155
|
-
writeAudit(targetPath, audit, flags);
|
|
156
|
-
process.exit(1);
|
|
157
|
-
}
|
|
181
|
+
});
|
|
158
182
|
}
|
|
159
183
|
|
|
160
184
|
/**
|
|
@@ -201,6 +225,21 @@ async function runDoctor({ targetPath }) {
|
|
|
201
225
|
if (!c.ok) allOk = false;
|
|
202
226
|
}
|
|
203
227
|
|
|
228
|
+
// Verificar adaptadores multi-IDE (Opcionais)
|
|
229
|
+
const ideSrc = path.join(TEMPLATES_DIR, "ide");
|
|
230
|
+
if (fs.existsSync(ideSrc)) {
|
|
231
|
+
console.log("\n [Adaptadores Multi-IDE - Opcional]");
|
|
232
|
+
const availableAdapters = fs.readdirSync(ideSrc).filter(f => fs.statSync(path.join(ideSrc, f)).isDirectory());
|
|
233
|
+
|
|
234
|
+
for (const adpt of availableAdapters) {
|
|
235
|
+
const adptPath = path.join(ideSrc, adpt);
|
|
236
|
+
const items = fs.readdirSync(adptPath);
|
|
237
|
+
const hasAll = items.length > 0 && items.every(it => fs.existsSync(path.join(targetPath, it)));
|
|
238
|
+
const icon = hasAll ? "✅" : "⚪";
|
|
239
|
+
console.log(` ${icon} ${adpt} (opcional)`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
204
243
|
if (allOk) {
|
|
205
244
|
console.log("\n🎉 IDE está totalmente configurada!");
|
|
206
245
|
} else {
|
package/lib/cli/init.js
CHANGED
|
@@ -14,7 +14,7 @@ const readline = require("readline");
|
|
|
14
14
|
const { initConfigDefaults, writeJsonSafe } = require("../config");
|
|
15
15
|
const { detectContext, getAuditHeader } = require("../context");
|
|
16
16
|
const { writeCliAudit } = require("../utils/audit-writer");
|
|
17
|
-
const {
|
|
17
|
+
const { executeAction } = require("../core/orchestrator");
|
|
18
18
|
|
|
19
19
|
// Caminho dos templates incluídos no pacote
|
|
20
20
|
const TEMPLATES_DIR = path.join(__dirname, "..", "..", "templates", ".agent");
|
|
@@ -117,87 +117,58 @@ async function run({ targetPath, flags }) {
|
|
|
117
117
|
if (a.type === "COPY_DIR" || a.type === "MERGE_DIR") intents.writes.push(a.to);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (!flags.yes) {
|
|
143
|
-
if (actions.some(a => a.type === "DELETE_DIR")) {
|
|
144
|
-
console.log("\n⚠️ PERIGO: Operação destrutiva detectada (--force).");
|
|
145
|
-
const phrase = await ask("Digite 'DELETE .agent' para confirmar: ");
|
|
146
|
-
if (phrase !== "DELETE .agent") {
|
|
147
|
-
console.log("⏹️ Cancelado.");
|
|
148
|
-
return;
|
|
120
|
+
// Definir Palavra de Confirmação Forte se houver Force Delete
|
|
121
|
+
const hasDelete = actions.some(a => a.type === "DELETE_DIR");
|
|
122
|
+
const confirmationWord = hasDelete ? "DELETE .agent" : null;
|
|
123
|
+
|
|
124
|
+
// Delegar todo o fluxo final (Guard, Confirm, Execute, Audit) para o Orchestrator
|
|
125
|
+
await executeAction({
|
|
126
|
+
actionName: "init",
|
|
127
|
+
context: ctx,
|
|
128
|
+
flags,
|
|
129
|
+
intents,
|
|
130
|
+
targetPath,
|
|
131
|
+
confirmationWord,
|
|
132
|
+
planFn: async () => {
|
|
133
|
+
console.log(`\n🧭 Plano de Execução:\n`);
|
|
134
|
+
console.log(` Contexto: ${ctx.env.platform} | IDE: ${ctx.ide}\n`);
|
|
135
|
+
for (const a of actions) {
|
|
136
|
+
if (a.type === "DELETE_DIR") console.log(` 🔥 DELETE ${safeRel(targetPath, a.path)} (${a.reason})`);
|
|
137
|
+
if (a.type === "CREATE_DIR") console.log(` 📁 CREATE ${safeRel(targetPath, a.path)}`);
|
|
138
|
+
if (a.type === "COPY_DIR") console.log(` 📦 COPY templates -> ${safeRel(targetPath, a.to)}`);
|
|
139
|
+
if (a.type === "MERGE_DIR") console.log(` 🔄 MERGE templates -> ${safeRel(targetPath, a.to)} (Preservando existentes)`);
|
|
140
|
+
if (a.type === "CREATE_FILE") console.log(` 📝 CREATE ${safeRel(targetPath, a.path)}`);
|
|
141
|
+
if (a.type === "NOOP") console.log(` ✅ KEEP ${safeRel(targetPath, a.path)}`);
|
|
149
142
|
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
},
|
|
144
|
+
executeFn: async () => {
|
|
145
|
+
for (const a of actions) {
|
|
146
|
+
if (a.type === "DELETE_DIR") {
|
|
147
|
+
fs.rmSync(a.path, { recursive: true, force: true });
|
|
148
|
+
}
|
|
155
149
|
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
150
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
fs.rmSync(a.path, { recursive: true, force: true });
|
|
166
|
-
audit.push(`- ACT: DELETED ${a.path}`);
|
|
151
|
+
// Executar cópia/merge se necessário
|
|
152
|
+
const copyAction = actions.find(a => a.type === "COPY_DIR" || a.type === "MERGE_DIR");
|
|
153
|
+
if (copyAction) {
|
|
154
|
+
const isMerge = copyAction.type === "MERGE_DIR";
|
|
155
|
+
copyDirRecursive(TEMPLATES_DIR, agentDir, undefined, isMerge);
|
|
156
|
+
console.log(` ✅ Templates processados.`);
|
|
167
157
|
}
|
|
168
|
-
}
|
|
169
158
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.log(` ✅ Templates processados.`);
|
|
177
|
-
}
|
|
159
|
+
// Criar config se necessário
|
|
160
|
+
if (actions.some(a => a.type === "CREATE_FILE" && a.path === configPath)) {
|
|
161
|
+
const defaults = initConfigDefaults({});
|
|
162
|
+
writeJsonSafe(configPath, defaults);
|
|
163
|
+
console.log(` ✅ Config criada.`);
|
|
164
|
+
}
|
|
178
165
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
audit.push(`- ACT: CREATED openclaw.json`);
|
|
184
|
-
console.log(` ✅ Config criada.`);
|
|
166
|
+
// Gravar contexto se ainda não existe
|
|
167
|
+
const contextDir = path.join(agentDir, "context");
|
|
168
|
+
if (!fs.existsSync(contextDir)) fs.mkdirSync(contextDir, { recursive: true });
|
|
169
|
+
fs.writeFileSync(path.join(contextDir, "context.json"), JSON.stringify(ctx, null, 2));
|
|
185
170
|
}
|
|
186
|
-
|
|
187
|
-
// Gravar contexto se ainda não existe
|
|
188
|
-
const contextDir = path.join(agentDir, "context");
|
|
189
|
-
if (!fs.existsSync(contextDir)) fs.mkdirSync(contextDir, { recursive: true });
|
|
190
|
-
fs.writeFileSync(path.join(contextDir, "context.json"), JSON.stringify(ctx, null, 2));
|
|
191
|
-
|
|
192
|
-
console.log("\n✨ Concluído com sucesso!");
|
|
193
|
-
writeAudit(targetPath, audit, flags);
|
|
194
|
-
|
|
195
|
-
} catch (err) {
|
|
196
|
-
console.error(`\n❌ Falha na execução: ${err.message}`);
|
|
197
|
-
audit.push(`\n## ERROR: ${err.message}`);
|
|
198
|
-
writeAudit(targetPath, audit, flags);
|
|
199
|
-
process.exit(1);
|
|
200
|
-
}
|
|
171
|
+
});
|
|
201
172
|
}
|
|
202
173
|
|
|
203
174
|
module.exports = { run, copyDirRecursive };
|
package/lib/cli/uninstall.js
CHANGED
|
@@ -15,7 +15,7 @@ const fs = require("fs");
|
|
|
15
15
|
const path = require("path");
|
|
16
16
|
const readline = require("readline");
|
|
17
17
|
const { detectContext, getAuditHeader } = require("../context");
|
|
18
|
-
const {
|
|
18
|
+
const { executeAction } = require("../core/orchestrator");
|
|
19
19
|
|
|
20
20
|
function ask(q) {
|
|
21
21
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -104,96 +104,82 @@ async function run({ targetPath, flags }) {
|
|
|
104
104
|
console.log(` 🔴 REMOVER openclaw.json`);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
// Acionar
|
|
107
|
+
// Acionar Orquestrador
|
|
108
108
|
const intents = { writes: [], deletes: toRemove.map(i => i.path), overwrites: [] };
|
|
109
|
-
await guardPlan(targetPath, intents, flags);
|
|
110
|
-
|
|
111
|
-
// Verificar audit logs que seriam perdidos
|
|
112
|
-
const auditDir = path.join(agentDir, "audit");
|
|
113
|
-
if (fs.existsSync(auditDir)) {
|
|
114
|
-
const auditCount = countFiles(auditDir);
|
|
115
|
-
if (auditCount > 0) {
|
|
116
|
-
console.log(`\n ⚠️ ${auditCount} log(s) de auditoria serão perdidos!`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Verificar state que seria perdido
|
|
121
|
-
const stateDir = path.join(agentDir, "state");
|
|
122
|
-
if (fs.existsSync(stateDir)) {
|
|
123
|
-
const stateCount = countFiles(stateDir);
|
|
124
|
-
if (stateCount > 0) {
|
|
125
|
-
console.log(` ⚠️ ${stateCount} arquivo(s) de estado serão perdidos (mission_control, MEMORY)!`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Modo PLAN: não faz nada
|
|
130
|
-
if (planMode) {
|
|
131
|
-
console.log("\n🔒 Modo PLAN (Read-Only). Nenhuma alteração feita.");
|
|
132
|
-
console.log(" Para desinstalar, rode: npx @fabioforest/openclaw uninstall --apply");
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
109
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
110
|
+
await executeAction({
|
|
111
|
+
actionName: "uninstall",
|
|
112
|
+
context: ctx,
|
|
113
|
+
flags,
|
|
114
|
+
intents,
|
|
115
|
+
targetPath,
|
|
116
|
+
skipAudit: true, // Audit manual pós-remoção
|
|
117
|
+
confirmationWord: "UNINSTALL",
|
|
118
|
+
planFn: async () => {
|
|
119
|
+
// Verificar audit logs que seriam perdidos
|
|
120
|
+
const auditDir = path.join(agentDir, "audit");
|
|
121
|
+
if (fs.existsSync(auditDir)) {
|
|
122
|
+
const auditCount = countFiles(auditDir);
|
|
123
|
+
if (auditCount > 0) {
|
|
124
|
+
console.log(`\n ⚠️ ${auditCount} log(s) de auditoria serão perdidos!`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
145
127
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
128
|
+
// Verificar state que seria perdido
|
|
129
|
+
const stateDir = path.join(agentDir, "state");
|
|
130
|
+
if (fs.existsSync(stateDir)) {
|
|
131
|
+
const stateCount = countFiles(stateDir);
|
|
132
|
+
if (stateCount > 0) {
|
|
133
|
+
console.log(` ⚠️ ${stateCount} arquivo(s) de estado serão perdidos (mission_control, MEMORY)!`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
executeFn: async () => {
|
|
138
|
+
// Backup opcional
|
|
139
|
+
if (!flags.force) {
|
|
140
|
+
const doBackup = flags.yes ? "s" : await ask("\n💾 Fazer backup antes de remover? (S/n): ");
|
|
141
|
+
if (doBackup.toLowerCase() !== "n") {
|
|
142
|
+
const backupName = `.agent.backup-${Date.now()}`;
|
|
143
|
+
const backupPath = path.join(targetPath, backupName);
|
|
144
|
+
try {
|
|
145
|
+
fs.cpSync(agentDir, backupPath, { recursive: true });
|
|
146
|
+
console.log(` ✅ Backup criado: ${backupName}/`);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.error(` ⚠️ Falha no backup: ${err.message}`);
|
|
149
|
+
const cont = await ask(" Continuar sem backup? (y/N): ");
|
|
150
|
+
if (cont.toLowerCase() !== "y") {
|
|
151
|
+
throw new Error("Usuário abortou por falha no backup.");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
161
154
|
}
|
|
162
155
|
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
156
|
|
|
166
|
-
|
|
167
|
-
const audit = [getAuditHeader(ctx, "uninstall", flags)];
|
|
157
|
+
const audit = [getAuditHeader(ctx, "uninstall", flags)];
|
|
168
158
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
159
|
+
for (const item of toRemove) {
|
|
160
|
+
if (item.isDir) {
|
|
161
|
+
fs.rmSync(item.path, { recursive: true, force: true });
|
|
162
|
+
} else {
|
|
163
|
+
fs.unlinkSync(item.path);
|
|
164
|
+
}
|
|
165
|
+
console.log(` ✅ Removido: ${item.label}`);
|
|
166
|
+
audit.push(`- ACT: REMOVED ${item.label}`);
|
|
175
167
|
}
|
|
176
|
-
console.log(` ✅ Removido: ${item.label}`);
|
|
177
|
-
audit.push(`- ACT: REMOVED ${item.label}`);
|
|
178
|
-
}
|
|
179
168
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
169
|
+
console.log("\n✨ OpenClaw desinstalado com sucesso!");
|
|
170
|
+
console.log(" Para reinstalar: npx @fabioforest/openclaw init --apply\n");
|
|
171
|
+
|
|
172
|
+
// Gravar audit no diretório pai (já que .agent/ foi removido)
|
|
173
|
+
if (flags.audit !== false) {
|
|
174
|
+
const filename = `openclaw-uninstall-${new Date().toISOString().replace(/[:.]/g, "-")}.md`;
|
|
175
|
+
const auditPath = path.join(targetPath, filename);
|
|
176
|
+
try {
|
|
177
|
+
fs.writeFileSync(auditPath, audit.join("\n") + "\n", "utf8");
|
|
178
|
+
console.log(` 📝 Log de auditoria: ${filename}`);
|
|
179
|
+
} catch (e) { /* silencioso */ }
|
|
180
|
+
}
|
|
191
181
|
}
|
|
192
|
-
|
|
193
|
-
} catch (err) {
|
|
194
|
-
console.error(`\n❌ Falha: ${err.message}`);
|
|
195
|
-
process.exit(1);
|
|
196
|
-
}
|
|
182
|
+
});
|
|
197
183
|
}
|
|
198
184
|
|
|
199
185
|
module.exports = { run };
|