@takimcizgisi/ajan 0.2.5

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.
Files changed (79) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +366 -0
  3. package/config/models.json +54 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +741 -0
  6. package/dist/cli/tui.d.ts +145 -0
  7. package/dist/cli/tui.js +1003 -0
  8. package/dist/config/configManager.d.ts +18 -0
  9. package/dist/config/configManager.js +204 -0
  10. package/dist/config/types.d.ts +98 -0
  11. package/dist/config/types.js +2 -0
  12. package/dist/core/agent.d.ts +88 -0
  13. package/dist/core/agent.js +222 -0
  14. package/dist/electron/main.d.ts +1 -0
  15. package/dist/electron/main.js +364 -0
  16. package/dist/electron/preload.d.ts +1 -0
  17. package/dist/electron/preload.js +67 -0
  18. package/dist/engine/llama.d.ts +39 -0
  19. package/dist/engine/llama.js +146 -0
  20. package/dist/engine/modelManager.d.ts +18 -0
  21. package/dist/engine/modelManager.js +73 -0
  22. package/dist/index.d.ts +9 -0
  23. package/dist/index.js +9 -0
  24. package/dist/service.d.ts +90 -0
  25. package/dist/service.js +268 -0
  26. package/dist/tools/data.d.ts +3 -0
  27. package/dist/tools/data.js +111 -0
  28. package/dist/tools/edit.d.ts +2 -0
  29. package/dist/tools/edit.js +48 -0
  30. package/dist/tools/file.d.ts +6 -0
  31. package/dist/tools/file.js +274 -0
  32. package/dist/tools/filesys.d.ts +3 -0
  33. package/dist/tools/filesys.js +74 -0
  34. package/dist/tools/memory.d.ts +3 -0
  35. package/dist/tools/memory.js +69 -0
  36. package/dist/tools/patch.d.ts +15 -0
  37. package/dist/tools/patch.js +129 -0
  38. package/dist/tools/registry.d.ts +10 -0
  39. package/dist/tools/registry.js +64 -0
  40. package/dist/tools/taskComplete.d.ts +2 -0
  41. package/dist/tools/taskComplete.js +19 -0
  42. package/dist/tools/terminal.d.ts +2 -0
  43. package/dist/tools/terminal.js +130 -0
  44. package/dist/tools/utils.d.ts +7 -0
  45. package/dist/tools/utils.js +63 -0
  46. package/dist/tools/web.d.ts +8 -0
  47. package/dist/tools/web.js +181 -0
  48. package/dist/utils/clipboard.d.ts +2 -0
  49. package/dist/utils/clipboard.js +40 -0
  50. package/dist/utils/lock.d.ts +12 -0
  51. package/dist/utils/lock.js +68 -0
  52. package/dist/utils/paths.d.ts +13 -0
  53. package/dist/utils/paths.js +58 -0
  54. package/dist/utils/projectContext.d.ts +2 -0
  55. package/dist/utils/projectContext.js +24 -0
  56. package/dist/utils/sacGuard.d.ts +4 -0
  57. package/dist/utils/sacGuard.js +47 -0
  58. package/dist/utils/sessions.d.ts +29 -0
  59. package/dist/utils/sessions.js +64 -0
  60. package/dist/utils/spinner.d.ts +10 -0
  61. package/dist/utils/spinner.js +32 -0
  62. package/package.json +106 -0
  63. package/src/gui/assets/image/AJAN_LOGO.svg +1 -0
  64. package/src/gui/assets/image/ajan.png +0 -0
  65. package/src/gui/assets/image/logo.svg +1 -0
  66. package/src/gui/assets/image/opencode.svg +18 -0
  67. package/src/gui/css/style.css +430 -0
  68. package/src/gui/html/chat.html +34 -0
  69. package/src/gui/html/downloads.html +11 -0
  70. package/src/gui/html/hakkinda.html +26 -0
  71. package/src/gui/html/index.html +82 -0
  72. package/src/gui/html/projects.html +12 -0
  73. package/src/gui/html/settings.html +4 -0
  74. package/src/gui/js/app.js +197 -0
  75. package/src/gui/js/chat.js +434 -0
  76. package/src/gui/js/downloads.js +172 -0
  77. package/src/gui/js/hakkinda.js +16 -0
  78. package/src/gui/js/projects.js +113 -0
  79. package/src/gui/js/settings.js +177 -0
@@ -0,0 +1,130 @@
1
+ import { spawn } from "node:child_process";
2
+ import { limitOutput, resolveUserPath } from "./utils.js";
3
+ const DEFAULT_BLOCKED_COMMANDS = [
4
+ "^(\\s*)(format|fdisk|diskpart|mkfs|shutdown)\\b",
5
+ "^\\s*(rm|del|erase|rd|rmdir|reg)\\s+.*(-rf|-r\\s+-f|/s|/q|delete)\\b",
6
+ "^\\s*taskkill\\s+/f\\s+/im\\s+system\\b",
7
+ "rm\\s+(-rf|-r\\s+-f)\\s+[/\\\\]"
8
+ ];
9
+ function blockedBy(patterns, command) {
10
+ for (const pat of patterns) {
11
+ try {
12
+ if (new RegExp(pat, "i").test(command))
13
+ return pat;
14
+ }
15
+ catch {
16
+ /* geçersiz deseni yut */
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+ export const runCommandTool = {
22
+ name: "run_command",
23
+ description: "Terminal komutu çalıştırır. cwd, komutun çalıştırılacağı dizindir (varsayılan: ajanın çalışma dizini). " +
24
+ "timeout ms cinsinden zaman aşımıdır (varsayılan: 60000). Çıktı maksimum uzunlukta kesilir.",
25
+ parameters: {
26
+ type: "object",
27
+ properties: {
28
+ command: { type: "string", description: "Çalıştırılacak terminal komutu" },
29
+ cwd: { type: "string", description: "Çalışma dizini (ajanın dizinine göre göreli veya mutlak yol)" },
30
+ timeout: { type: "integer", description: "Zaman aşımı milisaniye" }
31
+ },
32
+ required: ["command"]
33
+ },
34
+ handler: async (params, ctx) => {
35
+ const timeoutMs = params.timeout ?? ctx.config.toolTimeout ?? 60_000;
36
+ const cwd = params.cwd ? resolveUserPath(ctx.cwd, params.cwd) : ctx.cwd;
37
+ const blocked = blockedBy(ctx.config.blockedCommands ?? DEFAULT_BLOCKED_COMMANDS, params.command);
38
+ if (blocked) {
39
+ return {
40
+ ok: false,
41
+ output: `Komut engellendi (tehlikeli komut deseni: ${blocked}).\n` +
42
+ `Engellenen komut: ${params.command}\n` +
43
+ 'İzin vermek için config.json\'daki "blockedCommands" alanını değiştirin ([] boş liste = tümü serbest).'
44
+ };
45
+ }
46
+ const result = await new Promise((resolvePromise) => {
47
+ const isWin = process.platform === "win32";
48
+ const shell = isWin ? "cmd.exe" : "/bin/sh";
49
+ const shellArgs = isWin ? ["/d", "/s", "/c"] : ["-c"];
50
+ const child = spawn(shell, [...shellArgs, params.command], {
51
+ cwd,
52
+ windowsHide: true,
53
+ detached: !isWin,
54
+ env: process.env,
55
+ stdio: ["ignore", "pipe", "pipe"]
56
+ });
57
+ let stdout = "";
58
+ let stderr = "";
59
+ let timedOut = false;
60
+ let settled = false;
61
+ const finish = (code) => {
62
+ if (settled)
63
+ return;
64
+ settled = true;
65
+ clearTimeout(timer);
66
+ clearTimeout(safetyTimer);
67
+ resolvePromise({ code, stdout, stderr, timedOut });
68
+ };
69
+ const timer = setTimeout(() => {
70
+ timedOut = true;
71
+ try {
72
+ if (isWin) {
73
+ child.kill("SIGKILL");
74
+ }
75
+ else if (child.pid != null) {
76
+ process.kill(-child.pid, "SIGKILL");
77
+ }
78
+ }
79
+ catch {
80
+ /* zaten ölmüş olabilir */
81
+ }
82
+ if (isWin && child.pid != null) {
83
+ const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
84
+ windowsHide: true,
85
+ stdio: "ignore"
86
+ });
87
+ killer.unref();
88
+ killer.on("error", () => {
89
+ /* taskkill başlatılamadıysa sessiz geç */
90
+ });
91
+ }
92
+ }, timeoutMs);
93
+ // 'close' ancak süreç bitip tüm pipe'lar kapanınca tetiklenir; askıda kalmamak için
94
+ // güvenlik zamanlayıcısı pipe'ları serbest bırakıp sonucu zorla döndürür.
95
+ const safetyTimer = setTimeout(() => {
96
+ if (!settled) {
97
+ child.stdout?.destroy();
98
+ child.stderr?.destroy();
99
+ finish(child.exitCode ?? null);
100
+ }
101
+ }, timeoutMs + 5_000);
102
+ child.stdout?.on("data", (chunk) => {
103
+ stdout += chunk.toString("utf8");
104
+ if (stdout.length > 2_000_000)
105
+ child.stdout?.pause();
106
+ });
107
+ child.stderr?.on("data", (chunk) => {
108
+ stderr += chunk.toString("utf8");
109
+ if (stderr.length > 2_000_000)
110
+ child.stderr?.pause();
111
+ });
112
+ child.on("error", (err) => {
113
+ stderr += `Komut başlatılamadı: ${err.message}`;
114
+ finish(null);
115
+ });
116
+ child.on("close", (code) => finish(code));
117
+ });
118
+ const parts = [];
119
+ if (result.timedOut)
120
+ parts.push(`[ZAMAN AŞIMI ${timeoutMs}ms]`);
121
+ parts.push(`Çıkış kodu: ${result.code}`);
122
+ if (result.stdout.trim())
123
+ parts.push(`--- STDOUT ---\n${result.stdout.trimEnd()}`);
124
+ if (result.stderr.trim())
125
+ parts.push(`--- STDERR ---\n${result.stderr.trimEnd()}`);
126
+ const ok = result.code === 0 && !result.timedOut;
127
+ return { ok, output: limitOutput(parts.join("\n"), ctx.config.maxToolOutput) };
128
+ }
129
+ };
130
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1,7 @@
1
+ export declare function limitOutput(text: string, maxChars?: number): string;
2
+ export declare const SKIP_DIRS: Set<string>;
3
+ /** Ağır veya gizli klasörlerde gezinmeyi atla (node_modules, dist, .git vb.) */
4
+ export declare function shouldSkipDir(name: string): boolean;
5
+ export declare function toMb(bytes: number): string;
6
+ /** 'masaüstü', 'belgeler', 'indirilenler' gibi adları kullanıcının gerçek klasörlerine çözer */
7
+ export declare function resolveUserPath(cwd: string, input: string): string;
@@ -0,0 +1,63 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ export function limitOutput(text, maxChars) {
5
+ const limit = maxChars ?? 40_000;
6
+ if (text.length <= limit)
7
+ return text;
8
+ const truncated = text.slice(0, limit);
9
+ return `${truncated}\n... [çıktı ${text.length - limit} karakter kesildi]`;
10
+ }
11
+ export const SKIP_DIRS = new Set([
12
+ "node_modules", ".git", "dist", "build", ".next", ".cache",
13
+ "__pycache__", ".venv", "venv", ".idea", ".vscode", "coverage"
14
+ ]);
15
+ /** Ağır veya gizli klasörlerde gezinmeyi atla (node_modules, dist, .git vb.) */
16
+ export function shouldSkipDir(name) {
17
+ return name.startsWith(".") || SKIP_DIRS.has(name);
18
+ }
19
+ export function toMb(bytes) {
20
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
21
+ }
22
+ function homeFolder(key) {
23
+ const candidates = [];
24
+ const home = os.homedir();
25
+ candidates.push(path.join(home, key));
26
+ const userProfile = process.env.USERPROFILE ?? process.env.HOME;
27
+ if (userProfile)
28
+ candidates.push(path.join(userProfile, key));
29
+ const oneDrive = process.env.ONEDRIVE;
30
+ if (oneDrive)
31
+ candidates.push(path.join(oneDrive, key));
32
+ for (const c of candidates) {
33
+ try {
34
+ if (existsSync(c))
35
+ return c;
36
+ }
37
+ catch {
38
+ /* yut */
39
+ }
40
+ }
41
+ return candidates[0] ?? null;
42
+ }
43
+ const FOLDER_PATTERNS = [
44
+ { regex: /masaüstü|masaustu|desktop/i, key: "Desktop" },
45
+ { regex: /belgeler|belgelerim|belge|documents/i, key: "Documents" },
46
+ { regex: /indirilenler|indirilen|indirmeler|downloads/i, key: "Downloads" }
47
+ ];
48
+ /** 'masaüstü', 'belgeler', 'indirilenler' gibi adları kullanıcının gerçek klasörlerine çözer */
49
+ export function resolveUserPath(cwd, input) {
50
+ const trimmed = input.trim();
51
+ for (const { regex, key } of FOLDER_PATTERNS) {
52
+ const match = trimmed.match(new RegExp(`^(${regex.source})(?:[\\\\/](.*))?$`, "i"));
53
+ if (!match)
54
+ continue;
55
+ const base = homeFolder(key);
56
+ if (!base)
57
+ continue;
58
+ const rest = match[2];
59
+ return rest ? path.resolve(base, rest) : base;
60
+ }
61
+ return path.isAbsolute(input) ? path.resolve(input) : path.resolve(cwd, input);
62
+ }
63
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,8 @@
1
+ import type { AgentTool } from "../config/types.js";
2
+ export type WebSearchResult = {
3
+ title: string;
4
+ url: string;
5
+ snippet: string;
6
+ };
7
+ export declare const webSearchTool: AgentTool;
8
+ export declare const fetchUrlTool: AgentTool;
@@ -0,0 +1,181 @@
1
+ import { limitOutput } from "./utils.js";
2
+ function decodeEntities(text) {
3
+ return text
4
+ .replace(/&amp;/g, "&")
5
+ .replace(/&lt;/g, "<")
6
+ .replace(/&gt;/g, ">")
7
+ .replace(/&quot;/g, '"')
8
+ .replace(/&#39;/g, "'")
9
+ .replace(/&nbsp;/g, " ")
10
+ .replace(/&#(\d+);/g, (_, code) => {
11
+ const cp = Number(code);
12
+ return cp > 0 ? String.fromCodePoint(cp) : "";
13
+ });
14
+ }
15
+ function stripTags(text) {
16
+ return decodeEntities(text.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim());
17
+ }
18
+ function decodeRedirectUrl(href) {
19
+ try {
20
+ if (href.startsWith("//"))
21
+ href = `https:${href}`;
22
+ const u = new URL(href);
23
+ if (u.hostname.includes("duckduckgo.com")) {
24
+ const target = u.searchParams.get("uddg");
25
+ if (target)
26
+ return decodeURIComponent(target);
27
+ }
28
+ return href;
29
+ }
30
+ catch {
31
+ return href;
32
+ }
33
+ }
34
+ function parseResults(html, maxResults) {
35
+ const results = [];
36
+ const resultRe = /<a[^>]*class="(?:result__a|result-link)"[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
37
+ const snippetRe = /<a[^>]*class="(?:result__snippet|result-snippet)"[^>]*>(.*?)<\/a>/gi;
38
+ let match;
39
+ const titles = [];
40
+ while ((match = resultRe.exec(html)) !== null && titles.length < maxResults) {
41
+ titles.push({ href: match[1] ?? "", title: stripTags(match[2] ?? "") });
42
+ }
43
+ if (titles.length === 0) {
44
+ const genericRe = /<a[^>]*href="(https?:\/\/[^"]+)"[^>]*>(.*?)<\/a>/gi;
45
+ while ((match = genericRe.exec(html)) !== null && titles.length < maxResults) {
46
+ const title = stripTags(match[2] ?? "");
47
+ if (!title)
48
+ continue;
49
+ titles.push({ href: match[1] ?? "", title });
50
+ }
51
+ }
52
+ const snippets = [];
53
+ while ((match = snippetRe.exec(html)) !== null && snippets.length < titles.length) {
54
+ snippets.push(stripTags(match[1] ?? ""));
55
+ }
56
+ for (let i = 0; i < titles.length; i++) {
57
+ const item = titles[i];
58
+ if (!item || !item.title)
59
+ continue;
60
+ results.push({
61
+ title: item.title,
62
+ url: decodeRedirectUrl(item.href),
63
+ snippet: snippets[i] ?? ""
64
+ });
65
+ }
66
+ return results;
67
+ }
68
+ export const webSearchTool = {
69
+ name: "web_search",
70
+ description: "Web'de arama yapar (DuckDuckGo, API anahtarı gerektirmez). Güncel bilgi, haber veya doküman bulmak için kullan. " +
71
+ "maxResults (varsayılan 5, en fazla 10) kadar sonuç döner: başlık, özet ve URL.",
72
+ parameters: {
73
+ type: "object",
74
+ properties: {
75
+ query: { type: "string", description: "Arama sorgusu (İngilizce daha iyi sonuç verir)" },
76
+ maxResults: { type: "integer", description: "Döndürülecek sonuç sayısı (varsayılan: 5, maks: 10)" }
77
+ },
78
+ required: ["query"]
79
+ },
80
+ handler: async (params, ctx) => {
81
+ const query = params.query.trim();
82
+ if (!query)
83
+ return { ok: false, output: "Arama sorgusu boş olamaz." };
84
+ const maxResults = Math.min(Math.max(params.maxResults ?? 5, 1), 10);
85
+ const timeoutMs = ctx.config.toolTimeout ?? 15_000;
86
+ const controller = new AbortController();
87
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
88
+ let html;
89
+ try {
90
+ const res = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`, {
91
+ headers: {
92
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
93
+ },
94
+ signal: controller.signal,
95
+ redirect: "follow"
96
+ });
97
+ if (!res.ok) {
98
+ return { ok: false, output: `Arama başarısız (HTTP ${res.status}): ${res.statusText}` };
99
+ }
100
+ html = await res.text();
101
+ }
102
+ catch (err) {
103
+ const aborted = controller.signal.aborted;
104
+ return {
105
+ ok: false,
106
+ output: aborted
107
+ ? `Arama zaman aşımı (${timeoutMs}ms) veya iptal edildi.`
108
+ : `Arama hatası: ${err.message}`
109
+ };
110
+ }
111
+ finally {
112
+ clearTimeout(timer);
113
+ }
114
+ const results = parseResults(html, maxResults);
115
+ if (results.length === 0) {
116
+ return { ok: true, output: `"${query}" için sonuç bulunamadı.` };
117
+ }
118
+ const lines = results.map((r, i) => `${i + 1}. ${r.title}\n ${r.url}\n ${r.snippet || "(özet yok)"}`);
119
+ return { ok: true, output: limitOutput(lines.join("\n\n"), ctx.config.maxToolOutput) };
120
+ }
121
+ };
122
+ function htmlToText(html) {
123
+ return stripTags(html
124
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
125
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
126
+ .replace(/<(br|p|div|li|h[1-6]|tr)[^>]*>/gi, "\n"));
127
+ }
128
+ export const fetchUrlTool = {
129
+ name: "fetch_url",
130
+ description: "Bir URL'nin içeriğini indirip metin olarak döner. HTML sayfalar düz metne çevrilir. " +
131
+ "web_search ile bulunan bir sayfanın detayını okumak için kullan.",
132
+ parameters: {
133
+ type: "object",
134
+ properties: {
135
+ url: { type: "string", description: "İndirilecek adres (http/https)" },
136
+ maxLength: { type: "integer", description: "Döndürülecek maksimum karakter (varsayılan 12000, maks 40000)" }
137
+ },
138
+ required: ["url"]
139
+ },
140
+ handler: async (params, ctx) => {
141
+ const url = params.url?.trim();
142
+ if (!url || !/^https?:\/\//i.test(url)) {
143
+ return { ok: false, output: "Geçerli bir http(s) URL gerekli." };
144
+ }
145
+ const timeoutMs = ctx.config.toolTimeout ?? 20_000;
146
+ const controller = new AbortController();
147
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
148
+ try {
149
+ const res = await fetch(url, {
150
+ headers: {
151
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
152
+ },
153
+ signal: controller.signal,
154
+ redirect: "follow"
155
+ });
156
+ if (!res.ok) {
157
+ return { ok: false, output: `İndirme başarısız (HTTP ${res.status}): ${res.statusText}` };
158
+ }
159
+ const contentType = res.headers.get("content-type") ?? "";
160
+ const body = await res.text();
161
+ const isHtml = contentType.includes("text/html") || /^\s*<(!doctype|html)/i.test(body);
162
+ const text = isHtml ? htmlToText(body) : body;
163
+ const maxLen = Math.min(Math.max(params.maxLength ?? 12_000, 500), 40_000);
164
+ const header = `URL: ${url}\nTür: ${contentType || "bilinmiyor"}\nUzunluk: ${text.length} karakter\n\n`;
165
+ return { ok: true, output: limitOutput(header + text, maxLen + header.length + 100) };
166
+ }
167
+ catch (err) {
168
+ const aborted = controller.signal.aborted;
169
+ return {
170
+ ok: false,
171
+ output: aborted
172
+ ? `Zaman aşımı (${timeoutMs}ms) veya iptal edildi.`
173
+ : `İndirme hatası: ${err.message}`
174
+ };
175
+ }
176
+ finally {
177
+ clearTimeout(timer);
178
+ }
179
+ }
180
+ };
181
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1,2 @@
1
+ /** Metni pano'ya kopyalar. Windows'ta PowerShell Set-Clipboard, diğerlerinde dosya fallback. */
2
+ export declare function copyToClipboard(text: string): Promise<string>;
@@ -0,0 +1,40 @@
1
+ import { spawn } from "node:child_process";
2
+ import { writeFileSync, mkdirSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { getCacheDir } from "./paths.js";
5
+ /** Metni pano'ya kopyalar. Windows'ta PowerShell Set-Clipboard, diğerlerinde dosya fallback. */
6
+ export function copyToClipboard(text) {
7
+ if (process.platform === "win32") {
8
+ const b64 = Buffer.from(text, "utf16le").toString("base64");
9
+ const script = `Set-Clipboard -Value ([System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('${b64}')))`;
10
+ return new Promise((resolve) => {
11
+ const child = spawn("powershell", ["-NoProfile", "-Command", script], {
12
+ windowsHide: true,
13
+ stdio: "ignore"
14
+ });
15
+ let failed = false;
16
+ child.on("error", () => {
17
+ failed = true;
18
+ resolve(fallbackCopy(text));
19
+ });
20
+ child.on("close", () => {
21
+ if (!failed)
22
+ resolve("Pano'ya kopyalandı (Windows).");
23
+ });
24
+ });
25
+ }
26
+ return Promise.resolve(fallbackCopy(text));
27
+ }
28
+ function fallbackCopy(text) {
29
+ try {
30
+ const dir = getCacheDir();
31
+ mkdirSync(dir, { recursive: true });
32
+ const file = join(dir, "clipboard.txt");
33
+ writeFileSync(file, text, "utf8");
34
+ return `Panoya kopyalanamadı; içerik şuraya yazıldı: ${file}`;
35
+ }
36
+ catch {
37
+ return "Panoya kopyalama desteklenmiyor.";
38
+ }
39
+ }
40
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1,12 @@
1
+ interface LockData {
2
+ pid: number;
3
+ type: "gui" | "tui";
4
+ startedAt: string;
5
+ }
6
+ export declare function acquireLock(type: "gui" | "tui"): {
7
+ ok: boolean;
8
+ error?: string;
9
+ };
10
+ export declare function releaseLock(): void;
11
+ export declare function getLockInfo(): LockData | null;
12
+ export {};
@@ -0,0 +1,68 @@
1
+ import { readFileSync, writeFileSync, unlinkSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { getDataDir } from "./paths.js";
4
+ function getLockPath() {
5
+ return join(getDataDir(), "ajan.lock");
6
+ }
7
+ function isProcessAlive(pid) {
8
+ try {
9
+ process.kill(pid, 0);
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ export function acquireLock(type) {
17
+ const lockPath = getLockPath();
18
+ if (existsSync(lockPath)) {
19
+ try {
20
+ const data = JSON.parse(readFileSync(lockPath, "utf8"));
21
+ if (isProcessAlive(data.pid)) {
22
+ const runningType = data.type.toUpperCase();
23
+ return {
24
+ ok: false,
25
+ error: `AJAN ${runningType} zaten çalışıyor (PID: ${data.pid}).\nAynı anda yalnızca bir arayüz çalışabilir.`
26
+ };
27
+ }
28
+ // Stale lock — process ölü, temizle
29
+ }
30
+ catch {
31
+ // Bozuk lock dosyası, sil
32
+ }
33
+ try {
34
+ unlinkSync(lockPath);
35
+ }
36
+ catch { /* empty */ }
37
+ }
38
+ const lockData = {
39
+ pid: process.pid,
40
+ type,
41
+ startedAt: new Date().toISOString()
42
+ };
43
+ writeFileSync(lockPath, JSON.stringify(lockData, null, 2), "utf8");
44
+ return { ok: true };
45
+ }
46
+ export function releaseLock() {
47
+ const lockPath = getLockPath();
48
+ try {
49
+ if (existsSync(lockPath)) {
50
+ const data = JSON.parse(readFileSync(lockPath, "utf8"));
51
+ if (data.pid === process.pid) {
52
+ unlinkSync(lockPath);
53
+ }
54
+ }
55
+ }
56
+ catch { /* empty */ }
57
+ }
58
+ export function getLockInfo() {
59
+ const lockPath = getLockPath();
60
+ try {
61
+ if (existsSync(lockPath)) {
62
+ return JSON.parse(readFileSync(lockPath, "utf8"));
63
+ }
64
+ }
65
+ catch { /* empty */ }
66
+ return null;
67
+ }
68
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1,13 @@
1
+ export type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
2
+ export declare function setLogLevel(level: LogLevel): void;
3
+ export declare const logger: {
4
+ error: (...args: unknown[]) => void;
5
+ warn: (...args: unknown[]) => void;
6
+ info: (...args: unknown[]) => void;
7
+ debug: (...args: unknown[]) => void;
8
+ };
9
+ export declare function getDataDir(): string;
10
+ export declare function getModelsDir(): string;
11
+ export declare function getConfigPath(): string;
12
+ export declare function getCacheDir(): string;
13
+ export declare function getLogDir(): string;
@@ -0,0 +1,58 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import os from "node:os";
4
+ let currentLevel = "info";
5
+ export function setLogLevel(level) {
6
+ currentLevel = level;
7
+ }
8
+ const levelOrder = {
9
+ silent: 0,
10
+ error: 1,
11
+ warn: 2,
12
+ info: 3,
13
+ debug: 4
14
+ };
15
+ function log(level, tag, ...args) {
16
+ if (levelOrder[level] > levelOrder[currentLevel])
17
+ return;
18
+ const prefix = `[${tag}]`;
19
+ if (level === "error") {
20
+ console.error(prefix, ...args);
21
+ }
22
+ else if (level === "warn") {
23
+ console.warn(prefix, ...args);
24
+ }
25
+ else {
26
+ console.log(prefix, ...args);
27
+ }
28
+ }
29
+ export const logger = {
30
+ error: (...args) => log("error", "hata", ...args),
31
+ warn: (...args) => log("warn", "uyari", ...args),
32
+ info: (...args) => log("info", "bilgi", ...args),
33
+ debug: (...args) => log("debug", "debug", ...args)
34
+ };
35
+ export function getDataDir() {
36
+ const base = process.env.AJAN_HOME ?? join(os.homedir(), ".ajan");
37
+ mkdirSync(base, { recursive: true });
38
+ return base;
39
+ }
40
+ export function getModelsDir() {
41
+ const dir = join(getDataDir(), "models");
42
+ mkdirSync(dir, { recursive: true });
43
+ return dir;
44
+ }
45
+ export function getConfigPath() {
46
+ return join(getDataDir(), "config.json");
47
+ }
48
+ export function getCacheDir() {
49
+ const dir = join(getDataDir(), "cache");
50
+ mkdirSync(dir, { recursive: true });
51
+ return dir;
52
+ }
53
+ export function getLogDir() {
54
+ const dir = join(getDataDir(), "logs");
55
+ mkdirSync(dir, { recursive: true });
56
+ return dir;
57
+ }
58
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1,2 @@
1
+ /** cwd içinde AGENTS.md veya AJAN.md varsa içeriğini sistem promptuna eklenebilir metin olarak döndürür */
2
+ export declare function loadProjectContext(cwd: string): string;
@@ -0,0 +1,24 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const MAX_CONTEXT_CHARS = 20_000;
4
+ const CANDIDATES = ["AGENTS.md", "AJAN.md"];
5
+ /** cwd içinde AGENTS.md veya AJAN.md varsa içeriğini sistem promptuna eklenebilir metin olarak döndürür */
6
+ export function loadProjectContext(cwd) {
7
+ for (const name of CANDIDATES) {
8
+ const file = join(cwd, name);
9
+ if (!existsSync(file))
10
+ continue;
11
+ try {
12
+ const content = readFileSync(file, "utf8").trim();
13
+ if (!content)
14
+ return "";
15
+ const limited = content.length > MAX_CONTEXT_CHARS ? `${content.slice(0, MAX_CONTEXT_CHARS)}\n... [proje notları kesildi]` : content;
16
+ return `\n\n--- PROJE NOTLARI (${name}) ---\n${limited}\n--- PROJE NOTLARI SONU ---`;
17
+ }
18
+ catch {
19
+ /* okunamayan not dosyasını yut */
20
+ }
21
+ }
22
+ return "";
23
+ }
24
+ //# sourceMappingURL=projectContext.js.map
@@ -0,0 +1,4 @@
1
+ export type SacState = "kapali" | "engelleme" | "degerlendirme" | "yok";
2
+ export declare function getSacState(): Promise<SacState>;
3
+ export declare function describeSacState(state: SacState): string;
4
+ export declare function isSacBlocking(state: SacState): boolean;
@@ -0,0 +1,47 @@
1
+ import { execFile } from "node:child_process";
2
+ const REG_PATH = "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\CI\\Policy";
3
+ const REG_NAME = "VerifiedAndReputablePolicyState";
4
+ function execPowerShell(command, timeoutMs = 180000) {
5
+ return new Promise((resolve) => {
6
+ execFile("powershell.exe", ["-NoProfile", "-Command", command], { timeout: timeoutMs, windowsHide: true }, (err, stdout) => {
7
+ let code = 0;
8
+ if (err) {
9
+ const c = err.code;
10
+ code = typeof c === "number" ? c : err.killed ? 124 : 1;
11
+ }
12
+ resolve({ code, stdout: String(stdout ?? "") });
13
+ });
14
+ });
15
+ }
16
+ export async function getSacState() {
17
+ if (process.platform !== "win32")
18
+ return "yok";
19
+ const { code, stdout } = await execPowerShell(`(Get-ItemProperty -LiteralPath '${REG_PATH}' -Name '${REG_NAME}' -ErrorAction SilentlyContinue).${REG_NAME}`);
20
+ const raw = stdout.trim().split(/\r?\n/).pop()?.trim() ?? "";
21
+ if (code !== 0 || raw === "")
22
+ return "yok";
23
+ const value = raw.startsWith("0x") || raw.startsWith("0X") ? parseInt(raw, 16) : Number(raw);
24
+ if (value === 1)
25
+ return "engelleme";
26
+ if (value === 2)
27
+ return "degerlendirme";
28
+ if (value === 0)
29
+ return "kapali";
30
+ return "yok";
31
+ }
32
+ export function describeSacState(state) {
33
+ switch (state) {
34
+ case "engelleme":
35
+ return "açık (engelleme modu)";
36
+ case "degerlendirme":
37
+ return "değerlendirme modu";
38
+ case "kapali":
39
+ return "kapalı";
40
+ default:
41
+ return "yok / desteklenmiyor";
42
+ }
43
+ }
44
+ export function isSacBlocking(state) {
45
+ return process.platform === "win32" && state === "engelleme";
46
+ }
47
+ //# sourceMappingURL=sacGuard.js.map