iron-edge 2.4.0 → 2.4.2
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 +7 -6
- package/agents/iron-auditor.md +5 -34
- package/installer/office.js +60 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -225,10 +225,11 @@ Antes, faça o **passo 4** do onboarding do Iron Office (ele salva a sua chave e
|
|
|
225
225
|
|
|
226
226
|
O que esperar:
|
|
227
227
|
|
|
228
|
-
1. O comando
|
|
229
|
-
2.
|
|
230
|
-
3.
|
|
231
|
-
4.
|
|
228
|
+
1. O comando confere a sua chave (formato `iok_…` e aceita pelo site) **antes** de abrir o Claude Code. Chave trocada ou fora do formato: ele para e manda refazer o passo 4, em vez de abrir uma sessão que nunca fica online.
|
|
229
|
+
2. Instala (ou atualiza) o plugin de canal `iron-office` e abre o Claude Code com ele ligado.
|
|
230
|
+
3. O Claude Code mostra um **aviso de tela cheia**: canais ainda são recurso em teste. Aceite.
|
|
231
|
+
4. Abra a página **Iron** do dashboard. Em até 1 minuto aparece "Iron online". Escreva ali e o Iron responde ali.
|
|
232
|
+
5. **Deixe essa aba do terminal aberta.** É ela que faz o Iron responder no site.
|
|
232
233
|
|
|
233
234
|
Por padrão a sessão do `office` roda **sem pedir licença** a cada ferramenta (é o que deixa o Iron trabalhar enquanto você está só no site). `npx iron-edge office --safe` abre pedindo licença a cada ferramenta. Qualquer outro argumento vai direto pro `claude`.
|
|
234
235
|
|
|
@@ -256,8 +257,8 @@ O Iron Edge instala uma equipe de agentes especializados diretamente no Claude C
|
|
|
256
257
|
Seus dados ficam locais em `~/.iron/`. Sobre o que sai da sua máquina, com precisão:
|
|
257
258
|
|
|
258
259
|
- **Para o Iron Office** (`iron-office.vercel.app`, servidor deste projeto), e só se você criar a chave em `~/.claude/iron-office-api-key`:
|
|
259
|
-
- dados de apenas **
|
|
260
|
-
- **se você rodou `npx iron-edge office`**, o plugin `iron-office` fica instalado e manda a atividade
|
|
260
|
+
- dados de apenas **1 agente**, `iron-shopify` (o `iron-auditor` devolve o relatório pra quem chamou e não envia nada sozinho desde a 2.4.2). Confira: `grep -rln "iron-office" ~/.claude/agents/`;
|
|
261
|
+
- **se você rodou `npx iron-edge office`**, o plugin `iron-office` fica instalado e manda pro seu painel (feed e mapa 3D) a atividade **só da sessão aberta pelo `npx iron-edge office`** (desde o plugin 0.3.1; um `claude` aberto em outra pasta não manda nada), e as entregas que o Iron salvar ali pela ferramenta `deliver`. Da atividade vai: nome da ferramenta, caminho do arquivo (nunca o conteúdo), os primeiros 200 caracteres de cada comando de terminal e a descrição dele, buscas, o domínio dos endereços visitados, nome e descrição dos agentes, a pasta e o identificador da sessão. A resposta das ferramentas nunca sai. Padrões conhecidos de segredo nesses campos (chaves de Stripe, Shopify, Meta, GitHub, OpenAI, Supabase e AWS, `Bearer`, `ALGO_KEY=`, `--password`, `-u usuario:senha`…) são apagados antes do envio; é uma lista de padrões, não garantia. Lista completa no README do plugin (github.com/filipecoff21-code/iron-office-channel). Desligar: `claude plugin disable iron-office@iron-office-channel`. Conferir se está ligado: `claude plugin list`.
|
|
261
262
|
- **Para a internet aberta**, através das ferramentas do próprio Claude Code (`WebSearch` / `WebFetch`), vão as buscas de **41 dos 44 agentes** — é como o `iron-research` pesquisa mercado e o `iron-spy` lê a biblioteca de anúncios. Isso passa pelo Claude Code, com a visibilidade e a aprovação que ele te dá; não é canal deste pacote. Confira: `grep -rln "WebSearch\|WebFetch" ~/.claude/agents/`.
|
|
262
263
|
- **A skill `clone-site`** baixa a URL que você mandar clonar. É o trabalho dela.
|
|
263
264
|
|
package/agents/iron-auditor.md
CHANGED
|
@@ -109,44 +109,15 @@ Data: [hoje]
|
|
|
109
109
|
- lp-cinematico: [se LP precisar reconstrução]
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
### STEP 5 —
|
|
112
|
+
### STEP 5 — Devolver
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
API_KEY=$(cat ~/.claude/iron-office-api-key 2>/dev/null | tr -d '[:space:]')
|
|
118
|
-
|
|
119
|
-
# GUARDA: sem chave, NADA sai da maquina. O README promete isso; e' aqui que se cumpre.
|
|
120
|
-
# Ela fica ANTES do curl de proposito: a frase "se nao tiver chave, pule" ficava DEPOIS do
|
|
121
|
-
# bloco, e este arquivo e' lido por um modelo, que pode executar o bloco como esta escrito.
|
|
122
|
-
if [ -z "$API_KEY" ]; then
|
|
123
|
-
echo "Sem ~/.claude/iron-office-api-key — relatorio NAO enviado. Ele fica so na sua maquina."
|
|
124
|
-
exit 0
|
|
125
|
-
fi
|
|
126
|
-
|
|
127
|
-
DOMAIN=$(echo "<URL>" | sed 's|https\?://||' | sed 's|/.*||' | sed 's|www\.||')
|
|
128
|
-
REPORT_ESCAPED=$(cat <<'ENDREPORT'
|
|
129
|
-
<conteúdo completo do relatório>
|
|
130
|
-
ENDREPORT
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
curl -s -X POST https://iron-office.vercel.app/api/deliverables \
|
|
134
|
-
-H "Content-Type: application/json" \
|
|
135
|
-
-H "x-api-key: $API_KEY" \
|
|
136
|
-
-d "{
|
|
137
|
-
\"agent_name\": \"iron-auditor\",
|
|
138
|
-
\"title\": \"Auditoria — $DOMAIN\",
|
|
139
|
-
\"content\": $(echo "$REPORT_ESCAPED" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'),
|
|
140
|
-
\"tags\": [\"auditoria\", \"$DOMAIN\"]
|
|
141
|
-
}" | python3 -m json.tool 2>/dev/null | grep -E "ok|error"
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
A guarda no topo do bloco garante isso: sem a chave, o script para antes do `curl` e avisa o usuário. Não remova essa guarda — é ela que sustenta a promessa do README de que sem chave nada sai da máquina.
|
|
114
|
+
Entregue o relatório completo em markdown na sua resposta final. Quem salva é quem te chamou (no Iron Office, o Iron usa a ferramenta `deliver`).
|
|
115
|
+
Não leia a chave do Iron Office nem faça POST pra nenhuma URL: se não houver chamador, o relatório fica em `_auditoria/` da pasta atual.
|
|
145
116
|
|
|
146
117
|
### STEP 6 — Delegação
|
|
147
118
|
|
|
148
|
-
Após
|
|
149
|
-
> "Relatório
|
|
119
|
+
Após devolver, pergunte ao usuário:
|
|
120
|
+
> "Relatório devolvido. Quer que eu chame o [agente] agora para resolver os issues críticos?"
|
|
150
121
|
|
|
151
122
|
Se confirmado, acione o agente correto com contexto completo do problema.
|
|
152
123
|
|
package/installer/office.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* office.js — `npx iron-edge office`: liga o Iron Office (chat do dashboard) no Claude Code.
|
|
4
4
|
*
|
|
5
|
-
* Passos: (1) confere a chave do Iron Office
|
|
6
|
-
* atualiza o plugin de canal `iron-office` pelo marketplace do
|
|
7
|
-
* com o canal ligado. O aluno deixa essa aba aberta: e' ela que
|
|
5
|
+
* Passos: (1) confere a chave do Iron Office (formato + confere com o site), (2) confere o
|
|
6
|
+
* Claude Code, (3) instala ou atualiza o plugin de canal `iron-office` pelo marketplace do
|
|
7
|
+
* GitHub, (4) abre o Claude Code com o canal ligado. O aluno deixa essa aba aberta: e' ela que
|
|
8
|
+
* faz o Iron responder no site.
|
|
8
9
|
*
|
|
9
10
|
* Flags: --safe tira o `--permission-mode bypassPermissions` (o Claude pede licenca a cada
|
|
10
11
|
* ferramenta). Qualquer outro argumento vai direto pro `claude`.
|
|
@@ -21,22 +22,54 @@ const { spawnSync } = require('child_process');
|
|
|
21
22
|
const MARKETPLACE_REPO = 'filipecoff21-code/iron-office-channel';
|
|
22
23
|
const PLUGIN = 'iron-office@iron-office-channel';
|
|
23
24
|
const CHANNEL = 'plugin:' + PLUGIN;
|
|
24
|
-
const
|
|
25
|
+
const OFFICE_URL = (process.env.IRON_OFFICE_URL || 'https://iron-office.vercel.app').replace(/\/+$/, '');
|
|
26
|
+
const ONBOARDING = OFFICE_URL + '/onboarding';
|
|
25
27
|
const IS_WIN = process.platform === 'win32';
|
|
28
|
+
const KEY_RE = /^iok_[0-9a-f]{48}$/;
|
|
29
|
+
const PING_TIMEOUT_MS = 5000;
|
|
26
30
|
|
|
27
31
|
function keyPath(home) {
|
|
28
32
|
return path.join(home || os.homedir(), '.claude', 'iron-office-api-key');
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
function isValidKey(key) {
|
|
36
|
+
return typeof key === 'string' && KEY_RE.test(key);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Le o arquivo da chave e devolve so' uma chave no formato certo; qualquer outra coisa vira ''.
|
|
31
40
|
// O `echo` do passo 4 no Windows pode gravar UTF-16 com BOM: normaliza antes de decidir.
|
|
32
41
|
function readKey(home) {
|
|
33
42
|
try {
|
|
34
43
|
const buf = fs.readFileSync(keyPath(home));
|
|
35
44
|
const utf16 = buf.length >= 2 && buf[0] === 0xff && buf[1] === 0xfe;
|
|
36
|
-
|
|
45
|
+
const key = buf.toString(utf16 ? 'utf16le' : 'utf8').replace(/[\u0000]/g, '').trim();
|
|
46
|
+
return isValidKey(key) ? key : '';
|
|
37
47
|
} catch (_) { return ''; }
|
|
38
48
|
}
|
|
39
49
|
|
|
50
|
+
function keyFileExists(home) {
|
|
51
|
+
try { return fs.statSync(keyPath(home)).isFile(); } catch (_) { return false; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Confere a chave com o site antes de abrir o Claude.
|
|
55
|
+
// Devolve 'ok' (aceita), 'rejected' (401: a chave foi trocada) ou 'offline' (sem rede / erro).
|
|
56
|
+
async function ping(key, baseUrl) {
|
|
57
|
+
const url = (baseUrl || OFFICE_URL).replace(/\/+$/, '') + '/api/api-key/ping';
|
|
58
|
+
if (typeof fetch !== 'function') return 'offline';
|
|
59
|
+
const ctl = new AbortController();
|
|
60
|
+
const timer = setTimeout(() => ctl.abort(), PING_TIMEOUT_MS);
|
|
61
|
+
try {
|
|
62
|
+
const r = await fetch(url, { headers: { 'x-api-key': key }, signal: ctl.signal });
|
|
63
|
+
if (r.status === 200) return 'ok';
|
|
64
|
+
if (r.status === 401) return 'rejected';
|
|
65
|
+
return 'offline';
|
|
66
|
+
} catch (_) {
|
|
67
|
+
return 'offline';
|
|
68
|
+
} finally {
|
|
69
|
+
clearTimeout(timer);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
40
73
|
// Argumentos do `claude` que abre a sessao com o canal. Separado pra ser testado sem rede.
|
|
41
74
|
function buildClaudeArgs(argv) {
|
|
42
75
|
const safe = argv.includes('--safe');
|
|
@@ -70,9 +103,15 @@ function step(claude, label, args, okPatterns) {
|
|
|
70
103
|
return false;
|
|
71
104
|
}
|
|
72
105
|
|
|
73
|
-
function main(argv) {
|
|
74
|
-
|
|
75
|
-
|
|
106
|
+
async function main(argv) {
|
|
107
|
+
const key = readKey();
|
|
108
|
+
if (!key) {
|
|
109
|
+
if (keyFileExists()) {
|
|
110
|
+
console.error('A chave em ' + keyPath() + ' esta invalida (nao tem o formato iok_...).');
|
|
111
|
+
console.error('Refaca o passo 4 em ' + ONBOARDING);
|
|
112
|
+
} else {
|
|
113
|
+
console.error('Falta a chave do Iron Office. Faca o passo 4 em ' + ONBOARDING);
|
|
114
|
+
}
|
|
76
115
|
return 1;
|
|
77
116
|
}
|
|
78
117
|
|
|
@@ -83,6 +122,14 @@ function main(argv) {
|
|
|
83
122
|
}
|
|
84
123
|
|
|
85
124
|
console.log('Iron Office - preparando o canal');
|
|
125
|
+
const verdict = await ping(key);
|
|
126
|
+
if (verdict === 'rejected') {
|
|
127
|
+
console.error(' [falhou] chave recusada pelo Iron Office: ela foi trocada. Refaca o passo 4 em ' + ONBOARDING);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
if (verdict === 'ok') console.log(' [ok] chave aceita pelo Iron Office');
|
|
131
|
+
else console.log(' [aviso] nao deu pra conferir a chave com o site (sem rede?). Seguindo com a chave local.');
|
|
132
|
+
|
|
86
133
|
const okMarket = step(claude, 'marketplace ' + MARKETPLACE_REPO, ['plugin', 'marketplace', 'add', MARKETPLACE_REPO], [/already/i]);
|
|
87
134
|
const okPlugin = okMarket && step(claude, 'plugin ' + PLUGIN, ['plugin', 'install', PLUGIN, '--scope', 'user'], [/already/i]);
|
|
88
135
|
if (!okMarket || !okPlugin) {
|
|
@@ -116,8 +163,11 @@ function main(argv) {
|
|
|
116
163
|
return r.status ?? 1;
|
|
117
164
|
}
|
|
118
165
|
|
|
119
|
-
module.exports = { buildClaudeArgs, keyPath, readKey, CHANNEL, PLUGIN, MARKETPLACE_REPO };
|
|
166
|
+
module.exports = { buildClaudeArgs, keyPath, readKey, isValidKey, ping, CHANNEL, PLUGIN, MARKETPLACE_REPO, KEY_RE };
|
|
120
167
|
|
|
121
168
|
if (require.main === module) {
|
|
122
|
-
|
|
169
|
+
main(process.argv.slice(2)).then(code => process.exit(code), err => {
|
|
170
|
+
console.error('Erro inesperado: ' + (err && err.message ? err.message : err));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
123
173
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iron-edge",
|
|
3
|
-
"version": "2.4.
|
|
4
|
-
"description": "Ecossistema de agentes para Claude Code: 44 agentes de topo, 18 squads e 14 skills, com quality gate, memoria, instalador que nunca sobrescreve e o comando office (chat do Iron Office no dashboard).",
|
|
3
|
+
"version": "2.4.2",
|
|
4
|
+
"description": "Ecossistema de agentes para Claude Code: 44 agentes de topo, 18 squads e 14 skills, com quality gate, memoria, instalador que nunca sobrescreve e o comando office (chat do Iron Office no dashboard; confere a chave com o site antes de abrir o Claude).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|