quotalala 0.0.0-stage β 1.0.0
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/LICENSE +21 -0
- package/README.md +175 -2
- package/app/assets/icon.ico +0 -0
- package/app/assets/icon.png +0 -0
- package/app/assets/icon.svg +5 -0
- package/app/package.json +7 -0
- package/app/src/main/agents/base-agent.js +118 -0
- package/app/src/main/agents/claude.js +52 -0
- package/app/src/main/agents/codex.js +66 -0
- package/app/src/main/agents/gemini.js +42 -0
- package/app/src/main/agents/index.js +24 -0
- package/app/src/main/capabilities.js +23 -0
- package/app/src/main/cli-locator.js +160 -0
- package/app/src/main/coordinator.js +154 -0
- package/app/src/main/default-models.json +80 -0
- package/app/src/main/events.js +22 -0
- package/app/src/main/health.js +43 -0
- package/app/src/main/main.js +115 -0
- package/app/src/main/models.js +185 -0
- package/app/src/main/preload.js +19 -0
- package/app/src/main/privacy.js +19 -0
- package/app/src/main/quota.js +262 -0
- package/app/src/main/session-runner.js +84 -0
- package/app/src/main/storage.js +108 -0
- package/app/src/main/task-outcome.js +18 -0
- package/app/src/main/workspace.js +47 -0
- package/app/src/renderer/app.js +305 -0
- package/app/src/renderer/i18n/en.json +151 -0
- package/app/src/renderer/i18n/tr.json +151 -0
- package/app/src/renderer/index.html +67 -0
- package/app/src/renderer/log-model.js +13 -0
- package/app/src/renderer/style.css +20 -0
- package/app/src/shared/activity.js +45 -0
- package/bin/quotalala.js +42 -0
- package/package.json +26 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Quotalala Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,176 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Quotalala ποΈ
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Quotalala** is a local desktop application built with Electron that automatically distributes a single prompt across Claude, ChatGPT (Codex), and Google Gemini subscription quotas without wasting usage limits. Claude serves as the main coordinator and synthesizer.
|
|
4
|
+
|
|
5
|
+
Launch Quotalala from the project you are working on. The npm launcher opens the desktop app with that folder as the working directory for the agents, and its Quotalala conversations and project memory load automatically. Write your prompt and continue working in that project.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Key Principles & Guarantees
|
|
10
|
+
|
|
11
|
+
- **No API Keys**: Only invokes installed and authenticated command-line tools on your machine (`claude -p`, `codex exec`, `agy`), with prompts sent through stdin.
|
|
12
|
+
- **Zero Credential Scraping**: Never reads, copies, or proxies user auth tokens or session secrets.
|
|
13
|
+
- **Secure Architecture**: Electron desktop app with `contextIsolation: true`, `nodeIntegration: false`, and sandboxed IPC communication.
|
|
14
|
+
- **Windows-Optimized**: Native handling of `.cmd`, `.bat`, and `.exe` binaries, piping prompts through `stdin` to eliminate command-line character escaping issues.
|
|
15
|
+
- **System Tray & Desktop Notifications**: Minimizes to system tray on close with real-time quota status on hover and Windows reminder notifications when quotas are approaching renewal.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Architecture & Project Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
quotalala/
|
|
23
|
+
βββ src/
|
|
24
|
+
β βββ main/ # Electron main process
|
|
25
|
+
β β βββ main.js # Window, system tray, native notifications
|
|
26
|
+
β β βββ preload.js # Secure contextBridge IPC
|
|
27
|
+
β β βββ coordinator.js # Claude task breakdown & synthesis
|
|
28
|
+
β β βββ quota.js # Persistent quota & usage tracking
|
|
29
|
+
β β βββ cli-locator.js # Cross-platform CLI binary resolution
|
|
30
|
+
β β βββ agents/ # Modular agent plugins
|
|
31
|
+
β β βββ base-agent.js # Base agent class
|
|
32
|
+
β β βββ claude.js # Claude Code CLI module
|
|
33
|
+
β β βββ codex.js # Codex / ChatGPT CLI module
|
|
34
|
+
β β βββ gemini.js # Google Gemini CLI module
|
|
35
|
+
β βββ renderer/ # Desktop UI (Single Screen)
|
|
36
|
+
β βββ index.html # Clean HTML layout
|
|
37
|
+
β βββ style.css # Modern dark-mode styling
|
|
38
|
+
β βββ app.js # IPC client logic & live streams
|
|
39
|
+
β βββ i18n/ # Multi-language translations
|
|
40
|
+
β βββ tr.json # Turkish (default)
|
|
41
|
+
β βββ en.json # English
|
|
42
|
+
βββ assets/ # Application icons (.ico, .png)
|
|
43
|
+
βββ test/ # Diagnostics & tests
|
|
44
|
+
βββ AGENTS.md # Agent system guidelines
|
|
45
|
+
βββ LICENSE # MIT License
|
|
46
|
+
βββ package.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Getting Started
|
|
52
|
+
|
|
53
|
+
### Prerequisites
|
|
54
|
+
|
|
55
|
+
- Node.js 22.12+ for development (tested on Node v24). The installed desktop application includes its own runtime.
|
|
56
|
+
- Local AI CLI tools installed and logged in:
|
|
57
|
+
- **Claude Code**: `@anthropic-ai/claude-code` (`claude auth status`)
|
|
58
|
+
- **Codex CLI**: `codex-cli` (`codex login status`)
|
|
59
|
+
- **Antigravity CLI (Gemini)**: `agy` (optional, disabled by default; sign in interactively)
|
|
60
|
+
|
|
61
|
+
> [!WARNING]
|
|
62
|
+
> **Gemini / Antigravity CLI Notice**: Some accounts using Antigravity CLI automation have reportedly been restricted by Google. Gemini starts disabled and requires explicit acceptance of the risk notice on its card. Its stdin and optional effort capabilities are checked against the installed CLI's help; unverified flags are never used.
|
|
63
|
+
|
|
64
|
+
### Use Quotalala in your project
|
|
65
|
+
|
|
66
|
+
The Windows npm launcher opens the desktop app directly in your project:
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
# Run from your project's terminal.
|
|
70
|
+
npx quotalala
|
|
71
|
+
|
|
72
|
+
# Or install once, then use the short command in any project.
|
|
73
|
+
npm install -g quotalala
|
|
74
|
+
quotalala
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The npm launcher always uses your terminal's current folder, including folders without project markers. Its sidebar loads that project's saved conversations, and its memory panel loads the project's summary. Every new task runs in that folder. You can also use `quotalala --project "C:\projects\my-app"` or `quotalala "C:\projects\my-app"` to open another folder.
|
|
78
|
+
|
|
79
|
+
The npm package requires Node.js 22.12+ and downloads its pinned Electron runtime during dependency installation. It does not require a separate Quotalala installer or native compilation. The first installation/download can take some time; normal launches use the installed runtime. It opens the Electron desktop interface.
|
|
80
|
+
|
|
81
|
+
#### Standalone Windows installer
|
|
82
|
+
|
|
83
|
+
The NSIS installer remains an alternative without a Node.js requirement. Launch its executable from your project's terminal, replacing the example with the actual installed path:
|
|
84
|
+
|
|
85
|
+
```powershell
|
|
86
|
+
& "C:\path\to\Quotalala.exe"
|
|
87
|
+
|
|
88
|
+
# Explicit project, useful when launching from another directory:
|
|
89
|
+
& "C:\path\to\Quotalala.exe" --project "C:\projects\my-app"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The standalone executable detects the current folder when it contains `.git`, `AGENTS.md`, `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod` or `README.md`. Launching it without a detected or explicit project restores the last project. The sidebar's folder picker is available to switch projects, or to open one when no previous project exists.
|
|
93
|
+
|
|
94
|
+
### Develop Quotalala itself
|
|
95
|
+
|
|
96
|
+
Run these commands in the cloned **Quotalala repository**:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Install dependencies
|
|
100
|
+
npm ci
|
|
101
|
+
|
|
102
|
+
# Run desktop application
|
|
103
|
+
npm start
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
To test the development application against another project, use `npm start -- --project "C:\projects\my-app"` from the Quotalala repository.
|
|
107
|
+
|
|
108
|
+
### Packaging Windows Installer
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Builds NSIS .exe installer in release/
|
|
112
|
+
npm run build
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Packaging the npm launcher
|
|
116
|
+
|
|
117
|
+
```powershell
|
|
118
|
+
# From the Quotalala repository; validates an explicit package file list.
|
|
119
|
+
npm run pack:cli
|
|
120
|
+
|
|
121
|
+
# Local installation for testing before npm publication (changes your global npm installation):
|
|
122
|
+
npm install -g .\.test-artifacts\npm\quotalala-1.0.0.tgz
|
|
123
|
+
|
|
124
|
+
# Then, from the project you want to work on:
|
|
125
|
+
quotalala
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The generated archive includes the launcher, application source/assets, README and license. Its only runtime dependency is the pinned Electron package; development/build tools and user data are excluded. The root development manifest remains separate so the NSIS build continues using Electron as a development dependency. Packaging does not publish to npm.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## History and project memory
|
|
133
|
+
|
|
134
|
+
The project detected at launch controls all task working directories, conversation history and project memory. Quotalala remembers the last project and lists recent folders for switching between projects. Its workspace preference is stored locally in `~/.quotalala/workspace.json`, independently of whether conversation saving is enabled.
|
|
135
|
+
|
|
136
|
+
The application checks that the active directory is readable before spending CLI quota; it does not bypass the CLI's own sandbox or policy. The sidebar shows Quotalala conversations saved for that folder. Native Claude/Codex/Gemini transcripts are not imported into this history.
|
|
137
|
+
|
|
138
|
+
The desktop layout has a conversation sidebar, compact agent cards, a central task composer and a project-memory panel. Outputs, activity timeline and diagnostic logs have separate tabs. `Ctrl+N` opens a new conversation and `Ctrl+Enter` sends a task from the composer.
|
|
139
|
+
|
|
140
|
+
Each coordinated run records its prompt, plan, agent/model choices, outputs, structured logs and status. Open, rename, delete or continue sessions from the project-grouped sidebar. Continuing uses an exact native CLI session ID when supported; if that session is unavailable, project memory provides the context. Resume support is verified using at most two help commands per CLI per application run. Claude and Codex support was verified locally; `agy` was unavailable on the development machine.
|
|
141
|
+
|
|
142
|
+
Data stays in `~/.quotalala/sessions/` and `~/.quotalala/memory/` as JSON. Claude updates a bounded 2000-character project summary after each saved run, using the light model tier. You can edit this summary, reset a project's memory, turn off saving, or delete all sessions and memories in Settings. Disabling saving also skips automatic memory generation; explicitly saving an edited summary still stores it. CLI tools may independently retain their own transcripts.
|
|
143
|
+
|
|
144
|
+
Recognizable API keys, bearer tokens, JWTs and labeled secrets are masked before Quotalala writes records. Redaction is pattern based and cannot recognize every possible unlabeled secret; review raw diagnostics before sharing them. Quota error history remains capped at 200 characters per entry, independently of full session output.
|
|
145
|
+
|
|
146
|
+
Logs support severity and agent filters, text selection, Ctrl+C, a native Copy/Select All context menu, row copy controls and Copy All. Error cards provide a localized explanation, suggested fix and copyable raw output. Task outputs have the same selection/copy controls. All generated content is inserted as text rather than executable HTML.
|
|
147
|
+
|
|
148
|
+
Models come from `~/.quotalala/models.json`; Settings exposes the list with required source metadata. The planner selects a tier and the backend resolves it to a model. Preview models are excluded from default choices. An unavailable model falls back to the CLI default with a warning. Automatic model catalog discovery is still pending.
|
|
149
|
+
|
|
150
|
+
Gemini has no bundled model names because they could not be verified on the development machine. Add a model with an actual CLI/local-source reference after verifying your `agy` installation, or use its default model. Invalid local model catalogs fall back to the validated bundled list without replacing your file.
|
|
151
|
+
|
|
152
|
+
Sessions also retain a structured activity timeline: planning, assignments with model/tier/reason, completion or failure, output transfers, quota-driven agent changes and final review. Expand an event to inspect and copy its input/output; related assignment and completion records are linked without duplicating payloads. Events appear live and are saved immediately with the existing secret masking and history preferences. Older sessions without activity records show an empty timeline notice.
|
|
153
|
+
|
|
154
|
+
Planning receives project memory plus at most 20 short activity headings from that project. When continuing a session, its own timeline headings take priority within that same 20-event budget. Complete historical event inputs and outputs are never included in this activity context.
|
|
155
|
+
|
|
156
|
+
## Validation
|
|
157
|
+
|
|
158
|
+
Task completion is evaluated separately from a successful CLI exit. Codex command failures are retained alongside the final answer. Project review tasks require file/line references and, when command telemetry is available, a successful file-read command. A policy-denied task is shown as blocked and does not increase the completed-job counter. Remaining file-review tasks are skipped after a shared access denial; Quotalala does not automatically route around the policy with another agent or disable CLI protections. This detects common failure reports and command evidence; it is not a proof that every finding is correct.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm test # Offline unit/integration tests, mocked CLIs
|
|
162
|
+
npm run test:ui # Electron UI tests, mocked CLIs and local fixture storage
|
|
163
|
+
npm run test:startup # Production IPC startup with local fixtures
|
|
164
|
+
npm run check # Git candidate files, syntax, translations, lockfile and secret patterns
|
|
165
|
+
npm run check:package -- path/to/app.asar # Compare a built package with current source
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Fixtures and screenshots stay in `.test-artifacts/`, which is ignored by Git. The opt-in `node test/e2e-real.js --approved` performs one short real coordinated flow and writes to the normal local store. Run it only after permission to use CLI quota and create records outside the repository. Do not run live diagnostics automatically on startup.
|
|
169
|
+
|
|
170
|
+
## Release preparation
|
|
171
|
+
|
|
172
|
+
Windows source checks and mocked Electron tests are configured in `.github/workflows/checks.yml`. A manual Actions run also builds and verifies an unsigned installer for download, without publishing a Release. See [RELEASE-CHECKLIST.md](RELEASE-CHECKLIST.md) for verified checks, known build dependency advisories and the remaining release steps.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT Β© Quotalala Contributors
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<rect width="512" height="512" rx="112" fill="#bdd4b9"/>
|
|
3
|
+
<path fill="#223227" fill-rule="evenodd" d="M252 126c-71 0-117 51-117 121s44 119 106 119c24 0 44-7 59-21v87h56V132h-56v16c-13-14-29-22-48-22zm-3 52c32 0 52 26 52 68 0 43-21 69-52 69s-54-25-54-69c0-42 21-68 54-68z"/>
|
|
4
|
+
<circle cx="409" cy="337" r="27" fill="#223227"/>
|
|
5
|
+
</svg>
|
package/app/package.json
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const { runWithStdin } = require('../cli-locator');
|
|
2
|
+
|
|
3
|
+
class BaseAgent {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.id = options.id;
|
|
6
|
+
this.name = options.name;
|
|
7
|
+
this.provider = options.provider;
|
|
8
|
+
this.role = options.role;
|
|
9
|
+
this.command = options.command;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getRunArgs() {
|
|
13
|
+
throw new Error('getRunArgs must be implemented by subclass');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async getVersion() {
|
|
17
|
+
const res = await this.execute(['--version'], '', { timeout: 10000 });
|
|
18
|
+
if (res.code === 0 && res.stdout) {
|
|
19
|
+
return res.stdout.split(/\r?\n/)[0].trim();
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async checkAuth() {
|
|
25
|
+
return { loggedIn: false };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async runHealthTest(onLog = () => {}, options = {}) {
|
|
29
|
+
onLog(`[${this.id.toUpperCase()}] SaΔlΔ±k testi baΕlatΔ±lΔ±yor...`);
|
|
30
|
+
|
|
31
|
+
const version = await this.getVersion();
|
|
32
|
+
onLog(`[${this.id.toUpperCase()}] CLI SΓΌrΓΌmΓΌ: ${version || 'BulunamadΔ±'}`);
|
|
33
|
+
|
|
34
|
+
const authInfo = await this.checkAuth();
|
|
35
|
+
if (authInfo.loggedIn) {
|
|
36
|
+
onLog(`[${this.id.toUpperCase()}] Oturum: Aktif (${authInfo.details || authInfo.subscription || ''})`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onLog(`[${this.id.toUpperCase()}] stdin ΓΌzerinden test promptu gΓΆnderiliyor: "OK yaz"`);
|
|
40
|
+
let res = await this.runPrompt('Sadece tek kelime OK yaz', { ...options, timeout: 35000, healthTest: true });
|
|
41
|
+
let modelFallback = false;
|
|
42
|
+
if (res.code !== 0 && options.model && /model.*(?:not found|not available|not supported|access|invalid|unknown)|unknown.*model/i.test(res.stderr + res.stdout)) {
|
|
43
|
+
modelFallback = true;
|
|
44
|
+
res = await this.runPrompt('Sadece tek kelime OK yaz', { ...options, model: undefined, thinking: undefined, timeout: 35000, healthTest: true });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const durationSec = (res.durationMs / 1000).toFixed(2);
|
|
48
|
+
const combinedOutput = (res.stdout + '\n' + res.stderr).trim();
|
|
49
|
+
const containsOk = /^OK[.!]?$/i.test(res.stdout.trim());
|
|
50
|
+
|
|
51
|
+
let status = 'error';
|
|
52
|
+
let message = '';
|
|
53
|
+
let solution = null;
|
|
54
|
+
|
|
55
|
+
if (res.code === 0 && containsOk) {
|
|
56
|
+
status = 'healthy';
|
|
57
|
+
if (!authInfo.loggedIn) {
|
|
58
|
+
authInfo.loggedIn = true;
|
|
59
|
+
authInfo.details = 'Oturum aΓ§Δ±k';
|
|
60
|
+
}
|
|
61
|
+
message = `BaΕarΔ±lΔ± ("OK" yanΔ±tΔ± alΔ±ndΔ±, ${durationSec}s)`;
|
|
62
|
+
onLog(`[${this.id.toUpperCase()}] β BaΕarΔ±lΔ±! YanΔ±t: "${res.stdout.split('\n')[0].trim()}" (${durationSec}s)`);
|
|
63
|
+
} else if (res.code === -999) {
|
|
64
|
+
status = 'timeout';
|
|
65
|
+
message = 'Zaman aΕΔ±mΔ± (CLI yanΔ±t vermedi)';
|
|
66
|
+
onLog(`[${this.id.toUpperCase()}] β Zaman aΕΔ±mΔ± gerΓ§ekleΕti.`);
|
|
67
|
+
} else if (
|
|
68
|
+
combinedOutput.includes('Auth method') ||
|
|
69
|
+
combinedOutput.includes('GEMINI_API_KEY') ||
|
|
70
|
+
combinedOutput.includes('auth.json') ||
|
|
71
|
+
combinedOutput.includes('login')
|
|
72
|
+
) {
|
|
73
|
+
status = 'auth_required';
|
|
74
|
+
message = 'GiriΕ yapΔ±lmamΔ±Ε (Oturum aΓ§Δ±lmasΔ± gerekiyor)';
|
|
75
|
+
solution = this.id === 'gemini'
|
|
76
|
+
? 'Terminalde bir kez `agy` komutunu Γ§alΔ±ΕtΔ±rarak Google hesabΔ±nΔ±zla giriΕ yapΔ±nΔ±z.'
|
|
77
|
+
: 'CLI ΓΌzerinde oturum aΓ§Δ±nΔ±z.';
|
|
78
|
+
onLog(`[${this.id.toUpperCase()}] ! GiriΕ gerekli: ${message}`);
|
|
79
|
+
} else if (res.code === -1 && res.stderr.includes('spawn')) {
|
|
80
|
+
status = 'missing';
|
|
81
|
+
message = 'CLI kurulu deΔil veya PATH ΓΌzerinde bulunamadΔ±';
|
|
82
|
+
solution = this.id === 'gemini'
|
|
83
|
+
? 'Antigravity CLI (agy) kurulumunu yapΔ±nΔ±z.'
|
|
84
|
+
: 'Δ°lgili CLI aracΔ±nΔ± kurunuz.';
|
|
85
|
+
onLog(`[${this.id.toUpperCase()}] β CLI kurulu deΔil.`);
|
|
86
|
+
} else {
|
|
87
|
+
status = 'failed';
|
|
88
|
+
message = `Hata kodu ${res.code}: ${res.stderr || res.stdout || 'Bilinmeyen hata'}`;
|
|
89
|
+
onLog(`[${this.id.toUpperCase()}] β Hata oluΕtu: ${message}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
id: this.id,
|
|
94
|
+
name: this.name,
|
|
95
|
+
version,
|
|
96
|
+
modelFallback,
|
|
97
|
+
authInfo,
|
|
98
|
+
status,
|
|
99
|
+
healthy: status === 'healthy',
|
|
100
|
+
message,
|
|
101
|
+
solution,
|
|
102
|
+
durationMs: res.durationMs,
|
|
103
|
+
rawStdout: res.stdout,
|
|
104
|
+
rawStderr: res.stderr,
|
|
105
|
+
timestamp: new Date().toISOString()
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async runPrompt(prompt, options = {}) {
|
|
110
|
+
return this.execute(this.getRunArgs(prompt, options), prompt + '\n', options);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
execute(args, input = '', options = {}) {
|
|
114
|
+
return runWithStdin(this.command, args, input, options);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = BaseAgent;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const BaseAgent = require('./base-agent');
|
|
2
|
+
|
|
3
|
+
class ClaudeAgent extends BaseAgent {
|
|
4
|
+
constructor() {
|
|
5
|
+
super({
|
|
6
|
+
id: 'claude',
|
|
7
|
+
name: 'Claude Code',
|
|
8
|
+
provider: 'Anthropic',
|
|
9
|
+
role: 'KoordinatΓΆr & PlanlayΔ±cΔ±',
|
|
10
|
+
command: 'claude'
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getRunArgs(prompt, options = {}) {
|
|
15
|
+
const args = ['-p', '--output-format', options.healthTest ? 'text' : 'json'];
|
|
16
|
+
if (options.model) args.push('--model', options.model);
|
|
17
|
+
if (options.thinking) args.push('--effort', options.thinking);
|
|
18
|
+
if (options.resumeId && this.capabilities?.resume) args.push('--resume', options.resumeId);
|
|
19
|
+
if (options.noTools && this.capabilities?.noTools) args.push('--tools', '');
|
|
20
|
+
return args;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async runPrompt(prompt, options = {}) {
|
|
24
|
+
const res = await super.runPrompt(prompt, options);
|
|
25
|
+
if (!options.healthTest && res.code === 0) {
|
|
26
|
+
try {
|
|
27
|
+
const data = JSON.parse(res.stdout);
|
|
28
|
+
res.stdout = data.result || '';
|
|
29
|
+
res.sessionId = data.session_id;
|
|
30
|
+
if (data.is_error) { res.code = 1; res.stderr = res.stdout; }
|
|
31
|
+
} catch { res.code = 1; res.stderr = 'Invalid Claude JSON response: ' + res.stdout; }
|
|
32
|
+
}
|
|
33
|
+
return res;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async checkAuth() {
|
|
37
|
+
try {
|
|
38
|
+
const res = await this.execute(['auth', 'status'], '', { timeout: 10000 });
|
|
39
|
+
const parsed = JSON.parse(res.stdout.trim());
|
|
40
|
+
return {
|
|
41
|
+
loggedIn: !!parsed.loggedIn,
|
|
42
|
+
subscription: parsed.subscriptionType || 'Pro',
|
|
43
|
+
email: parsed.email || null,
|
|
44
|
+
details: parsed.subscriptionType ? `${parsed.subscriptionType.toUpperCase()} Abonelik` : 'GiriΕ YapΔ±ldΔ±'
|
|
45
|
+
};
|
|
46
|
+
} catch {
|
|
47
|
+
return { loggedIn: false };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = new ClaudeAgent();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const BaseAgent = require('./base-agent');
|
|
2
|
+
const { policyPattern } = require('../task-outcome');
|
|
3
|
+
|
|
4
|
+
class CodexAgent extends BaseAgent {
|
|
5
|
+
constructor() {
|
|
6
|
+
super({
|
|
7
|
+
id: 'codex',
|
|
8
|
+
name: 'ChatGPT / Codex',
|
|
9
|
+
provider: 'OpenAI',
|
|
10
|
+
role: 'Kodlama ve GΓΆrev YΓΌrΓΌtme',
|
|
11
|
+
command: 'codex'
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getRunArgs(prompt, options = {}) {
|
|
16
|
+
const args = ['exec'];
|
|
17
|
+
if (options.resumeId && this.capabilities?.resume) args.push('resume', options.resumeId);
|
|
18
|
+
args.push('--ignore-user-config', '--skip-git-repo-check');
|
|
19
|
+
if (!options.healthTest) args.push('--json');
|
|
20
|
+
if (options.model) args.push('--model', options.model);
|
|
21
|
+
if (options.thinking) args.push('-c', `model_reasoning_effort=${options.thinking}`);
|
|
22
|
+
args.push('-');
|
|
23
|
+
return args;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async runPrompt(prompt, options = {}) {
|
|
27
|
+
const res = await super.runPrompt(prompt, options);
|
|
28
|
+
if (!options.healthTest) {
|
|
29
|
+
const messages = [];
|
|
30
|
+
res.toolFailures = [];
|
|
31
|
+
res.successfulFileCommands = 0;
|
|
32
|
+
res.toolChecksAvailable = true;
|
|
33
|
+
for (const line of res.stdout.split(/\r?\n/)) {
|
|
34
|
+
try {
|
|
35
|
+
const event = JSON.parse(line);
|
|
36
|
+
if (event.type === 'thread.started') res.sessionId = event.thread_id;
|
|
37
|
+
if (event.type === 'item.completed' && event.item?.type === 'agent_message') messages.push(event.item.text);
|
|
38
|
+
if (event.type === 'item.completed' && event.item?.type === 'command_execution') {
|
|
39
|
+
const item = event.item, output = item.aggregated_output || item.output || '';
|
|
40
|
+
if (typeof item.exit_code === 'number' && item.exit_code !== 0 || item.status === 'failed' || policyPattern.test(output)) res.toolFailures.push({ command: item.command, output, exitCode: item.exit_code, policyBlocked: policyPattern.test(output) });
|
|
41
|
+
else if ((item.exit_code === 0 || item.status === 'completed') && /rg|Get-Content|Get-ChildItem|\bcat\b|\bls\b|read.*file/i.test(item.command || '')) res.successfulFileCommands++;
|
|
42
|
+
}
|
|
43
|
+
if (event.type === 'turn.failed' || event.type === 'error') { res.code = res.code || 1; res.stderr += '\n' + (event.error?.message || event.message || line); }
|
|
44
|
+
} catch { /* Ignore non-JSON diagnostics; never treat tool output as final text. */ }
|
|
45
|
+
}
|
|
46
|
+
res.stdout = messages.at(-1) || '';
|
|
47
|
+
}
|
|
48
|
+
return res;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async checkAuth() {
|
|
52
|
+
try {
|
|
53
|
+
const res = await this.execute(['login', 'status'], '', { timeout: 10000 });
|
|
54
|
+
const text = (res.stdout + '\n' + res.stderr).trim();
|
|
55
|
+
const loggedIn = text.toLowerCase().includes('logged in');
|
|
56
|
+
return {
|
|
57
|
+
loggedIn,
|
|
58
|
+
details: text || 'ChatGPT Oturumu'
|
|
59
|
+
};
|
|
60
|
+
} catch {
|
|
61
|
+
return { loggedIn: false };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = new CodexAgent();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const BaseAgent = require('./base-agent');
|
|
2
|
+
|
|
3
|
+
class GeminiAgent extends BaseAgent {
|
|
4
|
+
constructor() {
|
|
5
|
+
super({
|
|
6
|
+
id: 'gemini',
|
|
7
|
+
name: 'Google Gemini',
|
|
8
|
+
provider: 'Google',
|
|
9
|
+
role: 'HΔ±zlΔ± YanΔ±t & AraΕtΔ±rma',
|
|
10
|
+
command: 'agy',
|
|
11
|
+
enabled: false,
|
|
12
|
+
warning: "Google, Antigravity CLI'Δ±n otomatik kullanΔ±mΔ±na karΕΔ± bazΔ± hesaplarΔ± kΔ±sΔ±tladΔ±ΔΔ± rapor edildi. Bu ajanΔ± aΓ§arak Google hesabΔ±nla ilgili riski kabul etmiΕ olursun."
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getRunArgs(prompt = 'Sadece tek kelime OK yaz', options = {}) {
|
|
17
|
+
if (typeof prompt === 'object' && prompt !== null) {
|
|
18
|
+
options = prompt;
|
|
19
|
+
prompt = 'Sadece tek kelime OK yaz';
|
|
20
|
+
}
|
|
21
|
+
// No prompt argument and no unverified stdin/resume flags.
|
|
22
|
+
if (this.capabilities && !this.capabilities.available) throw new Error(this.capabilities.error || 'agy CLI not found.');
|
|
23
|
+
if (!this.capabilities?.stdin) throw new Error('agy stdin support could not be verified from --help.');
|
|
24
|
+
const args = this.capabilities.print ? ['-p'] : [];
|
|
25
|
+
if (options && options.model) {
|
|
26
|
+
if (!this.capabilities.model) throw new Error('Model not supported by agy --help.');
|
|
27
|
+
args.push('--model', options.model);
|
|
28
|
+
}
|
|
29
|
+
if (options && options.thinking && this.capabilities.effort) {
|
|
30
|
+
args.push('--effort', options.thinking);
|
|
31
|
+
}
|
|
32
|
+
return args;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async checkAuth() {
|
|
36
|
+
// Antigravity CLI does not have a separate auth status command.
|
|
37
|
+
// Auth status is validated by executing headless prompts.
|
|
38
|
+
return { loggedIn: false };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = new GeminiAgent();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const claude = require('./claude');
|
|
2
|
+
const codex = require('./codex');
|
|
3
|
+
const gemini = require('./gemini');
|
|
4
|
+
|
|
5
|
+
const agents = {
|
|
6
|
+
claude,
|
|
7
|
+
codex,
|
|
8
|
+
gemini
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function getAgent(id) {
|
|
12
|
+
const key = id.toLowerCase();
|
|
13
|
+
return agents[key] || null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getAllAgents() {
|
|
17
|
+
return Object.values(agents);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
agents,
|
|
22
|
+
getAgent,
|
|
23
|
+
getAllAgents
|
|
24
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// At most two read-only help calls per CLI, cached for this application run.
|
|
2
|
+
async function discover(agent) {
|
|
3
|
+
if (agent.capabilities) return agent.capabilities;
|
|
4
|
+
const args = agent.id === 'codex' ? ['exec', '--help'] : ['--help'];
|
|
5
|
+
const res = await agent.execute(args, '', { timeout: 10000 });
|
|
6
|
+
const help = res.code === 0 ? res.stdout : '';
|
|
7
|
+
const capabilities = { resume: false, stdin: agent.id !== 'gemini', source: `${agent.command} ${args.join(' ')}`, available: res.code === 0, error: res.code === 0 ? null : (res.stderr || 'CLI help unavailable') };
|
|
8
|
+
if (agent.id === 'claude') {
|
|
9
|
+
capabilities.resume = /--resume\s/.test(help) && /--output-format/.test(help);
|
|
10
|
+
capabilities.noTools = /--tools\s/.test(help);
|
|
11
|
+
} else if (agent.id === 'codex' && /\bresume\s/.test(help)) {
|
|
12
|
+
const resume = await agent.execute(['exec', 'resume', '--help'], '', { timeout: 10000 });
|
|
13
|
+
capabilities.resume = resume.code === 0 && /SESSION_ID/.test(resume.stdout) && /stdin/.test(resume.stdout);
|
|
14
|
+
} else if (agent.id === 'gemini') {
|
|
15
|
+
capabilities.stdin = /stdin|standard input/i.test(help);
|
|
16
|
+
capabilities.print = /-p,?\s+--print/.test(help);
|
|
17
|
+
capabilities.effort = /--effort\s/.test(help);
|
|
18
|
+
capabilities.model = /--model\s/.test(help);
|
|
19
|
+
}
|
|
20
|
+
agent.capabilities = capabilities;
|
|
21
|
+
return capabilities;
|
|
22
|
+
}
|
|
23
|
+
module.exports = { discover };
|