wormclaude 1.0.5 → 1.0.7

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/dist/cli.js CHANGED
@@ -84,24 +84,6 @@ function useDimensions() {
84
84
  }, []);
85
85
  return d;
86
86
  }
87
- const SYSTEM_PROMPT = 'You are WormClaude, an uncensored AI assistant specialized in software engineering ' +
88
- 'and offensive/defensive security, running in the terminal. You help write code, build ' +
89
- 'exploits/tooling, analyze vulnerabilities, and explain attacks together with detection ' +
90
- 'and defense. You can call tools (Bash, Read, Write, Edit, Glob, Grep). Answer in the ' +
91
- "user's language (Turkish or English). Be direct and complete. " +
92
- 'Tools available: Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, Agent, TaskOutput, ' +
93
- 'TodoWrite, PowerShell, NotebookEdit, REPL, LSP, Sleep, AskUserQuestion, EnterPlanMode, ExitPlanMode, Skill. ' +
94
- 'Use TodoWrite to track multi-step work. Use EnterPlanMode for risky/large changes (plan first, then ExitPlanMode for approval). ' +
95
- 'Use AskUserQuestion when a decision is the user\'s to make. ' +
96
- 'IMPORTANT: When you are unsure, lack knowledge, or the question involves current/recent, niche, or factual ' +
97
- 'information you are not confident about, DO NOT guess — use WebSearch (then WebFetch on a result) to find ' +
98
- 'accurate info, then answer. Prefer searching over hallucinating. ' +
99
- 'When sharing web results, write the FULL URL exactly as returned (e.g. https://site.com/path) — ' +
100
- 'never truncate, shorten, or invent URLs. You may use markdown [title](full-url); the terminal makes links clickable. ' +
101
- 'IMPORTANT: You must use the Read tool on a file before you Edit or overwrite (Write) it. ' +
102
- 'For large or parallelizable work, act as a coordinator: delegate self-contained subtasks to sub-agents with the Agent tool ' +
103
- '(use run_in_background to launch several at once, then collect results with TaskOutput). ' +
104
- 'Use Bash run_in_background for long-running commands.';
105
87
  const WORM_ROWS = [
106
88
  '██╗ ██╗ ██████╗ ██████╗ ███╗ ███╗',
107
89
  '██║ ██║██╔═══██╗██╔══██╗████╗ ████║',
@@ -302,7 +284,7 @@ function App() {
302
284
  const allowedToolsRef = useRef(new Set()); // oturum boyu izinli araçlar
303
285
  const [ask, setAsk] = useState(null);
304
286
  const [askSel, setAskSel] = useState(0); // AskUserQuestion seçimi
305
- const historyRef = useRef([{ role: 'system', content: SYSTEM_PROMPT }]);
287
+ const historyRef = useRef([]);
306
288
  // Acilis: klasore guven sorusu (WormClaude tarzi) — yon tuslari/1-2 sec, Enter onay
307
289
  // Dil seçimi (ilk açılış)
308
290
  useInput((inp, key) => {
@@ -699,7 +681,7 @@ function App() {
699
681
  setHistory: (h) => { historyRef.current = h; setCtxTokens(Math.round(JSON.stringify(h).length / 4)); },
700
682
  note: (text) => push({ kind: 'note', text }),
701
683
  assistant: (text) => push({ kind: 'assistant', text }),
702
- clearConv: () => { setItems([{ kind: 'banner' }]); historyRef.current = [{ role: 'system', content: SYSTEM_PROMPT }]; },
684
+ clearConv: () => { setItems([{ kind: 'banner' }]); historyRef.current = []; },
703
685
  exit,
704
686
  };
705
687
  setBusy(true);
@@ -0,0 +1,186 @@
1
+ // Computer-use: çapraz-platform ekran görüntüsü + fare/klavye.
2
+ // Yöntem: işletim sistemi araçları (native bağımlılık YOK).
3
+ // Windows → PowerShell + .NET (yerleşik)
4
+ // macOS → screencapture + osascript (yerleşik) · fare için cliclick gerekir
5
+ // Linux → scrot/import/grim + xdotool (paket gerekebilir)
6
+ import { execFileSync } from 'node:child_process';
7
+ import * as fs from 'node:fs';
8
+ import * as os from 'node:os';
9
+ import * as path from 'node:path';
10
+ const PLAT = process.platform;
11
+ const tmpFile = (ext) => path.join(os.tmpdir(), `wc_${Date.now()}_${Math.floor(Math.random() * 1e9)}.${ext}`);
12
+ // PowerShell tek-tırnak string literali ('' ile kaçış)
13
+ function psStr(s) {
14
+ return "'" + String(s).replace(/'/g, "''") + "'";
15
+ }
16
+ function runPS(script) {
17
+ return execFileSync('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script], { encoding: 'utf8', windowsHide: true, timeout: 30000, maxBuffer: 64 * 1024 * 1024 });
18
+ }
19
+ // ── Ekran görüntüsü → base64 PNG ────────────────────────────────────────────
20
+ export function screenshot() {
21
+ const out = tmpFile('png');
22
+ try {
23
+ if (PLAT === 'win32') {
24
+ const p = out.replace(/\\/g, '\\\\');
25
+ runPS(`Add-Type -AssemblyName System.Windows.Forms,System.Drawing; ` +
26
+ `$b=[System.Windows.Forms.SystemInformation]::VirtualScreen; ` +
27
+ `$bmp=New-Object System.Drawing.Bitmap $b.Width,$b.Height; ` +
28
+ `$g=[System.Drawing.Graphics]::FromImage($bmp); ` +
29
+ `$g.CopyFromScreen($b.X,$b.Y,0,0,$bmp.Size); ` +
30
+ `$bmp.Save('${p}',[System.Drawing.Imaging.ImageFormat]::Png); ` +
31
+ `$g.Dispose();$bmp.Dispose()`);
32
+ }
33
+ else if (PLAT === 'darwin') {
34
+ execFileSync('screencapture', ['-x', out], { timeout: 20000 });
35
+ }
36
+ else {
37
+ const tries = [
38
+ ['scrot', ['-o', out]],
39
+ ['import', ['-window', 'root', out]],
40
+ ['grim', [out]],
41
+ ];
42
+ let ok = false;
43
+ for (const [cmd, a] of tries) {
44
+ try {
45
+ execFileSync(cmd, a, { timeout: 20000 });
46
+ ok = true;
47
+ break;
48
+ }
49
+ catch { }
50
+ }
51
+ if (!ok)
52
+ throw new Error('Linux ekran görüntüsü aracı yok (scrot / imagemagick / grim kurun)');
53
+ }
54
+ return fs.readFileSync(out).toString('base64');
55
+ }
56
+ finally {
57
+ try {
58
+ fs.unlinkSync(out);
59
+ }
60
+ catch { }
61
+ }
62
+ }
63
+ // ── Fare ────────────────────────────────────────────────────────────────────
64
+ export function click(x, y, button = 'left') {
65
+ const X = Math.round(x), Y = Math.round(y);
66
+ if (PLAT === 'win32') {
67
+ const down = button === 'right' ? '0x08' : '0x02';
68
+ const up = button === 'right' ? '0x10' : '0x04';
69
+ runPS(`Add-Type @'
70
+ using System;using System.Runtime.InteropServices;
71
+ public class WCm{[DllImport("user32.dll")]public static extern bool SetCursorPos(int x,int y);[DllImport("user32.dll")]public static extern void mouse_event(uint f,uint x,uint y,uint d,int e);}
72
+ '@; [WCm]::SetCursorPos(${X},${Y}); Start-Sleep -Milliseconds 60; [WCm]::mouse_event(${down},0,0,0,0); [WCm]::mouse_event(${up},0,0,0,0)`);
73
+ }
74
+ else if (PLAT === 'darwin') {
75
+ execFileSync('cliclick', [`${button === 'right' ? 'rc' : 'c'}:${X},${Y}`], { timeout: 10000 });
76
+ }
77
+ else {
78
+ execFileSync('xdotool', ['mousemove', String(X), String(Y), 'click', button === 'right' ? '3' : '1'], { timeout: 10000 });
79
+ }
80
+ }
81
+ // ── Klavye: düz metin yazma ──────────────────────────────────────────────────
82
+ export function typeText(text) {
83
+ if (PLAT === 'win32') {
84
+ const esc = String(text).replace(/([+^%~(){}\[\]])/g, '{$1}');
85
+ runPS(`Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait(${psStr(esc)})`);
86
+ }
87
+ else if (PLAT === 'darwin') {
88
+ execFileSync('osascript', ['-e', `tell application "System Events" to keystroke ${osaStr(text)}`], { timeout: 15000 });
89
+ }
90
+ else {
91
+ execFileSync('xdotool', ['type', '--clearmodifiers', String(text)], { timeout: 15000 });
92
+ }
93
+ }
94
+ function osaStr(s) {
95
+ return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
96
+ }
97
+ // ── Klavye: özel tuş / kombinasyon (Enter, Tab, Ctrl+C ...) ──────────────────
98
+ export function key(combo) {
99
+ const parts = String(combo).split('+').map((p) => p.trim()).filter(Boolean);
100
+ if (PLAT === 'win32') {
101
+ runPS(`Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait(${psStr(toSendKeys(parts))})`);
102
+ }
103
+ else if (PLAT === 'darwin') {
104
+ const mods = parts.slice(0, -1).map(macMod).filter(Boolean);
105
+ const k = parts[parts.length - 1];
106
+ const code = macKeyCode(k);
107
+ const using = mods.length ? ` using {${mods.join(', ')}}` : '';
108
+ if (code != null)
109
+ execFileSync('osascript', ['-e', `tell application "System Events" to key code ${code}${using}`], { timeout: 10000 });
110
+ else
111
+ execFileSync('osascript', ['-e', `tell application "System Events" to keystroke ${osaStr(k.toLowerCase())}${using}`], { timeout: 10000 });
112
+ }
113
+ else {
114
+ execFileSync('xdotool', ['key', parts.map(xMod).join('+')], { timeout: 10000 });
115
+ }
116
+ }
117
+ // ── Kaydırma ─────────────────────────────────────────────────────────────────
118
+ export function scroll(direction, amount = 3) {
119
+ const n = Math.max(1, Math.min(20, Math.round(amount)));
120
+ if (PLAT === 'win32') {
121
+ const delta = direction === 'up' ? 120 : -120;
122
+ runPS(`Add-Type @'
123
+ using System;using System.Runtime.InteropServices;
124
+ public class WCs{[DllImport("user32.dll")]public static extern void mouse_event(uint f,uint x,uint y,int d,int e);}
125
+ '@; for($i=0;$i -lt ${n};$i++){[WCs]::mouse_event(0x0800,0,0,${delta},0);Start-Sleep -Milliseconds 30}`);
126
+ }
127
+ else if (PLAT === 'darwin') {
128
+ const dir = direction === 'up' ? n : -n;
129
+ execFileSync('osascript', ['-e', `tell application "System Events" to scroll {0, ${dir}}`], { timeout: 10000 });
130
+ }
131
+ else {
132
+ execFileSync('xdotool', ['click', '--repeat', String(n), direction === 'up' ? '4' : '5'], { timeout: 10000 });
133
+ }
134
+ }
135
+ // ── Yardımcı eşlemeler ───────────────────────────────────────────────────────
136
+ function toSendKeys(parts) {
137
+ const mod = { ctrl: '^', control: '^', alt: '%', shift: '+', win: '^{ESC}' };
138
+ const special = {
139
+ enter: '{ENTER}', return: '{ENTER}', tab: '{TAB}', esc: '{ESC}', escape: '{ESC}',
140
+ backspace: '{BACKSPACE}', delete: '{DELETE}', del: '{DELETE}', up: '{UP}', down: '{DOWN}',
141
+ left: '{LEFT}', right: '{RIGHT}', home: '{HOME}', end: '{END}', pageup: '{PGUP}', pagedown: '{PGDN}',
142
+ space: ' ', f1: '{F1}', f2: '{F2}', f3: '{F3}', f4: '{F4}', f5: '{F5}',
143
+ };
144
+ const k = parts[parts.length - 1].toLowerCase();
145
+ const pre = parts.slice(0, -1).map((p) => mod[p.toLowerCase()] || '').join('');
146
+ const main = special[k] || (k.length === 1 ? k : `{${k.toUpperCase()}}`);
147
+ return pre + main;
148
+ }
149
+ function macMod(m) {
150
+ const map = { ctrl: 'control down', control: 'control down', alt: 'option down', option: 'option down', shift: 'shift down', cmd: 'command down', win: 'command down' };
151
+ return map[m.toLowerCase()] || '';
152
+ }
153
+ function macKeyCode(k) {
154
+ const map = { enter: 36, return: 36, tab: 48, space: 49, esc: 53, escape: 53, delete: 51, backspace: 51, up: 126, down: 125, left: 123, right: 124, home: 115, end: 119 };
155
+ return map[k.toLowerCase()] ?? null;
156
+ }
157
+ function xMod(m) {
158
+ const map = { ctrl: 'ctrl', control: 'ctrl', alt: 'alt', shift: 'shift', win: 'super', cmd: 'super', enter: 'Return', return: 'Return', esc: 'Escape', escape: 'Escape', del: 'Delete', pageup: 'Prior', pagedown: 'Next' };
159
+ return map[m.toLowerCase()] || m;
160
+ }
161
+ // ── "Gör": ekran görüntüsü al → VL modeline sor → metin döndür ────────────────
162
+ export async function see(question, config) {
163
+ const b64 = screenshot();
164
+ const base = (config.baseUrl || 'https://api.wormclaude.ai/v1').replace(/\/$/, '');
165
+ const body = {
166
+ model: 'wormclaude-vision',
167
+ messages: [{
168
+ role: 'user',
169
+ content: [
170
+ { type: 'text', text: question || 'Ekranda ne görüyorsun? Önemli öğeleri ve konumlarını (yaklaşık koordinat) söyle.' },
171
+ { type: 'image_url', image_url: { url: 'data:image/png;base64,' + b64 } },
172
+ ],
173
+ }],
174
+ max_tokens: 400,
175
+ temperature: 0.2,
176
+ };
177
+ const res = await fetch(`${base}/chat/completions`, {
178
+ method: 'POST',
179
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.apiKey}` },
180
+ body: JSON.stringify(body),
181
+ });
182
+ if (!res.ok)
183
+ throw new Error(`vision HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`);
184
+ const data = await res.json();
185
+ return data?.choices?.[0]?.message?.content || '(boş yanıt)';
186
+ }
package/dist/theme.js CHANGED
@@ -8,4 +8,4 @@ export const theme = {
8
8
  green: '#4ade80',
9
9
  errorRed: '#ff6b6b',
10
10
  };
11
- export const VERSION = '1.0.5';
11
+ export const VERSION = '1.0.7';
package/dist/tools.js CHANGED
@@ -11,6 +11,7 @@ import { runAgentLoop } from './agent.js';
11
11
  import { tasks } from './tasks.js';
12
12
  import { getMcpToolSchemas, callMcpTool } from './mcp.js';
13
13
  import { getAutoSkills, getSkill, buildSkillPrompt } from './skills.js';
14
+ import * as computer from './computer.js';
14
15
  // Agent/alt-agent araçlarının backend'e ulaşması için config. cli.tsx başlangıçta
15
16
  // setToolConfig ile aynı (mutable) config nesnesini verir → /config değişiklikleri görülür.
16
17
  let toolConfig = null;
@@ -503,9 +504,16 @@ function skillToolSchema() {
503
504
  };
504
505
  }
505
506
  // Yerleşik + MCP + autoInvoke-skill araçlarının tümü. Ana döngü ve alt-agent'lar kullanır.
507
+ const computerToolSchemas = [
508
+ { type: 'function', function: { name: 'See', description: 'Ekran goruntusu alir ve gorsel modele sorar. Bilgisayari kontrol etmeden ONCE ekranda ne oldugunu gormek icin kullan. Donen metin ekranin aciklamasidir (ogeler, metinler, yaklasik koordinatlar).', parameters: { type: 'object', properties: { question: { type: 'string', description: 'Ekranda ne ariyorsun? Orn: "Kaydet butonu nerede, koordinati ne?"' } }, required: ['question'] } } },
509
+ { type: 'function', function: { name: 'Click', description: 'Ekranda (x,y) piksel koordinatina fare tiklamasi yapar. Koordinati once See ile ogren.', parameters: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' }, button: { type: 'string', enum: ['left', 'right'], description: 'Varsayilan left' } }, required: ['x', 'y'] } } },
510
+ { type: 'function', function: { name: 'Type', description: 'Klavyeden duz metin yazar (aktif pencereye).', parameters: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] } } },
511
+ { type: 'function', function: { name: 'Key', description: 'Ozel tus / kombinasyon gonderir. Orn: "Enter", "Tab", "Ctrl+C", "Alt+F4".', parameters: { type: 'object', properties: { keys: { type: 'string' } }, required: ['keys'] } } },
512
+ { type: 'function', function: { name: 'Scroll', description: 'Fare tekerlegiyle kaydirir.', parameters: { type: 'object', properties: { direction: { type: 'string', enum: ['up', 'down'] }, amount: { type: 'number', description: 'Varsayilan 3' } }, required: ['direction'] } } },
513
+ ];
506
514
  export function allToolSchemas() {
507
515
  const sk = skillToolSchema();
508
- return [...toolSchemas, ...getMcpToolSchemas(), ...(sk ? [sk] : [])];
516
+ return [...toolSchemas, ...computerToolSchemas, ...getMcpToolSchemas(), ...(sk ? [sk] : [])];
509
517
  }
510
518
  const TOOL_META = {
511
519
  Read: { readOnly: true, concurrencySafe: true },
@@ -517,6 +525,11 @@ const TOOL_META = {
517
525
  Write: { needsPermission: true, validate: (a) => (a && a.file_path ? null : 'file_path gerekli') },
518
526
  Edit: { needsPermission: true, validate: (a) => (a && a.file_path && a.old_string != null ? null : 'file_path ve old_string gerekli') },
519
527
  Agent: { validate: (a) => (a && a.prompt ? null : 'prompt gerekli') },
528
+ See: { readOnly: true, needsPermission: true, validate: (a) => (a && a.question ? null : 'question gerekli') },
529
+ Click: { needsPermission: true, validate: (a) => (a && a.x != null && a.y != null ? null : 'x ve y gerekli') },
530
+ Type: { needsPermission: true, validate: (a) => (a && a.text != null ? null : 'text gerekli') },
531
+ Key: { needsPermission: true, validate: (a) => (a && a.keys ? null : 'keys gerekli') },
532
+ Scroll: { needsPermission: true, validate: (a) => (a && a.direction ? null : 'direction gerekli') },
520
533
  WebSearch: { readOnly: true, needsPermission: true, validate: (a) => (a && a.query ? null : 'query gerekli') },
521
534
  TodoWrite: { readOnly: true, validate: (a) => (a && Array.isArray(a.todos) ? null : 'todos dizisi gerekli') },
522
535
  PowerShell: { needsPermission: true, validate: (a) => (a && a.command ? null : 'command gerekli') },
@@ -681,6 +694,16 @@ export function toolLabel(name, args) {
681
694
  return 'EnterPlanMode()';
682
695
  if (name === 'ExitPlanMode')
683
696
  return 'ExitPlanMode()';
697
+ if (name === 'See')
698
+ return `See(${String(args.question || '').slice(0, 40)})`;
699
+ if (name === 'Click')
700
+ return `Click(${Math.round(args.x)}, ${Math.round(args.y)})`;
701
+ if (name === 'Type')
702
+ return `Type(${String(args.text || '').slice(0, 30)})`;
703
+ if (name === 'Key')
704
+ return `Key(${args.keys})`;
705
+ if (name === 'Scroll')
706
+ return `Scroll(${args.direction})`;
684
707
  if (name.startsWith('mcp__')) {
685
708
  const parts = name.split('__');
686
709
  return `MCP·${parts[1]}(${parts.slice(2).join('__')})`;
@@ -737,6 +760,26 @@ const TYPE_EXT = {
737
760
  // ── Executor ──────────────────────────────────────────────────────────────────
738
761
  export async function executeTool(name, args) {
739
762
  try {
763
+ if (name === 'See') {
764
+ const ans = await computer.see(String(args.question || ''), cfg());
765
+ return { ok: true, output: ans };
766
+ }
767
+ if (name === 'Click') {
768
+ computer.click(Number(args.x), Number(args.y), args.button === 'right' ? 'right' : 'left');
769
+ return { ok: true, output: `Tiklandi (${Math.round(args.x)}, ${Math.round(args.y)})` };
770
+ }
771
+ if (name === 'Type') {
772
+ computer.typeText(String(args.text ?? ''));
773
+ return { ok: true, output: `Yazildi (${String(args.text ?? '').length} karakter)` };
774
+ }
775
+ if (name === 'Key') {
776
+ computer.key(String(args.keys || ''));
777
+ return { ok: true, output: `Tus: ${args.keys}` };
778
+ }
779
+ if (name === 'Scroll') {
780
+ computer.scroll(args.direction === 'up' ? 'up' : 'down', Number(args.amount) || 3);
781
+ return { ok: true, output: `Kaydirildi: ${args.direction}` };
782
+ }
740
783
  if (name === 'Bash') {
741
784
  if (args.run_in_background) {
742
785
  const task = tasks.create('shell', String(args.command).slice(0, 60));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {