@innominatum/agentforge-cli 1.0.1
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/LICENSE +393 -0
- package/README.md +72 -0
- package/dist/index.js +739 -0
- package/documents/goclaw-llms-full.txt +28973 -0
- package/package.json +52 -0
- package/src/index.ts +805 -0
- package/templates/CLI_MANUAL.md +98 -0
- package/templates/default-agent/AGENTS.md +26 -0
- package/templates/default-agent/CAPABILITIES.md +15 -0
- package/templates/default-agent/HEARTBEAT.md +19 -0
- package/templates/default-agent/IDENTITY.md +8 -0
- package/templates/default-agent/MEMORY.md +23 -0
- package/templates/default-agent/SOUL.md +31 -0
- package/templates/default-agent/USER.md +13 -0
- package/templates/default-agent/USER_PREDEFINED.md +13 -0
- package/templates/default-skill/SKILL.md +9 -0
- package/tsconfig.json +14 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
42
|
+
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const slugify_1 = __importDefault(require("slugify"));
|
|
44
|
+
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
45
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
46
|
+
const tar = __importStar(require("tar"));
|
|
47
|
+
const axios_1 = __importDefault(require("axios"));
|
|
48
|
+
const readline = __importStar(require("readline"));
|
|
49
|
+
function confirmOverwrite(entityType) {
|
|
50
|
+
const rl = readline.createInterface({
|
|
51
|
+
input: process.stdin,
|
|
52
|
+
output: process.stdout
|
|
53
|
+
});
|
|
54
|
+
return new Promise(resolve => {
|
|
55
|
+
rl.question(`⚠️ Atenção: O pull irá sobrescrever as suas ${entityType} locais. Quaisquer alterações não publicadas serão perdidas. Deseja continuar? (s/N) `, answer => {
|
|
56
|
+
rl.close();
|
|
57
|
+
const isYes = answer.toLowerCase() === 's' || answer.toLowerCase() === 'sim' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
58
|
+
resolve(isYes);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function getWorkspaceRoot() {
|
|
63
|
+
let dir = process.cwd();
|
|
64
|
+
while (dir !== path_1.default.parse(dir).root) {
|
|
65
|
+
if (fs_extra_1.default.existsSync(path_1.default.join(dir, "agentforge.json")) || fs_extra_1.default.existsSync(path_1.default.join(dir, "agentforge.yml"))) {
|
|
66
|
+
return dir;
|
|
67
|
+
}
|
|
68
|
+
dir = path_1.default.dirname(dir);
|
|
69
|
+
}
|
|
70
|
+
if (fs_extra_1.default.existsSync(path_1.default.join(dir, "agentforge.json")) || fs_extra_1.default.existsSync(path_1.default.join(dir, "agentforge.yml"))) {
|
|
71
|
+
return dir;
|
|
72
|
+
}
|
|
73
|
+
console.error("❌ Erro: Não foi possível encontrar a raiz do workspace (agentforge.json). Certifique-se de estar dentro do projeto.");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const program = new commander_1.Command();
|
|
77
|
+
program
|
|
78
|
+
.name("agentforge")
|
|
79
|
+
.description("CLI para gerir agentes, equipas e templates de agentes")
|
|
80
|
+
.version("0.1.0");
|
|
81
|
+
program
|
|
82
|
+
.command("init")
|
|
83
|
+
.alias("start")
|
|
84
|
+
.description("Cria a estrutura inicial do workspace de agentes")
|
|
85
|
+
.action(async () => {
|
|
86
|
+
const basePath = process.cwd();
|
|
87
|
+
const folders = [
|
|
88
|
+
"agents",
|
|
89
|
+
"documents",
|
|
90
|
+
"templates/default-agent",
|
|
91
|
+
"exports"
|
|
92
|
+
];
|
|
93
|
+
for (const folder of folders) {
|
|
94
|
+
await fs_extra_1.default.ensureDir(path_1.default.join(basePath, folder));
|
|
95
|
+
}
|
|
96
|
+
const config = {
|
|
97
|
+
workspace: "agentforge",
|
|
98
|
+
version: 1,
|
|
99
|
+
goclaw: {
|
|
100
|
+
api_url: "http://localhost:18790",
|
|
101
|
+
username: "system",
|
|
102
|
+
token: "",
|
|
103
|
+
default_provider: "ollama-cloud",
|
|
104
|
+
default_model: "deepseek-v4-pro",
|
|
105
|
+
skills_import_endpoint: "/v1/skills/import",
|
|
106
|
+
skills_export_endpoint: "/v1/skills/export"
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
await fs_extra_1.default.writeJson(path_1.default.join(basePath, "agentforge.json"), config, { spaces: 2 });
|
|
110
|
+
// Copiar o manual da CLI para servir de README do workspace
|
|
111
|
+
const cliManualPath = path_1.default.join(__dirname, "../templates/CLI_MANUAL.md");
|
|
112
|
+
const workspaceReadmePath = path_1.default.join(basePath, "README.md");
|
|
113
|
+
if (await fs_extra_1.default.pathExists(cliManualPath)) {
|
|
114
|
+
await fs_extra_1.default.copy(cliManualPath, workspaceReadmePath);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
await fs_extra_1.default.writeFile(workspaceReadmePath, `# Agent Workspace\n\nWorkspace criado pela AgentForge CLI.\n`);
|
|
118
|
+
}
|
|
119
|
+
// Opcional: Copiar os templates originais da CLI para o workspace do usuário
|
|
120
|
+
const cliTemplatePath = path_1.default.join(__dirname, "../templates/default-agent");
|
|
121
|
+
const workspaceTemplatePath = path_1.default.join(basePath, "templates/default-agent");
|
|
122
|
+
if (await fs_extra_1.default.pathExists(cliTemplatePath)) {
|
|
123
|
+
await fs_extra_1.default.copy(cliTemplatePath, workspaceTemplatePath);
|
|
124
|
+
}
|
|
125
|
+
// Copiar documentação do GoClaw para a pasta documents
|
|
126
|
+
const cliDocPath = path_1.default.join(__dirname, "../goclaw-llms-full.txt");
|
|
127
|
+
const workspaceDocPath = path_1.default.join(basePath, "documents/goclaw-llms-full.txt");
|
|
128
|
+
if (await fs_extra_1.default.pathExists(cliDocPath)) {
|
|
129
|
+
await fs_extra_1.default.copy(cliDocPath, workspaceDocPath);
|
|
130
|
+
}
|
|
131
|
+
console.log("Workspace de agentes criado com sucesso.");
|
|
132
|
+
});
|
|
133
|
+
const newCmd = program
|
|
134
|
+
.command("new")
|
|
135
|
+
.description("Cria novas entidades (agentes, skills, etc)");
|
|
136
|
+
program
|
|
137
|
+
.command("manual")
|
|
138
|
+
.alias("help-docs")
|
|
139
|
+
.description("Exibe o manual completo de uso da AgentForge CLI")
|
|
140
|
+
.action(async () => {
|
|
141
|
+
const cliManualPath = path_1.default.join(__dirname, "../templates/CLI_MANUAL.md");
|
|
142
|
+
if (await fs_extra_1.default.pathExists(cliManualPath)) {
|
|
143
|
+
const content = await fs_extra_1.default.readFile(cliManualPath, "utf-8");
|
|
144
|
+
console.log(content);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.error("❌ Manual não encontrado.");
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
newCmd
|
|
151
|
+
.command("agent <name>")
|
|
152
|
+
.description("Cria um novo agente com os ficheiros base da template")
|
|
153
|
+
.action(async (name) => {
|
|
154
|
+
const basePath = getWorkspaceRoot();
|
|
155
|
+
const slug = (0, slugify_1.default)(name, { lower: true, strict: true });
|
|
156
|
+
const agentPath = path_1.default.join(basePath, "agents", slug);
|
|
157
|
+
if (await fs_extra_1.default.pathExists(agentPath)) {
|
|
158
|
+
console.error(`❌ O agente "${name}" já existe em agents/${slug}.`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
await fs_extra_1.default.ensureDir(agentPath);
|
|
162
|
+
const workspaceTemplatePath = path_1.default.join(basePath, "templates/default-agent");
|
|
163
|
+
const cliTemplatePath = path_1.default.join(__dirname, "../templates/default-agent");
|
|
164
|
+
let sourceTemplatePath = "";
|
|
165
|
+
if (await fs_extra_1.default.pathExists(workspaceTemplatePath)) {
|
|
166
|
+
sourceTemplatePath = workspaceTemplatePath;
|
|
167
|
+
}
|
|
168
|
+
else if (await fs_extra_1.default.pathExists(cliTemplatePath)) {
|
|
169
|
+
sourceTemplatePath = cliTemplatePath;
|
|
170
|
+
}
|
|
171
|
+
if (sourceTemplatePath !== "") {
|
|
172
|
+
await fs_extra_1.default.copy(sourceTemplatePath, agentPath);
|
|
173
|
+
try {
|
|
174
|
+
const config = await getConfig();
|
|
175
|
+
const agentJson = {
|
|
176
|
+
agent_key: slug,
|
|
177
|
+
display_name: name,
|
|
178
|
+
agent_type: "custom",
|
|
179
|
+
status: "active",
|
|
180
|
+
emoji: "🔥",
|
|
181
|
+
context_window: 200000,
|
|
182
|
+
max_tool_iterations: 30,
|
|
183
|
+
provider: config.goclaw?.default_provider || "ollama cloud",
|
|
184
|
+
model: config.goclaw?.default_model || "deepseek-v4-pro",
|
|
185
|
+
frontmatter: `Expertise summary for ${name}`
|
|
186
|
+
};
|
|
187
|
+
await fs_extra_1.default.writeJson(path_1.default.join(agentPath, "agent.json"), agentJson, { spaces: 2 });
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
// Fallback se não conseguir ler config
|
|
191
|
+
}
|
|
192
|
+
console.log(`✅ Agente "${name}" criado com sucesso em agents/${slug} usando templates!`);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.warn("⚠️ Nenhuma pasta de templates encontrada. Criando estrutura básica...");
|
|
196
|
+
try {
|
|
197
|
+
const config = await getConfig();
|
|
198
|
+
const agentJson = {
|
|
199
|
+
agent_key: slug,
|
|
200
|
+
display_name: name,
|
|
201
|
+
agent_type: "custom",
|
|
202
|
+
provider: config.goclaw?.default_provider || "ollama cloud",
|
|
203
|
+
model: config.goclaw?.default_model || "deepseek-v4-pro",
|
|
204
|
+
other_config: {
|
|
205
|
+
description: `Agent ${name} created by AgentForge`
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
await fs_extra_1.default.writeJson(path_1.default.join(agentPath, "agent.json"), agentJson, { spaces: 2 });
|
|
209
|
+
}
|
|
210
|
+
catch (err) { }
|
|
211
|
+
await fs_extra_1.default.writeFile(path_1.default.join(agentPath, "SOUL.md"), `# ${name}\n\nAgente criado pela AgentForge CLI.\n`);
|
|
212
|
+
await fs_extra_1.default.writeFile(path_1.default.join(agentPath, "HEARTBEAT.md"), `# Instruções de Heartbeat\n`);
|
|
213
|
+
console.log(`✅ Agente "${name}" criado com sucesso em agents/${slug}.`);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
newCmd
|
|
217
|
+
.command("skill <name>")
|
|
218
|
+
.description("Cria uma nova skill usando o template base")
|
|
219
|
+
.action(async (name) => {
|
|
220
|
+
const basePath = getWorkspaceRoot();
|
|
221
|
+
const slug = (0, slugify_1.default)(name, { lower: true, strict: true });
|
|
222
|
+
const skillPath = path_1.default.join(basePath, "skills", slug);
|
|
223
|
+
if (await fs_extra_1.default.pathExists(skillPath)) {
|
|
224
|
+
console.error(`❌ A skill "${name}" já existe em skills/${slug}.`);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
await fs_extra_1.default.ensureDir(skillPath);
|
|
228
|
+
const workspaceTemplatePath = path_1.default.join(basePath, "templates/default-skill");
|
|
229
|
+
const cliTemplatePath = path_1.default.join(__dirname, "../templates/default-skill");
|
|
230
|
+
let sourceTemplatePath = "";
|
|
231
|
+
if (await fs_extra_1.default.pathExists(workspaceTemplatePath)) {
|
|
232
|
+
sourceTemplatePath = workspaceTemplatePath;
|
|
233
|
+
}
|
|
234
|
+
else if (await fs_extra_1.default.pathExists(cliTemplatePath)) {
|
|
235
|
+
sourceTemplatePath = cliTemplatePath;
|
|
236
|
+
}
|
|
237
|
+
if (sourceTemplatePath !== "") {
|
|
238
|
+
await fs_extra_1.default.copy(sourceTemplatePath, skillPath);
|
|
239
|
+
// Update the {{name}} placeholder in SKILL.md
|
|
240
|
+
const skillMdPath = path_1.default.join(skillPath, "SKILL.md");
|
|
241
|
+
if (await fs_extra_1.default.pathExists(skillMdPath)) {
|
|
242
|
+
let content = await fs_extra_1.default.readFile(skillMdPath, 'utf8');
|
|
243
|
+
content = content.replace(/{{name}}/g, name);
|
|
244
|
+
await fs_extra_1.default.writeFile(skillMdPath, content);
|
|
245
|
+
}
|
|
246
|
+
console.log(`✅ Skill "${name}" criada com sucesso em skills/${slug} usando templates!`);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
console.warn("⚠️ Nenhum template de skill encontrado. Criando um SKILL.md vazio.");
|
|
250
|
+
await fs_extra_1.default.writeFile(path_1.default.join(skillPath, "SKILL.md"), `---\nname: "${name}"\ndescription: "Skill description"\ndeps: []\n---\n\n## Instruções\n`);
|
|
251
|
+
console.log(`✅ Skill "${name}" criada com sucesso em skills/${slug}.`);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
const buildCmd = program
|
|
255
|
+
.command("build")
|
|
256
|
+
.description("Realiza o build (empacotamento) de entidades");
|
|
257
|
+
buildCmd
|
|
258
|
+
.command("skill <slug>")
|
|
259
|
+
.description("Empacota uma skill em um arquivo .zip na pasta exports/")
|
|
260
|
+
.action(async (slug) => {
|
|
261
|
+
const basePath = getWorkspaceRoot();
|
|
262
|
+
const skillPath = path_1.default.join(basePath, "skills", slug);
|
|
263
|
+
const exportsPath = path_1.default.join(basePath, "exports");
|
|
264
|
+
const zipPath = path_1.default.join(exportsPath, `${slug}.zip`);
|
|
265
|
+
if (!(await fs_extra_1.default.pathExists(skillPath))) {
|
|
266
|
+
console.error(`❌ A skill "${slug}" não foi encontrada em skills/${slug}.`);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
await fs_extra_1.default.ensureDir(exportsPath);
|
|
270
|
+
const zip = new adm_zip_1.default();
|
|
271
|
+
zip.addLocalFolder(skillPath, "");
|
|
272
|
+
zip.writeZip(zipPath);
|
|
273
|
+
console.log(`✅ Build concluído: ${slug}.zip salvo na pasta exports/`);
|
|
274
|
+
});
|
|
275
|
+
async function getConfig() {
|
|
276
|
+
const root = getWorkspaceRoot();
|
|
277
|
+
const configPath = path_1.default.join(root, "agentforge.json");
|
|
278
|
+
if (!(await fs_extra_1.default.pathExists(configPath))) {
|
|
279
|
+
console.error("❌ Arquivo agentforge.json não encontrado. Execute 'agentforge init' primeiro.");
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
const config = await fs_extra_1.default.readJson(configPath);
|
|
283
|
+
if (config.goclaw && config.goclaw.api_url) {
|
|
284
|
+
config.goclaw.api_url = config.goclaw.api_url.replace(/\/$/, "");
|
|
285
|
+
}
|
|
286
|
+
return config;
|
|
287
|
+
}
|
|
288
|
+
const deployCmd = program
|
|
289
|
+
.command("deploy")
|
|
290
|
+
.description("Faz o deploy de entidades para a plataforma GoClaw");
|
|
291
|
+
deployCmd
|
|
292
|
+
.command("skill <slug>")
|
|
293
|
+
.description("Faz o build da skill e envia para a API do GoClaw")
|
|
294
|
+
.action(async (slug) => {
|
|
295
|
+
const config = await getConfig();
|
|
296
|
+
if (!config.goclaw || !config.goclaw.token) {
|
|
297
|
+
console.error("❌ Configure sua chave de API (token) no agentforge.json antes de fazer o deploy.");
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
const basePath = getWorkspaceRoot();
|
|
301
|
+
const skillPath = path_1.default.join(basePath, "skills", slug);
|
|
302
|
+
const exportsPath = path_1.default.join(basePath, "exports");
|
|
303
|
+
const zipPath = path_1.default.join(exportsPath, `${slug}.zip`);
|
|
304
|
+
if (!(await fs_extra_1.default.pathExists(skillPath))) {
|
|
305
|
+
console.error(`❌ A skill "${slug}" não foi encontrada em skills/${slug}.`);
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
await fs_extra_1.default.ensureDir(exportsPath);
|
|
309
|
+
const zip = new adm_zip_1.default();
|
|
310
|
+
zip.addLocalFolder(skillPath, "");
|
|
311
|
+
zip.writeZip(zipPath);
|
|
312
|
+
console.log(`✅ Build concluído: ${slug}.zip preparado para envio.`);
|
|
313
|
+
console.log(`🚀 Fazendo upload para o GoClaw...`);
|
|
314
|
+
const form = new form_data_1.default();
|
|
315
|
+
form.append("file", fs_extra_1.default.createReadStream(zipPath));
|
|
316
|
+
try {
|
|
317
|
+
const url = `${config.goclaw.api_url}/v1/skills/upload`;
|
|
318
|
+
const response = await axios_1.default.post(url, form, {
|
|
319
|
+
headers: {
|
|
320
|
+
...form.getHeaders(),
|
|
321
|
+
Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system"
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
const data = response.data;
|
|
325
|
+
if (data && data.version) {
|
|
326
|
+
console.log(`📌 Skill atualizada para a versão ${data.version}.`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
console.error("❌ Erro durante o deploy:");
|
|
331
|
+
if (error.response) {
|
|
332
|
+
console.error(error.response.data);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
console.error(error.message);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
async function deployContextFiles(slug, config) {
|
|
340
|
+
const basePath = getWorkspaceRoot();
|
|
341
|
+
const agentPath = path_1.default.join(basePath, "agents", slug);
|
|
342
|
+
if (!(await fs_extra_1.default.pathExists(agentPath))) {
|
|
343
|
+
throw new Error(`Agente não encontrado em agents/${slug}`);
|
|
344
|
+
}
|
|
345
|
+
const files = await fs_extra_1.default.readdir(agentPath);
|
|
346
|
+
const contextFiles = files.filter(f => f.endsWith('.md') || f.endsWith('.txt') || f.endsWith('.py')).filter(f => f !== 'README.md' && f !== 'agent.json');
|
|
347
|
+
if (contextFiles.length === 0) {
|
|
348
|
+
console.log(`Nenhum arquivo de contexto encontrado para "${slug}".`);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const tempExportDir = path_1.default.join(basePath, `temp_export_${slug}`);
|
|
352
|
+
const tempContextDir = path_1.default.join(tempExportDir, "context_files");
|
|
353
|
+
const tarPath = path_1.default.join(basePath, `temp_export_${slug}.tar.gz`);
|
|
354
|
+
try {
|
|
355
|
+
await fs_extra_1.default.ensureDir(tempContextDir);
|
|
356
|
+
for (const file of contextFiles) {
|
|
357
|
+
await fs_extra_1.default.copy(path_1.default.join(agentPath, file), path_1.default.join(tempContextDir, file));
|
|
358
|
+
}
|
|
359
|
+
await tar.c({
|
|
360
|
+
gzip: true,
|
|
361
|
+
file: tarPath,
|
|
362
|
+
cwd: tempExportDir
|
|
363
|
+
}, ["context_files"]);
|
|
364
|
+
const form = new form_data_1.default();
|
|
365
|
+
form.append("file", fs_extra_1.default.createReadStream(tarPath));
|
|
366
|
+
const url = `${config.goclaw.api_url}/v1/agents/${slug}/import?include=context_files`;
|
|
367
|
+
await axios_1.default.post(url, form, {
|
|
368
|
+
headers: {
|
|
369
|
+
...form.getHeaders(),
|
|
370
|
+
Authorization: `Bearer ${config.goclaw.token}`,
|
|
371
|
+
"X-GoClaw-User-Id": config.goclaw.username || "system"
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
console.log(`✅ Upload cirúrgico de ${contextFiles.length} arquivos concluído com sucesso!`);
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
if (await fs_extra_1.default.pathExists(tempExportDir))
|
|
378
|
+
await fs_extra_1.default.remove(tempExportDir);
|
|
379
|
+
if (await fs_extra_1.default.pathExists(tarPath))
|
|
380
|
+
await fs_extra_1.default.remove(tarPath);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
deployCmd
|
|
384
|
+
.command("context <slug>")
|
|
385
|
+
.description("Faz upload dos arquivos de contexto diretamente para o agente usando a API de importação")
|
|
386
|
+
.action(async (slug) => {
|
|
387
|
+
const config = await getConfig();
|
|
388
|
+
if (!config.goclaw || !config.goclaw.token) {
|
|
389
|
+
console.error("❌ Configure sua chave de API (token) no agentforge.json.");
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
console.log(`🚀 Sincronizando arquivos de contexto do agente "${slug}"...`);
|
|
393
|
+
try {
|
|
394
|
+
await deployContextFiles(slug, config);
|
|
395
|
+
console.log("✅ Deploy de contexto concluído!");
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
console.error("❌ Erro ao enviar contexto:", error.response?.data || error.message);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
deployCmd
|
|
402
|
+
.command("agent <slug>")
|
|
403
|
+
.description("Faz deploy completo do agente (configuração + arquivos de contexto)")
|
|
404
|
+
.action(async (slug) => {
|
|
405
|
+
const config = await getConfig();
|
|
406
|
+
if (!config.goclaw || !config.goclaw.token) {
|
|
407
|
+
console.error("❌ Configure sua chave de API (token) no agentforge.json.");
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
const basePath = getWorkspaceRoot();
|
|
411
|
+
const agentPath = path_1.default.join(basePath, "agents", slug);
|
|
412
|
+
const agentJsonPath = path_1.default.join(agentPath, "agent.json");
|
|
413
|
+
if (!(await fs_extra_1.default.pathExists(agentJsonPath))) {
|
|
414
|
+
console.error(`❌ agent.json não encontrado em agents/${slug}.`);
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
const agentConfig = await fs_extra_1.default.readJson(agentJsonPath);
|
|
418
|
+
console.log(`🚀 Atualizando configuração do agente "${slug}"...`);
|
|
419
|
+
try {
|
|
420
|
+
let exists = true;
|
|
421
|
+
try {
|
|
422
|
+
await axios_1.default.get(`${config.goclaw.api_url}/v1/agents/${slug}`, {
|
|
423
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
catch (e) {
|
|
427
|
+
if (e.response && e.response.status === 404) {
|
|
428
|
+
exists = false;
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
throw e;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (!exists) {
|
|
435
|
+
await axios_1.default.post(`${config.goclaw.api_url}/v1/agents`, agentConfig, {
|
|
436
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
|
|
437
|
+
});
|
|
438
|
+
console.log("✅ Agente criado com sucesso.");
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
await axios_1.default.put(`${config.goclaw.api_url}/v1/agents/${slug}`, agentConfig, {
|
|
442
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
|
|
443
|
+
});
|
|
444
|
+
console.log("✅ Configurações do agente atualizadas.");
|
|
445
|
+
}
|
|
446
|
+
console.log(`🚀 Sincronizando arquivos de contexto...`);
|
|
447
|
+
await deployContextFiles(slug, config);
|
|
448
|
+
console.log("✅ Deploy completo concluído!");
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
console.error(`❌ Erro no deploy da configuração:`, error.response?.data || error.message);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
const pullCmd = program
|
|
455
|
+
.command("pull")
|
|
456
|
+
.description("Sincroniza entidades do GoClaw para o workspace local");
|
|
457
|
+
pullCmd
|
|
458
|
+
.command("skills")
|
|
459
|
+
.description("Faz download do arquivo tar.gz de skills do GoClaw e extrai localmente")
|
|
460
|
+
.action(async () => {
|
|
461
|
+
const config = await getConfig();
|
|
462
|
+
if (!config.goclaw || !config.goclaw.token) {
|
|
463
|
+
console.error("❌ Configure sua chave de API (token) no agentforge.json antes de fazer o pull.");
|
|
464
|
+
process.exit(1);
|
|
465
|
+
}
|
|
466
|
+
if (!(await confirmOverwrite("skills"))) {
|
|
467
|
+
console.log("❌ Pull cancelado pelo utilizador.");
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
console.log("📥 Baixando skills do GoClaw...");
|
|
471
|
+
try {
|
|
472
|
+
const url = `${config.goclaw.api_url}${config.goclaw.skills_export_endpoint || '/v1/skills/export'}`;
|
|
473
|
+
const response = await axios_1.default.get(url, {
|
|
474
|
+
headers: {
|
|
475
|
+
Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system"
|
|
476
|
+
},
|
|
477
|
+
responseType: "stream"
|
|
478
|
+
});
|
|
479
|
+
const tempTarPath = path_1.default.join(getWorkspaceRoot(), "temp_skills.tar.gz");
|
|
480
|
+
const writer = fs_extra_1.default.createWriteStream(tempTarPath);
|
|
481
|
+
response.data.pipe(writer);
|
|
482
|
+
await new Promise((resolve, reject) => {
|
|
483
|
+
writer.on("finish", resolve);
|
|
484
|
+
writer.on("error", reject);
|
|
485
|
+
});
|
|
486
|
+
console.log("📦 Extraindo skills para a pasta local...");
|
|
487
|
+
await tar.x({
|
|
488
|
+
file: tempTarPath,
|
|
489
|
+
cwd: getWorkspaceRoot()
|
|
490
|
+
});
|
|
491
|
+
await fs_extra_1.default.remove(tempTarPath);
|
|
492
|
+
console.log("📥 Baixando ficheiros de código das skills...");
|
|
493
|
+
const skillsListRes = await axios_1.default.get(`${config.goclaw.api_url}/v1/skills`, {
|
|
494
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
|
|
495
|
+
});
|
|
496
|
+
const skills = skillsListRes.data.skills || [];
|
|
497
|
+
for (const skill of skills) {
|
|
498
|
+
try {
|
|
499
|
+
const isSystem = skill.is_system === true;
|
|
500
|
+
const targetFolder = isSystem ? path_1.default.join("system", skill.slug) : skill.slug;
|
|
501
|
+
if (isSystem) {
|
|
502
|
+
const originalPath = path_1.default.join(getWorkspaceRoot(), "skills", skill.slug);
|
|
503
|
+
const newPath = path_1.default.join(getWorkspaceRoot(), "skills", targetFolder);
|
|
504
|
+
if (await fs_extra_1.default.pathExists(originalPath)) {
|
|
505
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(newPath));
|
|
506
|
+
await fs_extra_1.default.move(originalPath, newPath, { overwrite: true });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const filesRes = await axios_1.default.get(`${config.goclaw.api_url}/v1/skills/${skill.id}/files`, {
|
|
510
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
|
|
511
|
+
});
|
|
512
|
+
const files = filesRes.data.files || [];
|
|
513
|
+
for (const file of files) {
|
|
514
|
+
if (file.isDir)
|
|
515
|
+
continue;
|
|
516
|
+
const fileContentRes = await axios_1.default.get(`${config.goclaw.api_url}/v1/skills/${skill.id}/files/${file.path}`, {
|
|
517
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
|
|
518
|
+
});
|
|
519
|
+
const filePath = path_1.default.join(getWorkspaceRoot(), "skills", targetFolder, file.path);
|
|
520
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
521
|
+
await fs_extra_1.default.writeFile(filePath, fileContentRes.data.content || "");
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
catch (fileErr) {
|
|
525
|
+
console.warn(`⚠️ Não foi possível transferir os ficheiros da skill ${skill.slug}: ${fileErr.message}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
console.log("✅ Pull concluído com sucesso! As skills foram atualizadas localmente.");
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
console.error("❌ Erro durante o pull das skills:");
|
|
532
|
+
if (error.response) {
|
|
533
|
+
console.error(`Status HTTP ${error.response.status}`);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
console.error(error.message);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
pullCmd
|
|
541
|
+
.command("agents")
|
|
542
|
+
.description("Faz download cirúrgico dos agentes (configuração e contexto)")
|
|
543
|
+
.action(async () => {
|
|
544
|
+
const config = await getConfig();
|
|
545
|
+
if (!config.goclaw || !config.goclaw.token) {
|
|
546
|
+
console.error("❌ Configure sua chave de API (token) no agentforge.json antes de fazer o pull.");
|
|
547
|
+
process.exit(1);
|
|
548
|
+
}
|
|
549
|
+
if (!(await confirmOverwrite("agentes"))) {
|
|
550
|
+
console.log("❌ Pull cancelado pelo utilizador.");
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
console.log("📥 Buscando lista de agentes do GoClaw...");
|
|
554
|
+
try {
|
|
555
|
+
const listResponse = await axios_1.default.get(`${config.goclaw.api_url}/v1/agents`, {
|
|
556
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" }
|
|
557
|
+
});
|
|
558
|
+
const agents = listResponse.data.agents || [];
|
|
559
|
+
console.log(`Encontrados ${agents.length} agentes. Sincronizando...`);
|
|
560
|
+
for (const agent of agents) {
|
|
561
|
+
const slug = agent.agent_key;
|
|
562
|
+
console.log(`📦 Baixando agente: ${slug}...`);
|
|
563
|
+
const url = `${config.goclaw.api_url}/v1/agents/${agent.id}/export`;
|
|
564
|
+
const response = await axios_1.default.get(url, {
|
|
565
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, "X-GoClaw-User-Id": config.goclaw.username || "system" },
|
|
566
|
+
responseType: "stream"
|
|
567
|
+
});
|
|
568
|
+
const tempTarPath = path_1.default.join(getWorkspaceRoot(), `temp_agent_${slug}.tar.gz`);
|
|
569
|
+
try {
|
|
570
|
+
const writer = fs_extra_1.default.createWriteStream(tempTarPath);
|
|
571
|
+
response.data.pipe(writer);
|
|
572
|
+
await new Promise((resolve, reject) => {
|
|
573
|
+
writer.on("finish", resolve);
|
|
574
|
+
writer.on("error", reject);
|
|
575
|
+
});
|
|
576
|
+
const agentPath = path_1.default.join(getWorkspaceRoot(), "agents", slug);
|
|
577
|
+
await fs_extra_1.default.ensureDir(agentPath);
|
|
578
|
+
await tar.x({
|
|
579
|
+
file: tempTarPath,
|
|
580
|
+
cwd: agentPath,
|
|
581
|
+
strip: 0,
|
|
582
|
+
filter: (path) => {
|
|
583
|
+
return path === 'agent.json' || path.startsWith('context_files/');
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
const contextDir = path_1.default.join(agentPath, "context_files");
|
|
587
|
+
if (await fs_extra_1.default.pathExists(contextDir)) {
|
|
588
|
+
const contextFiles = await fs_extra_1.default.readdir(contextDir);
|
|
589
|
+
for (const f of contextFiles) {
|
|
590
|
+
await fs_extra_1.default.move(path_1.default.join(contextDir, f), path_1.default.join(agentPath, f), { overwrite: true });
|
|
591
|
+
}
|
|
592
|
+
await fs_extra_1.default.remove(contextDir);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
finally {
|
|
596
|
+
if (await fs_extra_1.default.pathExists(tempTarPath)) {
|
|
597
|
+
await fs_extra_1.default.remove(tempTarPath);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
console.log("✅ Pull de agentes concluído com sucesso!");
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
if (error.response && error.response.status) {
|
|
605
|
+
console.error(`❌ Erro durante o pull dos agentes: HTTP ${error.response.status} - Verifique suas credenciais e permissões no agentforge.json (username deve ser o dono do agente).`);
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
console.error("❌ Erro durante o pull dos agentes:", error.message);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
pullCmd
|
|
613
|
+
.command('all')
|
|
614
|
+
.description('Faz download cirúrgico de todos os agentes e skills do GoClaw para a pasta local')
|
|
615
|
+
.action(async () => {
|
|
616
|
+
const config = await getConfig();
|
|
617
|
+
if (!config.goclaw || !config.goclaw.token) {
|
|
618
|
+
console.error('❌ Configure sua chave de API (token) no agentforge.json antes de fazer o pull.');
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
if (!(await confirmOverwrite('TUDO (agentes e skills)'))) {
|
|
622
|
+
console.log('❌ Pull cancelado pelo utilizador.');
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
console.log('🔄 Iniciando sincronização completa (pull all)...');
|
|
626
|
+
// PULL SKILLS INLINE
|
|
627
|
+
console.log('\n--- [1/2] SKILLS ---');
|
|
628
|
+
try {
|
|
629
|
+
const url = `${config.goclaw.api_url}${config.goclaw.skills_export_endpoint || '/v1/skills/export'}`;
|
|
630
|
+
const response = await axios_1.default.get(url, {
|
|
631
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, 'X-GoClaw-User-Id': config.goclaw.username || 'system' },
|
|
632
|
+
responseType: 'stream'
|
|
633
|
+
});
|
|
634
|
+
const tempTarPath = path_1.default.join(getWorkspaceRoot(), 'temp_skills.tar.gz');
|
|
635
|
+
const writer = fs_extra_1.default.createWriteStream(tempTarPath);
|
|
636
|
+
response.data.pipe(writer);
|
|
637
|
+
await new Promise((resolve, reject) => {
|
|
638
|
+
writer.on('finish', resolve);
|
|
639
|
+
writer.on('error', reject);
|
|
640
|
+
});
|
|
641
|
+
await tar.x({ file: tempTarPath, cwd: getWorkspaceRoot() });
|
|
642
|
+
await fs_extra_1.default.remove(tempTarPath);
|
|
643
|
+
const skillsListRes = await axios_1.default.get(`${config.goclaw.api_url}/v1/skills`, {
|
|
644
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, 'X-GoClaw-User-Id': config.goclaw.username || 'system' }
|
|
645
|
+
});
|
|
646
|
+
const skills = skillsListRes.data.skills || [];
|
|
647
|
+
for (const skill of skills) {
|
|
648
|
+
try {
|
|
649
|
+
const isSystem = skill.is_system === true;
|
|
650
|
+
const targetFolder = isSystem ? path_1.default.join('system', skill.slug) : skill.slug;
|
|
651
|
+
if (isSystem) {
|
|
652
|
+
const originalPath = path_1.default.join(getWorkspaceRoot(), 'skills', skill.slug);
|
|
653
|
+
const newPath = path_1.default.join(getWorkspaceRoot(), 'skills', targetFolder);
|
|
654
|
+
if (await fs_extra_1.default.pathExists(originalPath)) {
|
|
655
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(newPath));
|
|
656
|
+
await fs_extra_1.default.move(originalPath, newPath, { overwrite: true });
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
const filesRes = await axios_1.default.get(`${config.goclaw.api_url}/v1/skills/${skill.id}/files`, {
|
|
660
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, 'X-GoClaw-User-Id': config.goclaw.username || 'system' }
|
|
661
|
+
});
|
|
662
|
+
const files = filesRes.data.files || [];
|
|
663
|
+
for (const file of files) {
|
|
664
|
+
if (file.isDir)
|
|
665
|
+
continue;
|
|
666
|
+
const fileContentRes = await axios_1.default.get(`${config.goclaw.api_url}/v1/skills/${skill.id}/files/${file.path}`, {
|
|
667
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, 'X-GoClaw-User-Id': config.goclaw.username || 'system' }
|
|
668
|
+
});
|
|
669
|
+
const filePath = path_1.default.join(getWorkspaceRoot(), 'skills', targetFolder, file.path);
|
|
670
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
671
|
+
await fs_extra_1.default.writeFile(filePath, fileContentRes.data.content || '');
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
catch (fileErr) {
|
|
675
|
+
console.warn(`⚠️ Não foi possível transferir os ficheiros da skill ${skill.slug}: ${fileErr.message}`);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
console.log('✅ Pull de skills concluído!');
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
console.error('❌ Erro durante o pull das skills:', error.message);
|
|
682
|
+
}
|
|
683
|
+
// PULL AGENTS INLINE
|
|
684
|
+
console.log('\n--- [2/2] AGENTS ---');
|
|
685
|
+
try {
|
|
686
|
+
const listResponse = await axios_1.default.get(`${config.goclaw.api_url}/v1/agents`, {
|
|
687
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, 'X-GoClaw-User-Id': config.goclaw.username || 'system' }
|
|
688
|
+
});
|
|
689
|
+
const agents = listResponse.data.agents || [];
|
|
690
|
+
console.log(`Encontrados ${agents.length} agentes. Sincronizando...`);
|
|
691
|
+
for (const agent of agents) {
|
|
692
|
+
const slug = agent.agent_key;
|
|
693
|
+
console.log(`📦 Baixando agente: ${slug}...`);
|
|
694
|
+
const url = `${config.goclaw.api_url}/v1/agents/${agent.id}/export`;
|
|
695
|
+
const response = await axios_1.default.get(url, {
|
|
696
|
+
headers: { Authorization: `Bearer ${config.goclaw.token}`, 'X-GoClaw-User-Id': config.goclaw.username || 'system' },
|
|
697
|
+
responseType: 'stream'
|
|
698
|
+
});
|
|
699
|
+
const tempTarPath = path_1.default.join(getWorkspaceRoot(), `temp_agent_${slug}.tar.gz`);
|
|
700
|
+
try {
|
|
701
|
+
const writer = fs_extra_1.default.createWriteStream(tempTarPath);
|
|
702
|
+
response.data.pipe(writer);
|
|
703
|
+
await new Promise((resolve, reject) => {
|
|
704
|
+
writer.on('finish', resolve);
|
|
705
|
+
writer.on('error', reject);
|
|
706
|
+
});
|
|
707
|
+
const agentPath = path_1.default.join(getWorkspaceRoot(), 'agents', slug);
|
|
708
|
+
await fs_extra_1.default.ensureDir(agentPath);
|
|
709
|
+
await tar.x({
|
|
710
|
+
file: tempTarPath,
|
|
711
|
+
cwd: agentPath,
|
|
712
|
+
strip: 0,
|
|
713
|
+
filter: (path) => {
|
|
714
|
+
return path === 'agent.json' || path.startsWith('context_files/');
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
const contextDir = path_1.default.join(agentPath, 'context_files');
|
|
718
|
+
if (await fs_extra_1.default.pathExists(contextDir)) {
|
|
719
|
+
const contextFiles = await fs_extra_1.default.readdir(contextDir);
|
|
720
|
+
for (const f of contextFiles) {
|
|
721
|
+
await fs_extra_1.default.move(path_1.default.join(contextDir, f), path_1.default.join(agentPath, f), { overwrite: true });
|
|
722
|
+
}
|
|
723
|
+
await fs_extra_1.default.remove(contextDir);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
finally {
|
|
727
|
+
if (await fs_extra_1.default.pathExists(tempTarPath)) {
|
|
728
|
+
await fs_extra_1.default.remove(tempTarPath);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
console.log('✅ Pull de agentes concluído!');
|
|
733
|
+
}
|
|
734
|
+
catch (error) {
|
|
735
|
+
console.error('❌ Erro durante o pull dos agentes:', error.message);
|
|
736
|
+
}
|
|
737
|
+
console.log('\n🚀 SYNC COMPLETO! Workspace atualizado.');
|
|
738
|
+
});
|
|
739
|
+
program.parse();
|