wormclaude 1.0.215 → 1.0.217

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.
@@ -0,0 +1,181 @@
1
+ // Render-modlu klonlama — gerçek (headless) Chrome/Edge başlatır, JS'i ÇALIŞTIRIR, sayfayı
2
+ // render eder ve network'ten geçen TÜM asset'leri (css/js/img/font) yakalar. SPA (React/Vue)
3
+ // ve Cloudflare/bot-koruması arkasındaki siteleri yakalar (gerçek tarayıcı = JS-challenge geçer).
4
+ // puppeteer-core kullanır (Chromium İNDİRMEZ) → sistemde kurulu Chrome/Edge'i çalıştırır.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { pageFile, assetFile, relHref, ASSET_EXT, PAGE_EXT } from './clone.js';
8
+ // Sistemde kurulu Chrome/Edge'i bul (puppeteer kendi Chromium'unu indirmesin)
9
+ export function findChrome() {
10
+ const win = [
11
+ 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
12
+ 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
13
+ (process.env.LOCALAPPDATA || '') + '\\Google\\Chrome\\Application\\chrome.exe',
14
+ 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
15
+ 'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
16
+ ];
17
+ const mac = [
18
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
19
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
20
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
21
+ ];
22
+ const linux = ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/microsoft-edge'];
23
+ const cands = process.platform === 'win32' ? win : process.platform === 'darwin' ? mac : linux;
24
+ for (const c of cands) {
25
+ try {
26
+ if (c && fs.existsSync(c))
27
+ return c;
28
+ }
29
+ catch { }
30
+ }
31
+ return null;
32
+ }
33
+ export async function cloneSiteRendered(startUrl, outDir, opts = {}) {
34
+ const maxPages = Math.min(Math.max(opts.maxPages ?? 20, 1), 100);
35
+ if (!/^https?:\/\//i.test(startUrl))
36
+ startUrl = 'https://' + startUrl;
37
+ const exe = findChrome();
38
+ if (!exe)
39
+ throw new Error('Chrome/Edge bulunamadı — render modu için gerekli. Google Chrome kurun.');
40
+ // puppeteer-extra + stealth (automation parmak izini gizler → güçlü Cloudflare/bot-koruması geçer);
41
+ // yoksa düz puppeteer-core'a düş.
42
+ let puppeteer;
43
+ try {
44
+ const addExtra = (await import('puppeteer-extra')).addExtra;
45
+ const core = (await import('puppeteer-core')).default;
46
+ const stealth = (await import('puppeteer-extra-plugin-stealth')).default;
47
+ puppeteer = addExtra(core);
48
+ puppeteer.use(stealth());
49
+ }
50
+ catch {
51
+ try {
52
+ puppeteer = (await import('puppeteer-core')).default;
53
+ }
54
+ catch {
55
+ throw new Error('puppeteer-core kurulu değil — CLI\'yi güncelle (npm i -g wormclaude).');
56
+ }
57
+ }
58
+ const start = new URL(startUrl);
59
+ const host = start.host;
60
+ const root = path.resolve(outDir);
61
+ const assetsDir = path.join(root, '_assets');
62
+ fs.mkdirSync(root, { recursive: true });
63
+ const errors = [];
64
+ const assetMap = new Map(); // absUrl(hash'siz) → yerel dosya
65
+ const pageMap = new Map();
66
+ const visited = new Set();
67
+ const queue = [start.href.replace(/#.*$/, '')];
68
+ const rendered = [];
69
+ const browser = await puppeteer.launch({
70
+ executablePath: exe, headless: true,
71
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled', '--disable-dev-shm-usage'],
72
+ });
73
+ try {
74
+ const page = await browser.newPage();
75
+ await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36');
76
+ // Network'ten geçen her asset'i yakala (HTML parse'tan çok daha eksiksiz)
77
+ page.on('response', async (res) => {
78
+ try {
79
+ const rt = res.request().resourceType(); // document/stylesheet/script/image/font/media/xhr...
80
+ if (!['stylesheet', 'script', 'image', 'font', 'media'].includes(rt))
81
+ return;
82
+ const u = res.url().replace(/#.*$/, '');
83
+ if (u.startsWith('data:') || assetMap.has(u) || !res.ok())
84
+ return;
85
+ const buf = await res.buffer();
86
+ const dest = assetFile(new URL(u), assetsDir);
87
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
88
+ fs.writeFileSync(dest, buf);
89
+ assetMap.set(u, dest);
90
+ }
91
+ catch { /* yoksay */ }
92
+ });
93
+ while (queue.length && rendered.length < maxPages) {
94
+ const cur = queue.shift();
95
+ if (visited.has(cur))
96
+ continue;
97
+ visited.add(cur);
98
+ try {
99
+ await page.goto(cur, { waitUntil: 'domcontentloaded', timeout: 45000 });
100
+ }
101
+ catch (e) {
102
+ errors.push(`page ${cur}: ${String(e.message).slice(0, 80)}`);
103
+ continue;
104
+ }
105
+ // Cloudflare / bot-challenge'ın kendini çözmesini bekle (max ~28s) — yoksa "Bir dakika lütfen" sayfası yakalanır
106
+ const CH = /just a moment|bir dakika|checking your browser|attention required|please wait|moment\.\.\./i;
107
+ for (let i = 0; i < 14; i++) {
108
+ let t = '';
109
+ try {
110
+ t = await page.title();
111
+ }
112
+ catch { }
113
+ const sel = await page.$('#challenge-running, #cf-challenge-running, .cf-browser-verification, #cf-please-wait').catch(() => null);
114
+ if (!CH.test(t) && !sel)
115
+ break;
116
+ await new Promise((r) => setTimeout(r, 2000));
117
+ }
118
+ await page.waitForNetworkIdle({ idleTime: 1200, timeout: 12000 }).catch(() => { });
119
+ await new Promise((r) => setTimeout(r, 1500)); // JS'in oturması + geç asset'ler
120
+ let title = '';
121
+ try {
122
+ title = await page.title();
123
+ }
124
+ catch { }
125
+ if (CH.test(title)) { // hâlâ challenge → boş "bekleyin" sayfasını KAYDETME
126
+ errors.push(`page ${cur}: bot-koruması (Cloudflare) challenge geçilemedi`);
127
+ continue;
128
+ }
129
+ const u = new URL(cur);
130
+ const file = pageFile(u, root);
131
+ pageMap.set(cur, file);
132
+ rendered.push({ url: u, file, html: await page.content() }); // RENDER OLMUŞ DOM
133
+ // render olmuş DOM'dan iç linkleri keşfet (SPA'da JS ile gelen linkler dahil)
134
+ let links = [];
135
+ try {
136
+ links = await page.$$eval('a[href]', (as) => as.map((a) => a.href));
137
+ }
138
+ catch { }
139
+ for (const href of links) {
140
+ try {
141
+ const abs = new URL(href);
142
+ abs.hash = '';
143
+ if (abs.host === host && !ASSET_EXT.test(abs.pathname) && !visited.has(abs.href) &&
144
+ rendered.length + queue.length < maxPages &&
145
+ (PAGE_EXT.test(abs.pathname) || !/\.[a-z0-9]{1,6}$/i.test(abs.pathname.split('/').pop() || ''))) {
146
+ queue.push(abs.href);
147
+ }
148
+ }
149
+ catch { }
150
+ }
151
+ }
152
+ }
153
+ finally {
154
+ await browser.close().catch(() => { });
155
+ }
156
+ // PASS 2: render olmuş HTML'de asset+sayfa yollarını yerele düzelt
157
+ for (const pg of rendered) {
158
+ let html = pg.html;
159
+ const refs = new Set();
160
+ for (const m of html.matchAll(/(?:href|src)\s*=\s*["']([^"']+)["']/gi))
161
+ refs.add(m[1]);
162
+ for (const ref of refs) {
163
+ let abs;
164
+ try {
165
+ abs = new URL(ref, pg.url.href);
166
+ }
167
+ catch {
168
+ continue;
169
+ }
170
+ const bare = abs.href.replace(/#.*$/, '');
171
+ const local = assetMap.get(bare) || pageMap.get(bare);
172
+ if (local) {
173
+ const rel = relHref(pg.file, local);
174
+ html = html.split(`"${ref}"`).join(`"${rel}"`).split(`'${ref}'`).join(`'${rel}'`);
175
+ }
176
+ }
177
+ fs.mkdirSync(path.dirname(pg.file), { recursive: true });
178
+ fs.writeFileSync(pg.file, html);
179
+ }
180
+ return { pages: rendered.length, assets: assetMap.size, outDir: root, pageFiles: rendered.map((p) => p.file), errors: errors.slice(0, 30) };
181
+ }
package/dist/clone.js CHANGED
@@ -4,16 +4,16 @@
4
4
  // wget --mirror / HTTrack mantığı, aynı-host, sayfa limitli. Bağımlılık yok (regex tabanlı).
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
- const UA = {
7
+ export const UA = {
8
8
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
9
9
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
10
10
  'Accept-Language': 'tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7',
11
11
  };
12
- const ASSET_EXT = /\.(css|js|mjs|png|jpe?g|gif|webp|avif|svg|ico|bmp|woff2?|ttf|eot|otf|mp4|webm|ogg|mp3|wav|pdf|json|xml|map)(\?.*)?$/i;
13
- const PAGE_EXT = /\.(html?|php|aspx?|jsp|cfm)(\?.*)?$/i;
14
- function san(s) { return s.replace(/(\?|%3F|#|%23).*$/i, '').replace(/[^a-zA-Z0-9._/-]/g, '_'); }
12
+ export const ASSET_EXT = /\.(css|js|mjs|png|jpe?g|gif|webp|avif|svg|ico|bmp|woff2?|ttf|eot|otf|mp4|webm|ogg|mp3|wav|pdf|json|xml|map)(\?.*)?$/i;
13
+ export const PAGE_EXT = /\.(html?|php|aspx?|jsp|cfm)(\?.*)?$/i;
14
+ export function san(s) { return s.replace(/(\?|%3F|#|%23).*$/i, '').replace(/[^a-zA-Z0-9._/-]/g, '_'); }
15
15
  // URL → yerel dosya yolu (sayfa). / → index.html, /about → about/index.html, /a.html → a.html
16
- function pageFile(u, root) {
16
+ export function pageFile(u, root) {
17
17
  let p = decodeURIComponent(u.pathname);
18
18
  const last = p.split('/').pop() || '';
19
19
  if (p.endsWith('/') || last === '')
@@ -25,13 +25,13 @@ function pageFile(u, root) {
25
25
  return path.join(root, san(p).replace(/^[/\\]+/, ''));
26
26
  }
27
27
  // URL → yerel asset dosyası (host+path bazlı, _assets altında)
28
- function assetFile(u, assetsDir) {
28
+ export function assetFile(u, assetsDir) {
29
29
  let rel = san(u.host + u.pathname);
30
30
  if (!/\.[a-z0-9]{1,6}$/i.test(rel.split('/').pop() || ''))
31
31
  rel += '.bin';
32
32
  return path.join(assetsDir, rel);
33
33
  }
34
- function relHref(fromFile, toFile) {
34
+ export function relHref(fromFile, toFile) {
35
35
  let r = path.relative(path.dirname(fromFile), toFile).replace(/\\/g, '/');
36
36
  if (!r.startsWith('.'))
37
37
  r = './' + r;
package/dist/commands.js CHANGED
@@ -73,6 +73,7 @@ export const COMMANDS = [
73
73
  { name: '/harden', desc: 'savunma çıktısı üret (YARA/Sigma + yama önerisi): /harden <hedef|tehdit>' },
74
74
  { name: '/phish', desc: '[yetkili] phishing simülasyonu: pretext + e-posta şablonu: /phish <hedef|senaryo>' },
75
75
  { name: '/clone', desc: '[yetkili] login sayfası klonu + credential-capture: /clone <login|marka>' },
76
+ { name: '/mirror', desc: 'tam SİTE klonla (mirror, gerçek HTML+CSS+JS+asset, SPA/render destekli): /mirror <url> [klasör]' },
76
77
  { name: '/dropper', desc: '[yetkili, trust 4+] initial-access payload/dropper: /dropper <vektör>' },
77
78
  { name: '/postex', desc: '[yetkili] post-exploitation playbook (persistence+lateral+loot): /postex <senaryo>' },
78
79
  { name: '/privesc', desc: '[yetkili] privilege escalation (enum+exploit, Win/Linux): /privesc <os|bağlam>' },
@@ -583,6 +584,23 @@ export function buildPhishPrompt(arg) {
583
584
  'for the debrief. Concrete and operational, no moral disclaimers. Reply ENTIRELY in the user’s language.\n\n' +
584
585
  'Target / scenario: ' + a);
585
586
  }
587
+ export function buildMirrorPrompt(arg) {
588
+ const a = (arg || '').trim();
589
+ if (!a)
590
+ return 'Hangi sitenin tam klonunu (mirror) çıkarayım? Kullanım: `/mirror <url> [klasör]` (örn. `/mirror https://site.com ./klon`).';
591
+ const parts = a.split(/\s+/);
592
+ const url = parts[0];
593
+ const dir = parts[1] || ('./' + (() => { try {
594
+ return new URL(/^https?:/.test(url) ? url : 'https://' + url).hostname.replace(/[^a-z0-9.-]/gi, '_');
595
+ }
596
+ catch {
597
+ return 'site-klon';
598
+ } })());
599
+ return ('TAM SİTE KLONLAMA görevi. `CloneSite` aracını ŞİMDİ çağır (WebFetch DEĞİL): ' +
600
+ `url="${url}", out_dir="${dir}", render=true (JS/SPA/Cloudflare siteleri için gerçek tarayıcıyla render et). ` +
601
+ 'Araç "0 sayfa" veya hata dönerse KLON OLUŞMAMIŞTIR — kullanıcıya DÜRÜST söyle, "indirildi" deme. ' +
602
+ 'Başarılıysa kaç sayfa/asset indiğini ve nasıl açılacağını (index.html) tek-iki cümlede özetle. Reply in user\'s language.');
603
+ }
586
604
  export function buildClonePrompt(arg) {
587
605
  const a = (arg || '').trim();
588
606
  if (!a)
package/dist/theme.js CHANGED
@@ -16,4 +16,4 @@ export const theme = {
16
16
  synType: '#a78bfa', // tip/sınıf adları, sabitler
17
17
  synProp: '#e0e0e0', // özellik/anahtar adları
18
18
  };
19
- export const VERSION = '1.0.215';
19
+ export const VERSION = '1.0.217';
package/dist/tools.js CHANGED
@@ -449,15 +449,18 @@ export const toolSchemas = [
449
449
  function: {
450
450
  name: 'CloneSite',
451
451
  description: 'Tam SİTE KLONLAMA (mirror) — gerçek HTML+CSS+JS+görselleri indirip BİREBİR yerel kopya yazar. ' +
452
- 'Kullanıcı bir siteyi "klonla / kopyala / indir / mirror / tam klon" istediğinde BUNU kullan (WebFetch DEĞİL ' +
453
- 'WebFetch sadece metin döndürür, klonlama için işe yaramaz). İç linkleri takip eder (aynı host, sayfa limitli), ' +
454
- 'tüm asset yollarını yerele düzeltir çıktı tarayıcıda offline açılır. Bittiğinde kaç sayfa/asset indiğini özetle.',
452
+ 'Kullanıcı bir siteyi "klonla / kopyala / indir / mirror / tam klon" istediğinde BUNU kullan (WebFetch DEĞİL). ' +
453
+ 'İç linkleri takip eder, asset yollarını yerele düzeltir çıktı tarayıcıda offline açılır. ' +
454
+ 'İKİ MOD: varsayılan hızlı (fetch); başarısız olursa (Cloudflare/SPA 0 sayfa) OTOMATİK gerçek Chrome (render) ' +
455
+ 'moduna geçer. SPA/React/Cloudflare sitesi olduğunu biliyorsan render=true ver. ' +
456
+ 'ÖNEMLİ: araç "0 sayfa" veya hata dönerse KLON OLUŞMAMIŞTIR — kullanıcıya dürüst söyle, "indirildi" deme.',
455
457
  parameters: {
456
458
  type: 'object',
457
459
  properties: {
458
460
  url: { type: 'string', description: 'Klonlanacak sitenin başlangıç URL\'i (örn. https://site.com)' },
459
461
  out_dir: { type: 'string', description: 'Çıktı klasörü (örn. ./site-klon). Yoksa oluşturulur.' },
460
462
  max_pages: { type: 'number', description: 'En fazla kaç sayfa gezilsin (varsayılan 30, üst sınır 200). Tek sayfa için 1.' },
463
+ render: { type: 'boolean', description: 'true → gerçek (headless) Chrome ile JS render ederek klonla (SPA/React/Cloudflare siteleri için). Varsayılan false (hızlı fetch; başarısızsa zaten otomatik render\'a geçer).' },
461
464
  },
462
465
  required: ['url', 'out_dir'],
463
466
  },
@@ -1771,28 +1774,44 @@ export async function executeTool(name, args) {
1771
1774
  if (!url || !outDir)
1772
1775
  return { ok: false, output: 'url ve out_dir gerekli' };
1773
1776
  const maxPages = Number(args.max_pages) || 30;
1774
- try {
1775
- const r = await cloneSite(url, outDir, { maxPages });
1776
- if (r.pages === 0) {
1777
- // BAŞARISIZ — model bunu "başarılı" sanıp uydurmasın (gerçek hata + neden)
1778
- const why = r.errors.length ? r.errors.slice(0, 3).join(' | ') : 'sayfa çekilemedi';
1779
- return { ok: false, output: `❌ KLONLANAMADI — 0 sayfa indirildi (hiçbir şey kaydedilmedi). Sebep: ${why}.\n` +
1780
- `Bu site büyük olasılıkla Cloudflare / bot-koruması / JS-challenge arkasında (403/503/blok) ` +
1781
- `ya da içerik tamamen JavaScript ile yükleniyor (SPA). Basit fetch ile klonlanamaz — ` +
1782
- `headless tarayıcı (render) gerekir. KULLANICIYA DÜRÜST SÖYLE: klon OLUŞMADI, "indirildi" DEME.` };
1783
- }
1777
+ const wantRender = args.render === true || args.render === 'true';
1778
+ const fmt = (r, mode) => {
1784
1779
  const lines = [
1785
- `✓ Klon tamam: ${r.pages} sayfa + ${r.assets} asset indirildi → ${r.outDir}`,
1786
- `Aç: ${path.join(r.outDir, 'index.html')} (tarayıcıda offline açılır, asset yolları yerele düzeltildi)`,
1780
+ `✓ Klon tamam (${mode}): ${r.pages} sayfa + ${r.assets} asset → ${r.outDir}`,
1781
+ `Aç: ${path.join(r.outDir, 'index.html')} (tarayıcıda offline açılır, yollar yerele düzeltildi)`,
1787
1782
  ];
1788
1783
  if (r.pageFiles.length)
1789
1784
  lines.push('Sayfalar: ' + r.pageFiles.slice(0, 12).map((f) => path.relative(r.outDir, f)).join(', ') + (r.pageFiles.length > 12 ? ` (+${r.pageFiles.length - 12})` : ''));
1790
1785
  if (r.errors.length)
1791
- lines.push(`Atlanan/hatalı ${r.errors.length} kaynak (ölü link/erişim) — klon yine de çalışır.`);
1792
- return { ok: true, output: lines.join('\n') };
1786
+ lines.push(`Atlanan ${r.errors.length} kaynak (ölü link/erişim).`);
1787
+ return lines.join('\n');
1788
+ };
1789
+ try {
1790
+ // 1) Açıkça render istendi → doğrudan gerçek Chrome
1791
+ if (wantRender) {
1792
+ const { cloneSiteRendered } = await import('./clone-render.js');
1793
+ const r = await cloneSiteRendered(url, outDir, { maxPages });
1794
+ if (r.pages === 0)
1795
+ return { ok: false, output: `❌ KLONLANAMADI — render modu da 0 sayfa: ${r.errors.slice(0, 2).join(' | ') || 'sayfa yüklenemedi'}. KULLANICIYA DÜRÜST SÖYLE.` };
1796
+ return { ok: true, output: fmt(r, 'render/headless Chrome') };
1797
+ }
1798
+ // 2) Hızlı (fetch) mod
1799
+ const r = await cloneSite(url, outDir, { maxPages });
1800
+ if (r.pages > 0)
1801
+ return { ok: true, output: fmt(r, 'hızlı/fetch') };
1802
+ // 3) 0 sayfa → Cloudflare/SPA ihtimali → OTOMATİK render moduna geç
1803
+ const why = r.errors.length ? r.errors.slice(0, 2).join(' | ') : 'sayfa çekilemedi';
1804
+ const { cloneSiteRendered, findChrome } = await import('./clone-render.js');
1805
+ if (!findChrome()) {
1806
+ return { ok: false, output: `❌ KLONLANAMADI — hızlı mod 0 sayfa (${why}). Site Cloudflare/SPA arkasında, render modu gerekir ama Chrome/Edge bulunamadı (kurun). KULLANICIYA DÜRÜST SÖYLE: klon OLUŞMADI, "indirildi" DEME.` };
1807
+ }
1808
+ const r2 = await cloneSiteRendered(url, outDir, { maxPages });
1809
+ if (r2.pages > 0)
1810
+ return { ok: true, output: fmt(r2, 'render/gerçek Chrome — hızlı mod engellendi (Cloudflare/SPA), gerçek tarayıcıyla yapıldı') };
1811
+ return { ok: false, output: `❌ KLONLANAMADI — hızlı mod (${why}) ve render modu (${r2.errors.slice(0, 1).join('') || 'yüklenemedi'}) ikisi de başarısız. KULLANICIYA DÜRÜST SÖYLE: klon OLUŞMADI, "indirildi" DEME.` };
1793
1812
  }
1794
1813
  catch (e) {
1795
- return { ok: false, output: 'Klonlama hatası: ' + (e?.message || String(e)) };
1814
+ return { ok: false, output: 'Klonlama hatası: ' + (e?.message || String(e)) + '. KULLANICIYA DÜRÜST SÖYLE: klon oluşmadı.' };
1796
1815
  }
1797
1816
  }
1798
1817
  if (name === 'WebSearch') {
package/dist/tui.js CHANGED
@@ -16,7 +16,7 @@ import { itemAnsi, markdownAnsi } from './ansi.js';
16
16
  import { theme, VERSION } from './theme.js';
17
17
  import { cleanModelText } from './textclean.js';
18
18
  import { stripInlineToolCalls, stripEchoBlocks } from './inlinetools.js';
19
- import { COMMANDS, runSlashCommand, getPendingPentestCommand, buildExploitPrompt, buildHardenPrompt, buildPhishPrompt, buildClonePrompt, buildDropperPrompt, buildPostexPrompt, buildPrivescPrompt, buildAdenumPrompt, buildC2Prompt, buildEvasionPrompt, detectBareCommand } from './commands.js';
19
+ import { COMMANDS, runSlashCommand, getPendingPentestCommand, buildExploitPrompt, buildHardenPrompt, buildPhishPrompt, buildClonePrompt, buildMirrorPrompt, buildDropperPrompt, buildPostexPrompt, buildPrivescPrompt, buildAdenumPrompt, buildC2Prompt, buildEvasionPrompt, detectBareCommand } from './commands.js';
20
20
  import { cmdDesc, t, setLang, loadLang, getLang } from './i18n.js';
21
21
  import { getSkill, getSkills, buildSkillPrompt } from './skills.js';
22
22
  import { getExtCommand, getExtCommands, buildExtCommandPrompt } from './extensions.js';
@@ -1043,6 +1043,11 @@ export async function runTui() {
1043
1043
  runTurn(buildClonePrompt(a), v);
1044
1044
  return;
1045
1045
  }
1046
+ if (tok === '/mirror') {
1047
+ const a = v.slice(tok.length).trim();
1048
+ runTurn(buildMirrorPrompt(a), v);
1049
+ return;
1050
+ }
1046
1051
  if (tok === '/dropper') {
1047
1052
  const a = v.slice(tok.length).trim();
1048
1053
  runTurn(buildDropperPrompt(a), v);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.215",
3
+ "version": "1.0.217",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,6 +25,9 @@
25
25
  "pdf-parse": "^2.4.5",
26
26
  "pdfkit": "^0.19.1",
27
27
  "pptxgenjs": "^4.0.1",
28
+ "puppeteer-core": "^25.2.1",
29
+ "puppeteer-extra": "^3.3.6",
30
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
28
31
  "react": "^18.3.1",
29
32
  "string-width": "^7.2.0"
30
33
  },