iron-edge 2.4.0 → 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 +5 -4
- 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
|
|
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.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",
|