iron-edge 2.3.2 → 2.4.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/README.md CHANGED
@@ -213,6 +213,32 @@ npx iron-edge verify
213
213
 
214
214
  ---
215
215
 
216
+ ## Iron Office
217
+
218
+ Converse com o Iron pelo dashboard ([iron-office.vercel.app](https://iron-office.vercel.app)) e veja os agentes trabalhando no feed e no mapa 3D.
219
+
220
+ ```bash
221
+ npx iron-edge office
222
+ ```
223
+
224
+ Antes, faça o **passo 4** do onboarding do Iron Office (ele salva a sua chave em `~/.claude/iron-office-api-key`).
225
+
226
+ O que esperar:
227
+
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.
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`.
235
+
236
+ Mensagem escrita no site com o terminal fechado espera até 15 minutos; depois disso expira e o chat avisa pra mandar de novo (ordem velha não é executada sem você ver).
237
+
238
+ O plugin também manda a atividade das sessões pro seu painel: veja o que sai em "Sobre o que sai da sua máquina", mais abaixo.
239
+
240
+ ---
241
+
216
242
  ## Requisitos
217
243
 
218
244
  - **Node.js** 18 ou superior
@@ -230,12 +256,14 @@ O Iron Edge instala uma equipe de agentes especializados diretamente no Claude C
230
256
 
231
257
  Seus dados ficam locais em `~/.iron/`. Sobre o que sai da sua máquina, com precisão:
232
258
 
233
- - **Para o Iron Office** (`iron-office.vercel.app`, servidor deste projeto) vão dados de apenas **2 agentes**: `iron-auditor` e `iron-shopify`. E só se você criar a chave em `~/.claude/iron-office-api-key`. Confira você mesmo: `grep -rln "iron-office" ~/.claude/agents/`.
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`:
260
+ - dados de apenas **2 agentes**, `iron-auditor` e `iron-shopify`. Confira: `grep -rln "iron-office" ~/.claude/agents/`;
261
+ - **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`.
234
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/`.
235
263
  - **A skill `clone-site`** baixa a URL que você mandar clonar. É o trabalho dela.
236
264
 
237
265
 
238
- Sendo exato sobre a força dessa garantia, porque ela não é um sandbox: cada bloco que chama o Iron Office começa com uma guarda (`[ -z "$IRON_KEY" ] && exit 0`) e a regra R20 do lint impede que qualquer bloco novo entre sem ela. Mas esses arquivos são **instruções lidas por um modelo**, não um binário fechado: um agente que execute só a linha do `curl`, isolada da guarda, não é barrado por nada aqui. Se o que você quer é garantia dura, a que existe é não criar o arquivo de chave — sem ele, não há para onde enviar, e todo bloco para na guarda.
266
+ Sendo exato sobre a força dessa garantia, porque ela não é um sandbox: cada bloco que chama o Iron Office começa com uma guarda (`[ -z "$IRON_KEY" ] && exit 0`) e a regra R20 do lint impede que qualquer bloco novo entre sem ela. Mas esses arquivos são **instruções lidas por um modelo**, não um binário fechado: um agente que execute só a linha do `curl`, isolada da guarda, não é barrado por nada aqui. Se o que você quer é garantia dura, a que existe é não criar o arquivo de chave — sem ele, não há para onde enviar, e todo bloco para na guarda (o plugin `iron-office` também: sem a chave, ele não envia nada).
239
267
 
240
268
  ---
241
269
 
package/bin/iron-edge.js CHANGED
@@ -14,6 +14,9 @@
14
14
  * arquivos cujo hash bate com o manifesto — os que
15
15
  * ninguem alterou — e preserva o resto
16
16
  *
17
+ * office [--safe] liga o Iron Office: instala o plugin de canal e abre o
18
+ * Claude Code conversando com o chat do dashboard
19
+ *
17
20
  * Nao existe 'uninstall' automatico: remover continua sendo operacao conduzida, com o
18
21
  * manifesto na mao (o README descreve o procedimento).
19
22
  */
@@ -147,7 +150,12 @@ if (cmd === 'install') {
147
150
  // Reprovam so' divergencia sem registro e arquivo sumido.
148
151
  process.exit(alterados.length + sumidos.length === 0 ? 0 : 1);
149
152
 
153
+ } else if (cmd === 'office') {
154
+ // liga o chat do dashboard (iron-office.vercel.app/chat) na sessao do Claude Code
155
+ const r = spawnSync(process.execPath, [path.join(__dirname, '..', 'installer', 'office.js'), ...rest], { stdio: 'inherit' });
156
+ process.exit(r.status ?? 1);
157
+
150
158
  } else {
151
- console.log('uso: iron-edge <install|update|verify> [--dry-run] [--source <dir>]');
152
- process.exit(cmd ? 1 : 0);
159
+ console.log('uso: iron-edge <install|update|verify|office> [--dry-run] [--source <dir>] [--safe]');
160
+ process.exit(cmd && cmd !== '--help' && cmd !== '-h' ? 1 : 0);
153
161
  }
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * office.js — `npx iron-edge office`: liga o Iron Office (chat do dashboard) no Claude Code.
4
+ *
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.
9
+ *
10
+ * Flags: --safe tira o `--permission-mode bypassPermissions` (o Claude pede licenca a cada
11
+ * ferramenta). Qualquer outro argumento vai direto pro `claude`.
12
+ *
13
+ * Saida so' em ASCII, como o resto do instalador: console do Windows antigo mostra lixo em
14
+ * acento e simbolo. O `claude` recebe IRON_OFFICE_CHANNEL=1 no ambiente: e' o que diz ao
15
+ * servidor do plugin que ESTA sessao e' o canal (uma aba comum do Claude Code nao puxa o chat).
16
+ */
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const os = require('os');
20
+ const { spawnSync } = require('child_process');
21
+
22
+ const MARKETPLACE_REPO = 'filipecoff21-code/iron-office-channel';
23
+ const PLUGIN = 'iron-office@iron-office-channel';
24
+ const CHANNEL = 'plugin:' + PLUGIN;
25
+ const OFFICE_URL = (process.env.IRON_OFFICE_URL || 'https://iron-office.vercel.app').replace(/\/+$/, '');
26
+ const ONBOARDING = OFFICE_URL + '/onboarding';
27
+ const IS_WIN = process.platform === 'win32';
28
+ const KEY_RE = /^iok_[0-9a-f]{48}$/;
29
+ const PING_TIMEOUT_MS = 5000;
30
+
31
+ function keyPath(home) {
32
+ return path.join(home || os.homedir(), '.claude', 'iron-office-api-key');
33
+ }
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 ''.
40
+ // O `echo` do passo 4 no Windows pode gravar UTF-16 com BOM: normaliza antes de decidir.
41
+ function readKey(home) {
42
+ try {
43
+ const buf = fs.readFileSync(keyPath(home));
44
+ const utf16 = buf.length >= 2 && buf[0] === 0xff && buf[1] === 0xfe;
45
+ const key = buf.toString(utf16 ? 'utf16le' : 'utf8').replace(/[\u0000]/g, '').trim();
46
+ return isValidKey(key) ? key : '';
47
+ } catch (_) { return ''; }
48
+ }
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
+
73
+ // Argumentos do `claude` que abre a sessao com o canal. Separado pra ser testado sem rede.
74
+ function buildClaudeArgs(argv) {
75
+ const safe = argv.includes('--safe');
76
+ const extra = argv.filter(a => a !== '--safe');
77
+ const args = ['--dangerously-load-development-channels', CHANNEL];
78
+ if (!safe) args.push('--permission-mode', 'bypassPermissions');
79
+ return args.concat(extra);
80
+ }
81
+
82
+ function run(bin, args, opts) {
83
+ return spawnSync(bin, args, { encoding: 'utf8', shell: IS_WIN, ...opts });
84
+ }
85
+
86
+ // No Windows o `claude` instalado pelo npm e' um `claude.cmd`; com shell ele costuma resolver,
87
+ // mas ha PATH em que so' o nome com extensao funciona.
88
+ function resolveClaude() {
89
+ const candidates = IS_WIN ? ['claude', 'claude.cmd'] : ['claude'];
90
+ for (const bin of candidates) {
91
+ const r = run(bin, ['--version']);
92
+ if (!r.error && r.status === 0) return bin;
93
+ }
94
+ return null;
95
+ }
96
+
97
+ function step(claude, label, args, okPatterns) {
98
+ const r = run(claude, args);
99
+ const out = ((r.stdout || '') + (r.stderr || '')).trim();
100
+ if (r.status === 0) { console.log(' [ok] ' + label); return true; }
101
+ if (okPatterns.some(p => p.test(out))) { console.log(' [ok] ' + label + ' (ja estava)'); return true; }
102
+ console.log(' [falhou] ' + label + ': ' + (out.split('\n').pop() || 'sem detalhe'));
103
+ return false;
104
+ }
105
+
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
+ }
115
+ return 1;
116
+ }
117
+
118
+ const claude = resolveClaude();
119
+ if (!claude) {
120
+ console.error('Instale o Claude Code primeiro: https://claude.com/claude-code');
121
+ return 1;
122
+ }
123
+
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
+
133
+ const okMarket = step(claude, 'marketplace ' + MARKETPLACE_REPO, ['plugin', 'marketplace', 'add', MARKETPLACE_REPO], [/already/i]);
134
+ const okPlugin = okMarket && step(claude, 'plugin ' + PLUGIN, ['plugin', 'install', PLUGIN, '--scope', 'user'], [/already/i]);
135
+ if (!okMarket || !okPlugin) {
136
+ console.error('');
137
+ console.error('Nao deu pra instalar o plugin do Iron Office. Confira:');
138
+ console.error(' 1. a internet esta funcionando;');
139
+ console.error(' 2. o git esta instalado (rode: git --version);');
140
+ console.error(' 3. rode de novo: npx iron-edge office');
141
+ console.error('Se continuar, mande um print desta tela pro suporte.');
142
+ return 1;
143
+ }
144
+ // atualizar e' melhor-esforco: sem internet no meio, segue com a versao ja instalada
145
+ step(claude, 'atualizacao do plugin', ['plugin', 'update', PLUGIN], [/already|up.to.date|latest/i]);
146
+
147
+ const safe = argv.includes('--safe');
148
+ console.log('');
149
+ console.log('Abrindo o Claude Code com o Iron Office ligado.');
150
+ console.log('Vai aparecer um aviso de canal (recurso em teste): aceite.');
151
+ if (!safe) {
152
+ console.log('O Iron vai agir SEM pedir licenca a cada passo (e o que deixa ele trabalhar');
153
+ console.log('enquanto voce esta no site). Quer aprovar cada passo? Rode: npx iron-edge office --safe');
154
+ }
155
+ console.log('Deixe esta aba aberta: e ela que faz o Iron responder no site.');
156
+ console.log('');
157
+
158
+ const r = spawnSync(claude, buildClaudeArgs(argv), {
159
+ stdio: 'inherit',
160
+ shell: IS_WIN,
161
+ env: { ...process.env, IRON_OFFICE_CHANNEL: '1' },
162
+ });
163
+ return r.status ?? 1;
164
+ }
165
+
166
+ module.exports = { buildClaudeArgs, keyPath, readKey, isValidKey, ping, CHANNEL, PLUGIN, MARKETPLACE_REPO, KEY_RE };
167
+
168
+ if (require.main === module) {
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
+ });
173
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "iron-edge",
3
- "version": "2.3.2",
4
- "description": "Ecossistema de agentes para Claude Code: 44 agentes de topo, 18 squads e 14 skills, com quality gate, memoria e instalador que nunca sobrescreve.",
3
+ "version": "2.4.1",
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",
@@ -32,7 +32,7 @@
32
32
  "node": ">=18.0.0"
33
33
  },
34
34
  "scripts": {
35
- "test": "node scripts/lint.js && node scripts/test-verify.js && node scripts/test-update.js",
35
+ "test": "node scripts/lint.js && node scripts/test-verify.js && node scripts/test-update.js && node scripts/test-office.js",
36
36
  "test:matriz": "node scripts/test-matriz-update.js"
37
37
  },
38
38
  "devDependencies": {