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 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 instala (ou atualiza) o plugin de canal `iron-office` e abre o Claude Code com ele ligado.
229
- 2. O Claude Code mostra um **aviso de tela cheia**: canais ainda são recurso em teste. Aceite.
230
- 3. Abra a página **Iron** do dashboard. Em até 1 minuto aparece "Iron online". Escreva ali e o Iron responde ali.
231
- 4. **Deixe essa aba do terminal aberta.** É ela que faz o Iron responder no site.
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 **2 agentes**, `iron-auditor` e `iron-shopify`. Confira: `grep -rln "iron-office" ~/.claude/agents/`;
260
- - **se você rodou `npx iron-edge office`**, o plugin `iron-office` fica instalado e manda a atividade de **toda sessão** do Claude Code pro seu painel (feed e mapa 3D): 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`.
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
 
@@ -109,44 +109,15 @@ Data: [hoje]
109
109
  - lp-cinematico: [se LP precisar reconstrução]
110
110
  ```
111
111
 
112
- ### STEP 5 — Salvar no Iron Office
112
+ ### STEP 5 — Devolver
113
113
 
114
- Após gerar o relatório, salve automaticamente no Iron Office via API:
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 salvar, pergunte ao usuário:
149
- > "Relatório salvo no Iron Office (/audit). Quer que eu chame o [agente] agora para resolver os issues críticos?"
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
 
@@ -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, (2) confere o Claude Code, (3) instala ou
6
- * atualiza o plugin de canal `iron-office` pelo marketplace do GitHub, (4) abre o Claude Code
7
- * com o canal ligado. O aluno deixa essa aba aberta: e' ela que faz o Iron responder no site.
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 ONBOARDING = 'https://iron-office.vercel.app/onboarding';
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
- return buf.toString(utf16 ? 'utf16le' : 'utf8').replace(/[\uFEFF\u0000]/g, '').trim();
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
- if (!readKey()) {
75
- console.error('Falta a chave do Iron Office. Faca o passo 4 em ' + ONBOARDING);
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
- process.exit(main(process.argv.slice(2)));
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.0",
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",