@fazer-ai/agents 1.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/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/agents/claude.js +152 -0
- package/dist/agents/codex.js +155 -0
- package/dist/agents/detect.js +15 -0
- package/dist/agents/handoff.js +22 -0
- package/dist/agents/hermes-skills.js +177 -0
- package/dist/agents/hermes.js +474 -0
- package/dist/agents/index.js +57 -0
- package/dist/agents/manual.js +22 -0
- package/dist/agents/other.js +39 -0
- package/dist/agents/shell.js +15 -0
- package/dist/agents/types.js +2 -0
- package/dist/config.js +48 -0
- package/dist/exec.js +279 -0
- package/dist/hostinger.js +75 -0
- package/dist/hub-command.js +144 -0
- package/dist/index.js +726 -0
- package/dist/licenses.js +93 -0
- package/dist/mcp.js +100 -0
- package/dist/oauth.js +578 -0
- package/dist/onboarding-marker.js +48 -0
- package/dist/preferences.js +40 -0
- package/dist/skills/agents-dev/SKILL.md +37 -0
- package/dist/skills/agents-dev/gotchas.md +6 -0
- package/dist/skills/agents-dev/guardrails.md +6 -0
- package/dist/skills/agents-dev/references/00-get-the-code.md +28 -0
- package/dist/skills/agents-dev/references/01-layout-and-bun-check.md +29 -0
- package/dist/skills/agents-dev/references/02-free-full-and-invariants.md +7 -0
- package/dist/skills/agents-dev/references/03-implement.md +9 -0
- package/dist/skills/agents-dev/references/04-own-image-and-deploy.md +13 -0
- package/dist/skills/agents-onboarding/SKILL.md +80 -0
- package/dist/skills/agents-onboarding/gotchas.md +157 -0
- package/dist/skills/agents-onboarding/guardrails.md +65 -0
- package/dist/skills/agents-onboarding/references/00-prereqs-and-access.md +37 -0
- package/dist/skills/agents-onboarding/references/01-vps-dns-ssh.md +67 -0
- package/dist/skills/agents-onboarding/references/01b-brownfield.md +70 -0
- package/dist/skills/agents-onboarding/references/01c-pick-tier.md +61 -0
- package/dist/skills/agents-onboarding/references/02-coolify.md +109 -0
- package/dist/skills/agents-onboarding/references/03-chatwoot-pro.md +61 -0
- package/dist/skills/agents-onboarding/references/04-agents-image.md +46 -0
- package/dist/skills/agents-onboarding/references/05-langfuse.md +45 -0
- package/dist/skills/agents-onboarding/references/06-setup-and-mcp.md +47 -0
- package/dist/skills/agents-onboarding/references/08-agent-import.md +55 -0
- package/dist/skills/agents-onboarding/references/09-chatwoot-bind.md +41 -0
- package/dist/skills/agents-onboarding/references/10-validate-e2e.md +34 -0
- package/dist/skills/agents-onboarding/references/agent-features.md +61 -0
- package/dist/skills/agents-onboarding/references/chatwoot-hub-register.md +69 -0
- package/dist/skills/agents-onboarding/references/deploy-b-portainer.md +138 -0
- package/dist/skills/agents-onboarding/references/deploy-c-compose.md +64 -0
- package/dist/skills/agents-onboarding/samples/agents/README.md +23 -0
- package/dist/skills/agents-onboarding/samples/agents/maria-clinica-moreira.json +313 -0
- package/dist/skills/agents-onboarding/scripts/chatwoot-admin.py +248 -0
- package/dist/skills/agents-onboarding/scripts/coolify.py +552 -0
- package/dist/skills/agents-onboarding/scripts/docker-status.py +129 -0
- package/dist/skills/agents-onboarding/scripts/gen-onboarding-env.ts +187 -0
- package/dist/skills/agents-onboarding/scripts/harbor-login.py +118 -0
- package/dist/skills/agents-onboarding/scripts/langfuse-verify.py +118 -0
- package/dist/skills/agents-onboarding/scripts/portainer-brownfield.py +115 -0
- package/dist/skills/agents-onboarding/scripts/remote.py +198 -0
- package/dist/skills/agents-onboarding/scripts/sshkey.py +140 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/.env.example +30 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/README.md +65 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.coolify.yml +136 -0
- package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.yml +139 -0
- package/dist/skills/agents-onboarding/templates/docker-compose.coolify.yml +73 -0
- package/dist/skills/agents-onboarding/templates/docker-compose.portainer.yml +132 -0
- package/dist/skills/agents-onboarding/templates/docker-compose.prod.yml +85 -0
- package/dist/skills/agents-onboarding/templates/langfuse/.env.example +59 -0
- package/dist/skills/agents-onboarding/templates/langfuse/README.md +132 -0
- package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.coolify.yml +189 -0
- package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.yml +185 -0
- package/dist/skills/agents-operation/SKILL.md +42 -0
- package/dist/skills/agents-operation/gotchas.md +61 -0
- package/dist/skills/agents-operation/guardrails.md +26 -0
- package/dist/skills/agents-operation/references/00-production-safety.md +24 -0
- package/dist/skills/agents-operation/references/01-diagnose.md +34 -0
- package/dist/skills/agents-operation/references/02-reproduce.md +22 -0
- package/dist/skills/agents-operation/references/03-adjust.md +36 -0
- package/dist/skills/agents-operation/references/04-validate-and-apply.md +31 -0
- package/dist/ui-select.js +279 -0
- package/dist/ui.js +167 -0
- package/package.json +53 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hermesAdapter = exports.FAZER_SOUL_MD = exports.FAZER_SKIN_YAML = exports.HERMES_PROFILE = void 0;
|
|
4
|
+
exports.hermesProfileDir = hermesProfileDir;
|
|
5
|
+
exports.hermesSkinsDir = hermesSkinsDir;
|
|
6
|
+
exports.hermesMcpAddCommand = hermesMcpAddCommand;
|
|
7
|
+
exports.hermesMcpAddArgs = hermesMcpAddArgs;
|
|
8
|
+
exports.hermesLoggedIn = hermesLoggedIn;
|
|
9
|
+
exports.hermesProfileExists = hermesProfileExists;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_os_1 = require("node:os");
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
|
+
const exec_1 = require("../exec");
|
|
14
|
+
const mcp_1 = require("../mcp");
|
|
15
|
+
const ui_1 = require("../ui");
|
|
16
|
+
const detect_1 = require("./detect");
|
|
17
|
+
const handoff_1 = require("./handoff");
|
|
18
|
+
const hermes_skills_1 = require("./hermes-skills");
|
|
19
|
+
const shell_1 = require("./shell");
|
|
20
|
+
// Total de macro-passos do setup do Hermes (barra de progresso).
|
|
21
|
+
const HERMES_STEPS = 5;
|
|
22
|
+
// Handoff sem prompts de aprovação por-ação: `--yolo` (bypass dos prompts de comando
|
|
23
|
+
// "perigoso"). O onboarding é um fluxo DevOps guiado (ssh/docker/psql via a skill) em que
|
|
24
|
+
// aprovar cada comando trava a jornada; abrimos direto no modo auto-approve. NÃO usamos
|
|
25
|
+
// `--ignore-rules` (ele pularia a injeção do SOUL.md, que carrega o branding do Instalador).
|
|
26
|
+
const HERMES_YOLO_FLAGS = ["--yolo"];
|
|
27
|
+
// Profile dedicado do Hermes onde o onboarding roda, isolado do profile default do
|
|
28
|
+
// usuário. Validado por checksum: criar/mutar `fazer-ai` NÃO toca config/tools/skills/
|
|
29
|
+
// .env/auth do default. Tudo abaixo é escopado por `-p fazer-ai`; o `profile create`
|
|
30
|
+
// ainda cria o alias `~/.local/bin/fazer-ai` (o usuário reabre com `fazer-ai chat`).
|
|
31
|
+
exports.HERMES_PROFILE = "fazer-ai";
|
|
32
|
+
// Branding exibido no banner do profile (`branding.agent_name`). "Instalador fazer.ai" é o
|
|
33
|
+
// agente que CONDUZ o onboarding; a fazer.ai agents é o que ele constrói.
|
|
34
|
+
const AGENT_NAME = "Instalador fazer.ai";
|
|
35
|
+
// O branding "de verdade" do Hermes é uma SKIN (não só `config set branding.agent_name`).
|
|
36
|
+
// O TUI (Ink) renderiza o logo via `parseRichMarkup` (ui-tui/src/banner.ts): cada linha tem que
|
|
37
|
+
// ABRIR e FECHAR o markup `[#hex]...[/]` NA MESMA LINHA (ele dá split por \n antes), senão a tag
|
|
38
|
+
// vaza como texto literal. Logo em ANSI Shadow (a fonte do próprio Hermes), com gradiente laranja
|
|
39
|
+
// por linha. `banner_hero` é OMITIDO de propósito → cai no caduceu original do Hermes, colorido
|
|
40
|
+
// pelo tema. `agent_name` vira `t.brand.name` (theme.ts:577) → o rodapé "Hermes Agent v…" vira
|
|
41
|
+
// "Agente fazer.ai v…". O ícone ⚕ e a tagline "Nous Research · Messenger of the Digital Gods" são
|
|
42
|
+
// HARDCODED no source do TUI (theme.ts:578, branding.tsx:47), não dá pra sobrescrever via skin.
|
|
43
|
+
// Escrevemos o arquivo no skins dir do profile e ativamos via `config set display.skin`. Hermes 0.17.
|
|
44
|
+
const SKIN_NAME = "fazer-ai";
|
|
45
|
+
// Exportado só pra regressão de branding (nome do Instalador, sem em dash).
|
|
46
|
+
exports.FAZER_SKIN_YAML = `name: fazer-ai
|
|
47
|
+
description: "Tema fazer.ai · Instalador de onboarding"
|
|
48
|
+
|
|
49
|
+
branding:
|
|
50
|
+
agent_name: "Instalador fazer.ai"
|
|
51
|
+
prompt_symbol: "❯"
|
|
52
|
+
welcome: "Instalador fazer.ai: pronto pro onboarding."
|
|
53
|
+
goodbye: "Até logo, fazer.ai"
|
|
54
|
+
help_header: "Comandos · fazer.ai"
|
|
55
|
+
response_label: " ✦ fazer.ai "
|
|
56
|
+
|
|
57
|
+
banner_logo: |-
|
|
58
|
+
[#FFC078]███████╗ █████╗ ███████╗███████╗██████╗ █████╗ ██╗[/]
|
|
59
|
+
[#FFA94D]██╔════╝██╔══██╗╚══███╔╝██╔════╝██╔══██╗ ██╔══██╗██║[/]
|
|
60
|
+
[#FF922B]█████╗ ███████║ ███╔╝ █████╗ ██████╔╝ ███████║██║[/]
|
|
61
|
+
[#FD7E14]██╔══╝ ██╔══██║ ███╔╝ ██╔══╝ ██╔══██╗ ██╔══██║██║[/]
|
|
62
|
+
[#F76707]██║ ██║ ██║███████╗███████╗██║ ██║██╗██║ ██║██║[/]
|
|
63
|
+
[#E8590C]╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝[/]
|
|
64
|
+
|
|
65
|
+
colors:
|
|
66
|
+
banner_border: "#FF6A00"
|
|
67
|
+
banner_title: "#FF9A3C"
|
|
68
|
+
banner_accent: "#FF6A00"
|
|
69
|
+
banner_dim: "#C2740F"
|
|
70
|
+
banner_text: "#F5F5F5"
|
|
71
|
+
ui_accent: "#FF6A00"
|
|
72
|
+
ui_label: "#FF9A3C"
|
|
73
|
+
ui_ok: "#5CB85C"
|
|
74
|
+
ui_warn: "#E0A030"
|
|
75
|
+
ui_error: "#E0533C"
|
|
76
|
+
prompt: "#FF6A00"
|
|
77
|
+
session_label: "#C2740F"
|
|
78
|
+
session_border: "#C2740F"
|
|
79
|
+
`;
|
|
80
|
+
// Raiz de um profile do Hermes. Resolve a raiz do Hermes via HERMES_HOME quando
|
|
81
|
+
// definido (o instalador Windows o seta como %LOCALAPPDATA%\hermes), senão ~/.hermes
|
|
82
|
+
// (default Linux/macOS). Espelha a resolução do próprio Hermes: sem isto os arquivos
|
|
83
|
+
// iriam pra ~/.hermes no Windows e o agente (que lê de HERMES_HOME) não os acharia.
|
|
84
|
+
// Pura (testável).
|
|
85
|
+
function hermesProfileDir(profile, env = process.env, home = (0, node_os_1.homedir)()) {
|
|
86
|
+
const root = env.HERMES_HOME?.trim() || (0, node_path_1.join)(home, ".hermes");
|
|
87
|
+
return (0, node_path_1.join)(root, "profiles", profile);
|
|
88
|
+
}
|
|
89
|
+
// Diretório de skins do profile.
|
|
90
|
+
function hermesSkinsDir(profile, env = process.env, home = (0, node_os_1.homedir)()) {
|
|
91
|
+
return (0, node_path_1.join)(hermesProfileDir(profile, env, home), "skins");
|
|
92
|
+
}
|
|
93
|
+
// Identidade do agente (SOUL.md). O Hermes injeta o `SOUL.md` da raiz do profile no
|
|
94
|
+
// system prompt (get_hermes_home()/SOUL.md); o default nasce "You are Hermes Agent,
|
|
95
|
+
// created by Nous Research", o que causava o flash HERMES→fazer.ai ao abrir. Escrevemos
|
|
96
|
+
// a identidade do Instalador fazer.ai por cima pra ele nascer como o instalador, sem
|
|
97
|
+
// menção a Hermes/Nous.
|
|
98
|
+
exports.FAZER_SOUL_MD = `Você é o Instalador fazer.ai, o assistente que conduz o onboarding da fazer.ai agents (a plataforma de agentes de atendimento) no computador do usuário. Você provisiona infraestrutura, faz o deploy do Chatwoot, da fazer.ai agents e do Langfuse, e configura o agente de atendimento, seguindo a skill de onboarding. Seja claro, direto e prático; assuma incerteza quando for o caso e priorize ser genuinamente útil. Comunique cada passo em português do Brasil.`;
|
|
99
|
+
// Escreve a skin no skins dir do profile (best-effort: falha não bloqueia o onboarding).
|
|
100
|
+
function writeFazerSkin() {
|
|
101
|
+
const skinsDir = hermesSkinsDir(exports.HERMES_PROFILE);
|
|
102
|
+
(0, node_fs_1.mkdirSync)(skinsDir, { recursive: true });
|
|
103
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(skinsDir, `${SKIN_NAME}.yaml`), exports.FAZER_SKIN_YAML, "utf8");
|
|
104
|
+
}
|
|
105
|
+
// Escreve a identidade do Instalador fazer.ai no SOUL.md da raiz do profile
|
|
106
|
+
// (best-effort). Mata o flash HERMES→fazer.ai: sem isto o agente abre se
|
|
107
|
+
// apresentando como "Hermes Agent, by Nous Research".
|
|
108
|
+
function writeFazerSoul() {
|
|
109
|
+
const dir = hermesProfileDir(exports.HERMES_PROFILE);
|
|
110
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
111
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, "SOUL.md"), `${exports.FAZER_SOUL_MD}\n`, "utf8");
|
|
112
|
+
}
|
|
113
|
+
// Instala as skills OFFLINE a partir do bundle empacotado no CLI (caminho primário: sem
|
|
114
|
+
// rede, sem rate-limit do GitHub anônimo). Escreve os arquivos no profile + a entrada no
|
|
115
|
+
// lock.json com o content_hash correto. Devolve true se instalou pelo bundle; false se o
|
|
116
|
+
// bundle não está disponível (o chamador cai no fallback de rede). Uma skill que falhe não
|
|
117
|
+
// derruba as outras. `verbose` mostra o content_hash por skill.
|
|
118
|
+
function installSkillsOffline(ctx, bundleRoot) {
|
|
119
|
+
const { config, log } = ctx;
|
|
120
|
+
let ok = 0;
|
|
121
|
+
for (const ref of config.hermesSkillRefs) {
|
|
122
|
+
const skill = (0, hermes_skills_1.skillNameFromRef)(ref);
|
|
123
|
+
try {
|
|
124
|
+
const result = (0, hermes_skills_1.installBundledSkill)({
|
|
125
|
+
profileDir: hermesProfileDir(exports.HERMES_PROFILE),
|
|
126
|
+
bundledSkillDir: (0, node_path_1.join)(bundleRoot, skill),
|
|
127
|
+
skill,
|
|
128
|
+
tapRepo: config.hermesTapRepo,
|
|
129
|
+
});
|
|
130
|
+
ok++;
|
|
131
|
+
if (ctx.verbose) {
|
|
132
|
+
log(` ${ui_1.sym.bar} ${skill}: ${result.files} arquivo(s), ${result.contentHash}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
137
|
+
log(`${ui_1.sym.warn} Não consegui instalar a skill "${skill}" do pacote (${msg}); ela pode faltar no agente.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return ok > 0;
|
|
141
|
+
}
|
|
142
|
+
// Fallback de rede: registra o tap (GitHub) e instala as skills rastreadas via o CLI do
|
|
143
|
+
// Hermes. Só entra quando o bundle offline não está disponível. É este caminho que bate no
|
|
144
|
+
// skills.sh/GitHub anônimo (rate-limit 60/h), por isso é secundário.
|
|
145
|
+
async function installSkillsFromNetwork(ctx) {
|
|
146
|
+
const { config, log } = ctx;
|
|
147
|
+
try {
|
|
148
|
+
await (0, exec_1.run)("hermes", withProfile(["skills", "tap", "add", config.hermesTapRepo]), { quiet: !ctx.verbose });
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
152
|
+
log(`${ui_1.sym.warn} Não consegui registrar o tap "${config.hermesTapRepo}" (${msg}); as skills podem não instalar.`);
|
|
153
|
+
}
|
|
154
|
+
for (const ref of config.hermesSkillRefs) {
|
|
155
|
+
try {
|
|
156
|
+
await (0, exec_1.run)("hermes", withProfile(["skills", "install", ref, "--yes", "--force"]), { quiet: !ctx.verbose });
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
160
|
+
log(`${ui_1.sym.warn} Não consegui instalar a skill "${ref}" (${msg}); rode \`hermes -p ${exports.HERMES_PROFILE} skills install ${ref} --yes --force\` manualmente.`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// O Hermes liga ~16 toolsets embutidos por padrão; o onboarding precisa só de
|
|
165
|
+
// terminal/file/code_execution (SSH + docker + editar config) e skills (roda a própria
|
|
166
|
+
// skill). Os MCP (Hostinger/hub) são gerenciados à parte e seguem ligados. Desligamos o
|
|
167
|
+
// resto no profile pra cortar contexto/custo/superfície. `tools disable` é idempotente.
|
|
168
|
+
// NOTE: `clarify` (pergunta estruturada multiple-choice, navegável por setas, equivalente ao
|
|
169
|
+
// AskUserQuestion do Claude) fica LIGADO de propósito: o onboarding pergunta melhor por ele do
|
|
170
|
+
// que listando em texto.
|
|
171
|
+
const DISABLE_TOOLSETS = [
|
|
172
|
+
"web",
|
|
173
|
+
"browser",
|
|
174
|
+
"vision",
|
|
175
|
+
"video",
|
|
176
|
+
"image_gen",
|
|
177
|
+
"video_gen",
|
|
178
|
+
"tts",
|
|
179
|
+
"x_search",
|
|
180
|
+
"moa",
|
|
181
|
+
"context_engine",
|
|
182
|
+
"memory",
|
|
183
|
+
"session_search",
|
|
184
|
+
"delegation",
|
|
185
|
+
"cronjob",
|
|
186
|
+
"computer_use",
|
|
187
|
+
"homeassistant",
|
|
188
|
+
"spotify",
|
|
189
|
+
"yuanbao",
|
|
190
|
+
"todo",
|
|
191
|
+
];
|
|
192
|
+
// `-p <profile>` é flag GLOBAL e precisa vir ANTES do subcomando (vale pra qualquer um).
|
|
193
|
+
function withProfile(args) {
|
|
194
|
+
return ["-p", exports.HERMES_PROFILE, ...args];
|
|
195
|
+
}
|
|
196
|
+
// Projeta um McpServerSpec no `hermes -p fazer-ai mcp add` (validado no Hermes 0.17).
|
|
197
|
+
// stdio: `--command`/`--args` (--args é greedy → ÚLTIMA opção) + `--env KEY=VALUE`;
|
|
198
|
+
// http: `--url --auth oauth` (depois `mcp login <name>`).
|
|
199
|
+
function hermesMcpAddCommand(spec) {
|
|
200
|
+
if (spec.transport === "http") {
|
|
201
|
+
return `hermes -p ${exports.HERMES_PROFILE} mcp add ${spec.name} --url ${spec.url} --auth oauth`;
|
|
202
|
+
}
|
|
203
|
+
const envFlags = Object.entries(spec.env ?? {})
|
|
204
|
+
.map(([key, value]) => {
|
|
205
|
+
const shown = spec.secretEnvKeys?.includes(key) ? shell_1.REDACTED : value;
|
|
206
|
+
return `--env ${key}=${(0, shell_1.shellQuote)(shown)}`;
|
|
207
|
+
})
|
|
208
|
+
.join(" ");
|
|
209
|
+
const args = (spec.args ?? []).map(shell_1.shellQuote).join(" ");
|
|
210
|
+
const command = spec.command ?? "";
|
|
211
|
+
return `hermes -p ${exports.HERMES_PROFILE} mcp add ${spec.name}${envFlags ? ` ${envFlags}` : ""} --command ${command} --args ${args}`;
|
|
212
|
+
}
|
|
213
|
+
// Mesmo comando, como argv para spawn (sem shell).
|
|
214
|
+
function hermesMcpAddArgs(spec) {
|
|
215
|
+
if (spec.transport === "http") {
|
|
216
|
+
return withProfile([
|
|
217
|
+
"mcp",
|
|
218
|
+
"add",
|
|
219
|
+
spec.name,
|
|
220
|
+
"--url",
|
|
221
|
+
spec.url ?? "",
|
|
222
|
+
"--auth",
|
|
223
|
+
"oauth",
|
|
224
|
+
]);
|
|
225
|
+
}
|
|
226
|
+
const envFlags = Object.entries(spec.env ?? {}).flatMap(([key, value]) => [
|
|
227
|
+
"--env",
|
|
228
|
+
`${key}=${value}`,
|
|
229
|
+
]);
|
|
230
|
+
// `--args` consome o resto, então fica por último.
|
|
231
|
+
return withProfile([
|
|
232
|
+
"mcp",
|
|
233
|
+
"add",
|
|
234
|
+
spec.name,
|
|
235
|
+
...envFlags,
|
|
236
|
+
"--command",
|
|
237
|
+
spec.command ?? "",
|
|
238
|
+
"--args",
|
|
239
|
+
...(spec.args ?? []),
|
|
240
|
+
]);
|
|
241
|
+
}
|
|
242
|
+
// `hermes -p fazer-ai auth list` enumera as credenciais de provider pooladas (o profile
|
|
243
|
+
// herda o login global por fallback read-only). Cada credencial é uma linha `#N`, então
|
|
244
|
+
// ao menos uma ⇒ logado.
|
|
245
|
+
function hermesLoggedIn(authListOutput) {
|
|
246
|
+
return /^\s*#\d+/m.test(authListOutput);
|
|
247
|
+
}
|
|
248
|
+
// O profile `fazer-ai` já existe? `profile list` imprime uma linha por profile com o
|
|
249
|
+
// nome na 1ª coluna. `profile create` NÃO é idempotente (erra se já existe), então
|
|
250
|
+
// guardamos a criação com isto.
|
|
251
|
+
function hermesProfileExists(profileListOutput) {
|
|
252
|
+
return new RegExp(`(^|\\s)${exports.HERMES_PROFILE}(\\s|$)`, "m").test(profileListOutput);
|
|
253
|
+
}
|
|
254
|
+
// Cria o profile `fazer-ai` se faltar (idempotente sobre o `profile create`, que erra
|
|
255
|
+
// se já existe). `--no-skills` cria vazio e já opta fora do sync de bundled skills do
|
|
256
|
+
// `hermes update` (as nossas skills vêm pelo install nativo, à parte).
|
|
257
|
+
async function ensureProfile() {
|
|
258
|
+
const { stdout } = await (0, exec_1.capture)("hermes", ["profile", "list"]);
|
|
259
|
+
if (hermesProfileExists(stdout))
|
|
260
|
+
return;
|
|
261
|
+
await (0, exec_1.run)("hermes", [
|
|
262
|
+
"profile",
|
|
263
|
+
"create",
|
|
264
|
+
exports.HERMES_PROFILE,
|
|
265
|
+
"--no-skills",
|
|
266
|
+
"--description",
|
|
267
|
+
"Onboarding do fazer.ai agents (Instalador fazer.ai)",
|
|
268
|
+
], { quiet: true });
|
|
269
|
+
}
|
|
270
|
+
// O `hermes setup model` abre um picker do "free tool pool" da Nous (tools hospedados:
|
|
271
|
+
// Firecrawl/FAL/TTS/Whisper/Browser) sempre que o provider é Nous + há entitlement e algum
|
|
272
|
+
// tool coberto está "unconfigured"; NÃO há flag de "dismiss" (nous_subscription.py:920-975,
|
|
273
|
+
// 1051-1073). A única supressão sem trocar de provider é marcar cada seção como use_gateway=true
|
|
274
|
+
// (→ "already_managed" → o picker não aparece). Rodamos isso ANTES do setup model. Os tools com
|
|
275
|
+
// toolset próprio (web/image_gen/browser/tts) seguem cortados pelo `tools disable` do execute
|
|
276
|
+
// (model_tools.py:392-430: a subtração de toolset vence o use_gateway). O STT (transcribe_audio)
|
|
277
|
+
// não tem toolset estático nem é core, então o disable não o alcança; confirmar com `tools list`.
|
|
278
|
+
// Verificado no Hermes 0.17.
|
|
279
|
+
const TOOL_GATEWAY_KEYS = [
|
|
280
|
+
"web",
|
|
281
|
+
"image_gen",
|
|
282
|
+
"video_gen",
|
|
283
|
+
"tts",
|
|
284
|
+
"stt",
|
|
285
|
+
"browser",
|
|
286
|
+
];
|
|
287
|
+
async function suppressToolPoolPrompt() {
|
|
288
|
+
for (const key of TOOL_GATEWAY_KEYS) {
|
|
289
|
+
try {
|
|
290
|
+
await (0, exec_1.run)("hermes", withProfile(["config", "set", `${key}.use_gateway`, "true"]), { quiet: true });
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// best-effort: se falhar, o picker pode aparecer (o usuário desmarca); não trava o login.
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// NOTE: Comandos validados contra o Hermes CLI real (0.17), incl. install E2E: `skills tap
|
|
298
|
+
// add <owner/repo>` + `skills install <owner/repo/skill> --yes --force` (rastreado no lock →
|
|
299
|
+
// `skills update`). O `--force` passa o verdict `caution` do scanner (conteúdo DevOps:
|
|
300
|
+
// ssh/sudo/env); um verdict `dangerous` NÃO seria passável, por isso a skill evita findings
|
|
301
|
+
// `critical` (ver gotchas/docs sem `$TOKEN`/`$KEY` em exemplos).
|
|
302
|
+
exports.hermesAdapter = {
|
|
303
|
+
id: "hermes",
|
|
304
|
+
displayName: AGENT_NAME,
|
|
305
|
+
automated: true,
|
|
306
|
+
skillInvoker: "/",
|
|
307
|
+
detectCommand: "hermes --version",
|
|
308
|
+
installCommand: {
|
|
309
|
+
posix: "curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash",
|
|
310
|
+
win32: "iex (irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1)",
|
|
311
|
+
},
|
|
312
|
+
marketplaceSource: (config) => config.marketplaceAddTarget,
|
|
313
|
+
detect() {
|
|
314
|
+
return (0, detect_1.detectVersion)("hermes", ["--version"]);
|
|
315
|
+
},
|
|
316
|
+
// Logado? O profile herda o login global (fallback read-only do auth.json). Sem saída
|
|
317
|
+
// e exit != 0 ⇒ indeterminado (o fluxo oferece o login em vez de afirmar que falta).
|
|
318
|
+
async configured() {
|
|
319
|
+
// Não toca num profile inexistente (evitando auto-criação sem `--no-skills`): se o
|
|
320
|
+
// profile ainda não existe, `false` → o login cria certo e abre só o `setup model`.
|
|
321
|
+
const list = await (0, exec_1.capture)("hermes", ["profile", "list"]);
|
|
322
|
+
if (!hermesProfileExists(list.stdout))
|
|
323
|
+
return false;
|
|
324
|
+
const { code, stdout } = await (0, exec_1.capture)("hermes", withProfile(["auth", "list"]));
|
|
325
|
+
if (code !== 0 && !stdout.trim())
|
|
326
|
+
return undefined;
|
|
327
|
+
return hermesLoggedIn(stdout);
|
|
328
|
+
},
|
|
329
|
+
// Garante o profile e abre só a seção de modelo do wizard (provider/modelo é a única
|
|
330
|
+
// escolha do usuário). NÃO roda `hermes setup` completo (que perguntaria gateways/tools).
|
|
331
|
+
async login() {
|
|
332
|
+
await ensureProfile();
|
|
333
|
+
await suppressToolPoolPrompt();
|
|
334
|
+
await (0, exec_1.runInteractive)("hermes", withProfile(["setup", "model"]));
|
|
335
|
+
},
|
|
336
|
+
plan(ctx) {
|
|
337
|
+
const { config } = ctx;
|
|
338
|
+
const specs = (0, mcp_1.bootstrapMcpSpecs)({
|
|
339
|
+
config,
|
|
340
|
+
provider: ctx.provider,
|
|
341
|
+
hostingerTokenProvided: ctx.hostingerTokenProvided,
|
|
342
|
+
});
|
|
343
|
+
const mcpCommands = specs.map(hermesMcpAddCommand);
|
|
344
|
+
const httpSpec = specs.find((s) => s.transport === "http");
|
|
345
|
+
if (httpSpec) {
|
|
346
|
+
mcpCommands.push(`hermes -p ${exports.HERMES_PROFILE} mcp login ${httpSpec.name}`);
|
|
347
|
+
}
|
|
348
|
+
return [
|
|
349
|
+
{
|
|
350
|
+
title: `Criar o profile isolado "${exports.HERMES_PROFILE}" (Instalador fazer.ai)`,
|
|
351
|
+
},
|
|
352
|
+
{ title: "Escolher o provider/modelo do agente" },
|
|
353
|
+
{
|
|
354
|
+
title: ctx.provider === "hostinger"
|
|
355
|
+
? "Conectar as ferramentas (DNS, VPS, domínio)"
|
|
356
|
+
: "Conectar as ferramentas de infraestrutura",
|
|
357
|
+
commands: mcpCommands,
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
title: "Enxugar as ferramentas e aplicar o branding (skin fazer.ai)",
|
|
361
|
+
commands: [
|
|
362
|
+
`hermes -p ${exports.HERMES_PROFILE} tools disable ${DISABLE_TOOLSETS.join(" ")}`,
|
|
363
|
+
`hermes -p ${exports.HERMES_PROFILE} config set lsp.enabled false`,
|
|
364
|
+
`hermes -p ${exports.HERMES_PROFILE} config set branding.agent_name ${(0, shell_1.shellQuote)(AGENT_NAME)}`,
|
|
365
|
+
`hermes -p ${exports.HERMES_PROFILE} config set display.skin ${SKIN_NAME}`,
|
|
366
|
+
],
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
// As skills vêm empacotadas no CLI e são instaladas OFFLINE (arquivos + lock.json),
|
|
370
|
+
// sem bater no GitHub anônimo; o `tap add`/`skills install` fica só de fallback.
|
|
371
|
+
title: "Instalar as skills do fazer.ai agents (offline, do pacote; atualizáveis)",
|
|
372
|
+
},
|
|
373
|
+
{ title: "Abrir o Instalador fazer.ai no onboarding" },
|
|
374
|
+
];
|
|
375
|
+
},
|
|
376
|
+
async execute(ctx) {
|
|
377
|
+
const { config, log } = ctx;
|
|
378
|
+
const phase = (n, label) => log((0, ui_1.renderProgress)(n, HERMES_STEPS, label));
|
|
379
|
+
phase(1, "Preparando o profile do Instalador fazer.ai");
|
|
380
|
+
await ensureProfile();
|
|
381
|
+
const specs = (0, mcp_1.bootstrapMcpSpecs)({
|
|
382
|
+
config,
|
|
383
|
+
provider: ctx.provider,
|
|
384
|
+
hostingerTokenProvided: ctx.hostingerTokenProvided,
|
|
385
|
+
hostingerToken: ctx.hostingerToken,
|
|
386
|
+
});
|
|
387
|
+
phase(2, specs.length === 0
|
|
388
|
+
? "Sem ferramentas extras a conectar (provider externo)"
|
|
389
|
+
: "Conectando as ferramentas (DNS, VPS, domínio)");
|
|
390
|
+
if ((0, mcp_1.hasHostingerSpecs)(specs))
|
|
391
|
+
await (0, mcp_1.installHostingerMcp)(log);
|
|
392
|
+
for (const spec of specs) {
|
|
393
|
+
// `mcp add` pode perguntar "Overwrite?" e "Enable all tools?"; respondemos "Y" às
|
|
394
|
+
// duas pelo stdin (sem isso vira "Cancelled" e o MCP não é salvo).
|
|
395
|
+
await (0, exec_1.runWithInput)("hermes", hermesMcpAddArgs(spec), "Y\nY\n", {
|
|
396
|
+
quiet: !ctx.verbose,
|
|
397
|
+
});
|
|
398
|
+
if (spec.transport === "http") {
|
|
399
|
+
await (0, exec_1.runInteractive)("hermes", withProfile(["mcp", "login", spec.name]));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
phase(3, "Preparando o agente e aplicando o branding");
|
|
403
|
+
await (0, exec_1.run)("hermes", withProfile(["tools", "disable", ...DISABLE_TOOLSETS]), {
|
|
404
|
+
quiet: !ctx.verbose,
|
|
405
|
+
});
|
|
406
|
+
// NOTE: o onboarding roda comandos de instalação, não edita código, então o LSP/lint
|
|
407
|
+
// do Hermes é só ruído aqui (no Windows o spawn dos language-servers falha com
|
|
408
|
+
// WinError 193; o LSP acorda sozinho quando o cwd é um repo git). Desligamos o
|
|
409
|
+
// subsistema inteiro. Best-effort: a chave ausente numa versão antiga não bloqueia.
|
|
410
|
+
try {
|
|
411
|
+
await (0, exec_1.run)("hermes", withProfile(["config", "set", "lsp.enabled", "false"]), {
|
|
412
|
+
quiet: !ctx.verbose,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
// best-effort: sem o disable o agente ainda abre (só com o ruído de LSP no Windows).
|
|
417
|
+
}
|
|
418
|
+
await (0, exec_1.run)("hermes", withProfile(["config", "set", "branding.agent_name", AGENT_NAME]), { quiet: !ctx.verbose });
|
|
419
|
+
// Branding completo: skin (logo ASCII fazer.ai + cores + labels) e a identidade
|
|
420
|
+
// (SOUL.md) do Instalador fazer.ai, que mata o flash HERMES→fazer.ai ao abrir.
|
|
421
|
+
try {
|
|
422
|
+
writeFazerSkin();
|
|
423
|
+
writeFazerSoul();
|
|
424
|
+
await (0, exec_1.run)("hermes", withProfile(["config", "set", "display.skin", SKIN_NAME]), { quiet: !ctx.verbose });
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// best-effort: sem a skin/soul o agente ainda abre (só sem o branding completo).
|
|
428
|
+
}
|
|
429
|
+
// Pré-build do TUI em paralelo com a instalação da skill (sem contenção).
|
|
430
|
+
let tuiBuild = null;
|
|
431
|
+
if (ctx.handoff) {
|
|
432
|
+
tuiBuild = (0, exec_1.run)("hermes", withProfile(["chat", "--tui"]), {
|
|
433
|
+
quiet: true,
|
|
434
|
+
}).catch(() => { });
|
|
435
|
+
}
|
|
436
|
+
phase(4, "Instalando as skills do fazer.ai agents");
|
|
437
|
+
// Caminho PRIMÁRIO: instala as skills OFFLINE do bundle empacotado no CLI (sem rede,
|
|
438
|
+
// sem o rate-limit do GitHub anônimo que estourava aqui). Escreve os arquivos no profile
|
|
439
|
+
// + a entrada no lock.json (rastreável → `hermes skills update` depois). Só cai no
|
|
440
|
+
// fallback de rede (tap add + skills install) se o bundle não vier no pacote.
|
|
441
|
+
const bundleRoot = (0, hermes_skills_1.bundledSkillsRoot)();
|
|
442
|
+
const installedOffline = bundleRoot
|
|
443
|
+
? installSkillsOffline(ctx, bundleRoot)
|
|
444
|
+
: false;
|
|
445
|
+
if (!installedOffline) {
|
|
446
|
+
log(bundleRoot
|
|
447
|
+
? `${ui_1.sym.warn} Falha ao instalar as skills do pacote; tentando pela rede (pode esbarrar no rate-limit do GitHub).`
|
|
448
|
+
: `${ui_1.sym.warn} Sem skills empacotadas; instalando pela rede (pode esbarrar no rate-limit do GitHub).`);
|
|
449
|
+
await installSkillsFromNetwork(ctx);
|
|
450
|
+
}
|
|
451
|
+
if (ctx.handoff) {
|
|
452
|
+
const env = ctx.hostingerToken
|
|
453
|
+
? { ...process.env, HOSTINGER_API_TOKEN: ctx.hostingerToken }
|
|
454
|
+
: process.env;
|
|
455
|
+
if (tuiBuild) {
|
|
456
|
+
await tuiBuild;
|
|
457
|
+
}
|
|
458
|
+
phase(5, "Abrindo o Instalador fazer.ai no onboarding");
|
|
459
|
+
const launched = await (0, exec_1.runHandoff)("hermes", withProfile([
|
|
460
|
+
"chat",
|
|
461
|
+
"--tui",
|
|
462
|
+
...HERMES_YOLO_FLAGS,
|
|
463
|
+
"-q",
|
|
464
|
+
(0, handoff_1.handoffPrompt)("/", config.onboardingSkill, ctx.provider),
|
|
465
|
+
]), { env });
|
|
466
|
+
if (!launched) {
|
|
467
|
+
log(`${ui_1.sym.ok} Tudo pronto. Abra o Instalador e invoque a skill: rode \`fazer-ai chat --tui\` e digite \`/${config.onboardingSkill}\`.`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
phase(5, "Tudo pronto. Para abrir depois: fazer-ai chat --tui");
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.AGENTS = void 0;
|
|
18
|
+
exports.getAgent = getAgent;
|
|
19
|
+
exports.installCommandFor = installCommandFor;
|
|
20
|
+
exports.detectAgents = detectAgents;
|
|
21
|
+
const claude_1 = require("./claude");
|
|
22
|
+
const codex_1 = require("./codex");
|
|
23
|
+
const hermes_1 = require("./hermes");
|
|
24
|
+
const other_1 = require("./other");
|
|
25
|
+
__exportStar(require("./types"), exports);
|
|
26
|
+
// NOTE: a ordem desta lista é a ordem do menu e o critério de desempate do default
|
|
27
|
+
// (primeiro instalado). Codex e Claude Code (adapters de referência) vêm primeiro,
|
|
28
|
+
// depois o Instalador fazer.ai (Hermes), todos automatizados (detectam, planejam e
|
|
29
|
+
// executam o setup). O Instalador fazer.ai carrega o selo "recomendado" no picker
|
|
30
|
+
// mesmo em 3º (ver selectAgent em index.ts). "other" é o genérico (sem automação, só
|
|
31
|
+
// imprime o prompt pra mandar ao agente) e fica por último; também com --agent other.
|
|
32
|
+
exports.AGENTS = [
|
|
33
|
+
codex_1.codexAdapter,
|
|
34
|
+
claude_1.claudeAdapter,
|
|
35
|
+
hermes_1.hermesAdapter,
|
|
36
|
+
other_1.otherAdapter,
|
|
37
|
+
];
|
|
38
|
+
function getAgent(id) {
|
|
39
|
+
return exports.AGENTS.find((agent) => agent.id === id);
|
|
40
|
+
}
|
|
41
|
+
// Resolve o `installCommand` de um adapter para a plataforma atual. String =
|
|
42
|
+
// só POSIX (sem auto-install no Windows). Objeto = escolhe por SO; `win32`
|
|
43
|
+
// ausente → undefined (sem instalador Windows conhecido). Função pura (testável).
|
|
44
|
+
function installCommandFor(installCommand, platform = process.platform) {
|
|
45
|
+
if (installCommand === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
if (typeof installCommand === "string") {
|
|
48
|
+
return platform === "win32" ? undefined : installCommand;
|
|
49
|
+
}
|
|
50
|
+
return platform === "win32" ? installCommand.win32 : installCommand.posix;
|
|
51
|
+
}
|
|
52
|
+
async function detectAgents() {
|
|
53
|
+
return Promise.all(exports.AGENTS.map(async (adapter) => ({
|
|
54
|
+
adapter,
|
|
55
|
+
result: await adapter.detect(),
|
|
56
|
+
})));
|
|
57
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.manualExecute = manualExecute;
|
|
4
|
+
// Adapter genérico ("outro"): não há automação específica de agente. Como o plano
|
|
5
|
+
// não é impresso fora do dry-run, renderizamos aqui os passos acionáveis, e no
|
|
6
|
+
// fim, o prompt pronto para copiar e colar no agente que o usuário for usar.
|
|
7
|
+
async function manualExecute(adapter, ctx) {
|
|
8
|
+
const steps = adapter.plan({
|
|
9
|
+
config: ctx.config,
|
|
10
|
+
provider: ctx.provider,
|
|
11
|
+
hostingerTokenProvided: ctx.hostingerTokenProvided,
|
|
12
|
+
});
|
|
13
|
+
ctx.log("\nAgente genérico: sem automação. Faça estes passos no agente que você for usar:\n");
|
|
14
|
+
steps.forEach((step, i) => {
|
|
15
|
+
ctx.log(` ${i + 1}. ${step.title}`);
|
|
16
|
+
if (step.detail)
|
|
17
|
+
ctx.log(` ${step.detail}`);
|
|
18
|
+
for (const cmd of step.commands ?? []) {
|
|
19
|
+
ctx.log(`\n → copie e cole no seu agente:\n\n ${cmd}\n`);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.otherAdapter = void 0;
|
|
4
|
+
const handoff_1 = require("./handoff");
|
|
5
|
+
const manual_1 = require("./manual");
|
|
6
|
+
// NOTE: Agente genérico. Sem setup agente-específico (não conhecemos o formato de
|
|
7
|
+
// MCP/marketplace do agente). O CLI só imprime o que o usuário deve fazer e o
|
|
8
|
+
// prompt para mandar ao agente que ele for usar.
|
|
9
|
+
exports.otherAdapter = {
|
|
10
|
+
id: "other",
|
|
11
|
+
displayName: "Outro agente",
|
|
12
|
+
automated: false,
|
|
13
|
+
skillInvoker: "",
|
|
14
|
+
detectCommand: "",
|
|
15
|
+
detect() {
|
|
16
|
+
// Sem binário a detectar; é sempre uma opção válida (apenas instrui).
|
|
17
|
+
return Promise.resolve({ installed: true });
|
|
18
|
+
},
|
|
19
|
+
plan(ctx) {
|
|
20
|
+
const { config } = ctx;
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
title: "Sem setup automático (agente genérico)",
|
|
24
|
+
detail: "Para um agente não suportado, o CLI não injeta MCP nem instala o bundle; configure-os conforme a doc do seu agente (a página de conexões do hub tem os snippets genéricos de MCP).",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
title: "Obtenha o bundle de onboarding",
|
|
28
|
+
detail: `Instale/clone a skill ${config.onboardingSkill} do repo público ${config.marketplaceAddTarget} (GitHub) conforme o seu agente.`,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: "Mande este prompt ao seu agente",
|
|
32
|
+
commands: [(0, handoff_1.handoffPrompt)("", config.onboardingSkill, ctx.provider)],
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
},
|
|
36
|
+
execute(ctx) {
|
|
37
|
+
return (0, manual_1.manualExecute)(exports.otherAdapter, ctx);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Utilitários de formatação de comando shell compartilhados pelos adapters.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.REDACTED = void 0;
|
|
5
|
+
exports.shellQuote = shellQuote;
|
|
6
|
+
/** Marcador exibido no lugar de valores de env secretos no plano. */
|
|
7
|
+
exports.REDACTED = "<redacted>";
|
|
8
|
+
// Aspas simples POSIX só quando o valor tem caractere fora do conjunto seguro
|
|
9
|
+
// (inclui `=` para `--package=...` não sair com aspas); escapa aspas simples
|
|
10
|
+
// embutidas. Mantém os comandos exibidos legíveis.
|
|
11
|
+
function shellQuote(value) {
|
|
12
|
+
if (/^[A-Za-z0-9_./:@=-]+$/.test(value))
|
|
13
|
+
return value;
|
|
14
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
15
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Resolução de configuração de runtime a partir de variáveis de ambiente, com
|
|
3
|
+
// defaults seguros. Espelha o override `FAZER_AI_HUB_URL` do `@fazer-ai/setup`
|
|
4
|
+
// para que o mesmo fluxo aponte para um hub local/staging durante o desenvolvimento.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveConfig = resolveConfig;
|
|
7
|
+
exports.asProvider = asProvider;
|
|
8
|
+
const DEFAULT_HUB_BASE_URL = "https://app.fazer.ai";
|
|
9
|
+
// As 3 skills do fazer.ai agents publicadas no repo dedicado (plugin `agents`). O Claude/Codex
|
|
10
|
+
// instalam o plugin (traz as 3 de uma vez); o Hermes instala por skill, então precisa da lista.
|
|
11
|
+
// Sem ela só a de onboarding entrava, divergindo dos outros agentes.
|
|
12
|
+
const AGENTS_SKILLS = [
|
|
13
|
+
"agents-onboarding",
|
|
14
|
+
"agents-dev",
|
|
15
|
+
"agents-operation",
|
|
16
|
+
];
|
|
17
|
+
function stripTrailingSlash(value) {
|
|
18
|
+
return value.replace(/\/+$/, "");
|
|
19
|
+
}
|
|
20
|
+
function resolveConfig(env = process.env) {
|
|
21
|
+
const hubBaseUrl = stripTrailingSlash(env.FAZER_AI_HUB_URL?.trim() || DEFAULT_HUB_BASE_URL);
|
|
22
|
+
const hermesTapRepo = env.AGENTS_HERMES_TAP_REPO?.trim() || "fazer-ai/agents-skills";
|
|
23
|
+
return {
|
|
24
|
+
hubBaseUrl,
|
|
25
|
+
// NOTE: Repo dedicado da skill (marketplace co-localizado, `source: "./"`). Claude/Codex
|
|
26
|
+
// dão `plugin marketplace add` nele e instalam direto do clone, sem npm. Sobrescrevível.
|
|
27
|
+
marketplaceAddTarget: env.AGENTS_MARKETPLACE_TARGET?.trim() || "fazer-ai/agents-skills",
|
|
28
|
+
// NOTE: Nome do marketplace no `marketplace.json` do repo. "agents", NÃO "fazer-ai":
|
|
29
|
+
// colidiria com o marketplace pago fazer-ai/skills (Claude/Codex chaveiam por nome).
|
|
30
|
+
marketplaceName: env.AGENTS_MARKETPLACE_NAME?.trim() || "agents",
|
|
31
|
+
onboardingPlugin: env.AGENTS_PLUGIN?.trim() || "agents",
|
|
32
|
+
onboardingSkill: env.AGENTS_SKILL?.trim() || "agents-onboarding",
|
|
33
|
+
// NOTE: Identifiers `owner/repo/skill` pro `hermes skills install` (o Hermes mira
|
|
34
|
+
// `skills/<skill>/` no repo, uma skill por install). As 3 = paridade com o plugin
|
|
35
|
+
// Claude/Codex. O tap registra o repo antes. Sobrescrevível (CSV em
|
|
36
|
+
// AGENTS_HERMES_SKILL_REFS).
|
|
37
|
+
hermesSkillRefs: (env.AGENTS_HERMES_SKILL_REFS?.trim()
|
|
38
|
+
? env.AGENTS_HERMES_SKILL_REFS.split(",").map((s) => s.trim())
|
|
39
|
+
: AGENTS_SKILLS.map((name) => `${hermesTapRepo}/${name}`)).filter(Boolean),
|
|
40
|
+
// NOTE: Repo `owner/repo` pro `hermes skills tap add` (registra o repo público como fonte github).
|
|
41
|
+
hermesTapRepo,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function asProvider(value) {
|
|
45
|
+
if (value === "hostinger" || value === "other")
|
|
46
|
+
return value;
|
|
47
|
+
throw new Error(`provider inválido: ${value} (use "hostinger" ou "other")`);
|
|
48
|
+
}
|