Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<manifest package="com.quantumaiide.agent"
xmlns:android="http://schemas.android.com/apk/res/android">
<application android:label="QuantumAIIDE">
<activity android:name=".QuantumAIIDEWebViewActivity"
android:exported="true"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Comment thread
spiralgang marked this conversation as resolved.
21 changes: 21 additions & 0 deletions QuantumAIIDEWebViewActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.quantumaiide.agent

import android.annotation.SuppressLint
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity

class QuantumAIIDEWebViewActivity : AppCompatActivity() {
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val webView = WebView(this)
setContentView(webView)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.settings.allowFileAccess = true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Enabling allowFileAccess is a security risk. If the loaded web application has a Cross-Site Scripting (XSS) vulnerability, an attacker could exploit it to read arbitrary local files from the device's filesystem using file:/// URLs. Given that you are loading local assets, this might be intentional, but it significantly widens the attack surface. Please consider if this is strictly necessary and disable it if possible.

webView.webViewClient = WebViewClient()
webView.loadUrl("file:///android_asset/QuantumAIIDE_Monolith.html")
}
}
352 changes: 352 additions & 0 deletions QuantumAIIDE_Monolith.html

Large diffs are not rendered by default.

358 changes: 358 additions & 0 deletions QuantumWebIDE.html

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""
QuantumAIIDE Agent: Mobile-First, Privileged AI System Builder
Industry standards, agentic orchestration, Android/Aarch64 ready.
References: /reference vault, ai_dev_system.py, OWASP, GitHub API docs, DeepSeek, HuggingFace
"""
import asyncio, json, os, aiohttp, logging
from github import Github
from shell import run_shell_command
from ai import query_ai
from audit import log_action

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
logger = logging.getLogger("QuantumAIIDE-Agent")

class QuantumAgent:
def __init__(self, github_token, ai_token):
self.github = Github(github_token)
self.ai_token = ai_token

async def handle_request(self, req):
"""Dispatch agentic requests: build, refactor, run, push, etc."""
action = req.get("action")
params = req.get("params", {})
log_action(action, params)
if action == "refactor_file":
repo = self.github.get_repo(params["repo"])
file = repo.get_contents(params["path"])
code = file.decoded_content.decode()
instructions = params["instructions"]
prompt = f"Refactor this code for {params['path']}:\n{code}\nInstructions: {instructions}\nOutput only final code."
new_code = await query_ai(prompt, self.ai_token)
repo.update_file(params["path"], "Agentic AI refactor", new_code, file.sha)
return {"status": "ok", "msg": "File refactored"}
elif action == "generate_file":
repo = self.github.get_repo(params["repo"])
prompt = f"Generate code for {params['path']}:\n{params['description']}\nOutput only final code."
code = await query_ai(prompt, self.ai_token)
repo.create_file(params["path"], "Agentic AI generate", code)
return {"status": "ok", "msg": "File generated"}
elif action == "run_shell":
result = run_shell_command(params["command"])
return {"status": "ok", "output": result}
Comment thread
spiralgang marked this conversation as resolved.
# Extend for more agentic ops: build, deploy, proxy, etc.
else:
return {"status": "error", "msg": "Unknown action"}

# FastAPI or Flask app to expose agent endpoints (omitted for brevity)
# See /reference vault, ai_dev_system.py for full server reference
27 changes: 27 additions & 0 deletions ai-chat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { githubWrite, shellRun, auditLog } from './integrations.js';

export function initAIChat(container) {
container.innerHTML = `
<div class="chatbox" id="chatbox"></div>
<input id="chat-input" class="chat-input" placeholder="Ask anything (build, refactor, deploy)">
<button class="btn" id="chat-send-btn">Send</button>
`;
document.getElementById('chat-send-btn').onclick = async () => {
let val = document.getElementById('chat-input').value.trim();
if (!val) return;
addChatMsg('You', val);
// Call DeepSeek/HF API
let result = await aiExecute(val);
addChatMsg('AI', result.message);
// If result includes action (code, command), execute directly
if (result.action === 'write_file') {
await githubWrite(result.file, result.content);
auditLog('write_file', result.file);
Comment on lines +17 to +19
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): No error handling for failed githubWrite or auditLog operations.

Please add error handling and user notifications for failures in githubWrite and auditLog to prevent UI inconsistencies.

}
if (result.action === 'run_shell') {
let shellResult = await shellRun(result.command);
auditLog('run_shell', result.command, shellResult);
addChatMsg('Shell', shellResult.output);
}
};
}
51 changes: 51 additions & 0 deletions ai.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { files } from './files.js';

let aiKey = "";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: aiKey is declared as a local variable, which may limit cross-module updates.

Consider implementing a setter/getter or shared state management if cross-module access or updates to aiKey are required.


export function initAIChat(container) {
container.innerHTML = `
<div class="chatbox" id="chatbox"></div>
<div class="flex">
<input id="chat-input" class="chat-input" placeholder="Ask anything...">
<button class="btn" id="chat-send-btn">Send</button>
</div>
`;
document.getElementById('chat-send-btn').onclick = () => {
let val = document.getElementById('chat-input').value.trim();
if (!val) return;
runAIChat(val);
document.getElementById('chat-input').value = '';
};
}

export async function runAIChat(prompt, source="chat", cb) {
addChatMsg(source==="cli"?"CLI":"You",prompt);
let repoContext = Object.keys(files).map(f=>`${f}:\n${files[f].slice(0,300)}`).join('\n\n');
let userMsg = `QuantumWebIDE, Mobile-First, Android 10+/Aarch64. Files:\n${repoContext}\n\n${prompt}`;
let answer = await queryAI([{role:"user",content:userMsg}]);
addChatMsg("AI",answer);
if(cb) cb(answer);
}

function addChatMsg(user, text) {
let chatbox = document.getElementById('chatbox');
let div = document.createElement('div');
div.className = 'chat-msg';
div.innerHTML = `<span class="${user==='AI'?'chat-ai':'chat-user'}">${user}:</span> <span class="chat-text">${text}</span>`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-document-method): User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-innerhtml): User controlled data in a div.innerHTML is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep

chatbox.appendChild(div);
chatbox.scrollTop = chatbox.scrollHeight;
}
Comment thread
spiralgang marked this conversation as resolved.

async function queryAI(messages, model="deepseek-ai/DeepSeek-R1:fireworks-ai") {
if(!aiKey) return "AI key not set. Paste in settings.";
try {
const response = await fetch("https://router.huggingface.co/v1/chat/completions",{
headers: { Authorization: "Bearer "+aiKey, "Content-Type":"application/json"},
method: "POST",
body: JSON.stringify({messages,top_p:1,model}),
});
const result = await response.json();
if(result.choices && result.choices.length>0) return result.choices[0].message.content;
return result.error||"AI error: Unexpected response";
} catch(e){ return "AI error: "+e.message;}
}
13 changes: 13 additions & 0 deletions ai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import aiohttp, os
async def query_ai(prompt, ai_token, model="deepseek-ai/DeepSeek-R1:fireworks-ai"):
url = "https://router.huggingface.co/v1/chat/completions"
payload = {
"messages": [{"role": "user", "content": prompt}],
"top_p": 1,
"model": model
}
headers = {"Authorization": f"Bearer {ai_token}", "Content-Type": "application/json"}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, headers=headers) as resp:
res = await resp.json()
return res["choices"][0]["message"]["content"] if "choices" in res else res.get("error", "")
Comment thread
spiralgang marked this conversation as resolved.
5 changes: 5 additions & 0 deletions audit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import time, json
def log_action(action, params):
entry = {"ts": time.time(), "action": action, "params": params}
with open("agent_audit.log", "a") as f:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): No error handling for file write operations in log_action.

Wrap the file write in a try/except block to prevent agent crashes from disk errors.

f.write(json.dumps(entry) + "\n")
22 changes: 22 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 33
defaultConfig {
applicationId "com.quantumaiide.agent"
minSdkVersion 29
targetSdkVersion 33
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
Comment thread
spiralgang marked this conversation as resolved.
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
}
2 changes: 1 addition & 1 deletion build_apk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ cd QuantumAIIDE
echo "==> APK BUILD COMPLETE"
ls -l app/build/outputs/apk/release/

# Optionally upload artifact (if running on a CI runner with artifact upload support)
# Optionally upload artifact (if running on a CI runner with artifact upload support)
18 changes: 18 additions & 0 deletions editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { files, openFile, saveFile, deleteFile, downloadFile } from './files.js';

export function initEditor(container) {
container.innerHTML = `
<textarea id="editor" style="width:100%;height:180px;"></textarea>
<div>
<button id="save-file-btn" class="btn">Save</button>
<button id="new-file-btn" class="btn">New File</button>
<button id="delete-file-btn" class="btn">Delete File</button>
<button id="download-file-btn" class="btn">Download File</button>
</div>
`;
document.getElementById('editor').value = files[openFile()] || '';
document.getElementById('save-file-btn').onclick = () => saveFile(document.getElementById('editor').value);
document.getElementById('new-file-btn').onclick = () => openFile(prompt("New file name:"));
document.getElementById('delete-file-btn').onclick = () => deleteFile();
document.getElementById('download-file-btn').onclick = () => downloadFile();
}
18 changes: 18 additions & 0 deletions files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export let files = {
"README.md": "# QuantumWebIDE\nMobile-first, privileged shell.",
"main.sh": "#!/bin/bash\necho 'Ready.'"
};
let activeFile = "README.md";

export function openFile(fname) {
if (fname && files[fname]) activeFile = fname;
return activeFile;
}
export function saveFile(content) { files[activeFile] = content; }
export function deleteFile() { delete files[activeFile]; activeFile = Object.keys(files)[0];}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: deleteFile does not handle case when no files remain after deletion.

If files is empty after deletion, activeFile will be set to undefined, which may cause errors. Add a check to handle this scenario.

export function downloadFile() {
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([files[activeFile]], {type: "text/plain"}));
a.download = activeFile;
a.click();
}
22 changes: 22 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>QuantumAIIDE Mobile Agentic IDE</title>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<link rel="stylesheet" href="theme.css">
<script src="main.js" type="module"></script>
</head>
<body>
<header><h1>QuantumAIIDE</h1><span id="status"></span></header>
<nav>
<button id="nav-terminal">Terminal</button>
<button id="nav-editor">Editor</button>
<button id="nav-ai">AI Chat</button>
<button id="nav-proxy">Proxy</button>
<button id="nav-settings">Settings</button>
</nav>
<main id="main-panel"></main>
</body>
<!-- References: /reference vault, OWASP, Android10+ UI, DeepSeek API -->
</html>
Comment on lines +1 to +22
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This HTML file serves as the entry point for the modular version of the IDE, but it's missing the necessary <script> tags for third-party dependencies like xterm.js that are used by the JavaScript modules (e.g., terminal.js). This will lead to runtime errors, and the application will not function. These dependencies need to be included.

22 changes: 22 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { initTerminal } from './terminal.js'
import { initEditor } from './editor.js'
import { initAIChat } from './ai-chat.js'
import { initProxy } from './proxy.js'
import { initSettings } from './settings.js'

const panels = {
terminal: initTerminal,
editor: initEditor,
ai: initAIChat,
proxy: initProxy,
settings: initSettings
}
function showPanel(panel) {
const main = document.getElementById('main-panel')
main.innerHTML = ''
panels[panel](main)
}
['terminal','editor','ai','proxy','settings'].forEach(p=>{
document.getElementById('nav-'+p).onclick=()=>showPanel(p)
})
showPanel('terminal')
26 changes: 26 additions & 0 deletions proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export function initProxy(container) {
container.innerHTML = `
<div class="output" id="proxy-output">Proxy system ready.</div>
<input id="proxy-url" class="chat-input" placeholder="URL">
<input id="proxy-method" class="chat-input" style="width:90px;" value="GET">
<textarea id="proxy-headers" class="chat-input" style="height:40px;" placeholder='{"Authorization":"Bearer ..."}'></textarea>
<textarea id="proxy-body" class="chat-input" style="height:40px;" placeholder="Body (JSON)"></textarea>
<button class="btn" id="proxy-send-btn">Send Proxy Request</button>
`;
document.getElementById('proxy-send-btn').onclick = async () => {
const url = document.getElementById('proxy-url').value.trim();
const method = document.getElementById('proxy-method').value.trim();
let headers = {};
try { headers = JSON.parse(document.getElementById('proxy-headers').value || "{}"); } catch(e){}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Silent catch on JSON.parse may hide header parsing errors.

Invalid JSON in headers is ignored, resulting in empty headers. Please add error handling to inform users of parsing failures.

Comment thread
spiralgang marked this conversation as resolved.
let body = document.getElementById('proxy-body').value.trim();
let options = { method, headers };
if (body) options.body = body;
try {
let res = await fetch(url, options);
let text = await res.text();
document.getElementById('proxy-output').textContent = text.slice(0,2000);
} catch(e) {
document.getElementById('proxy-output').textContent = "Proxy error: "+e.message;
}
};
}
19 changes: 19 additions & 0 deletions settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { aiKey } from './ai.js';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Direct assignment to imported 'aiKey' may not update state as expected.

Direct assignment may not synchronize changes across modules. Use a setter or event-based method to ensure updates are properly propagated.


export function loadSettings(container) {
container.innerHTML = `
<h3>System Settings</h3>
<label>AI API Key: <input id="settings-ai-key" class="chat-input" placeholder="Paste DeepSeek/HuggingFace token"></label>
<label>Theme: <select id="settings-theme" class="chat-input">
<option value="dark">Dark</option>
<option value="light">Light</option>
</select></label>
`;
document.getElementById('settings-ai-key').onchange = e => {
aiKey = e.target.value.trim();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

You are attempting to reassign the aiKey variable, which is an imported binding from another module. In ES modules, imported bindings are read-only. This line will throw a TypeError at runtime, preventing the AI key from being set. To fix this, you should export a setter function from ai.js (e.g., setAiKey) and call that function here.

Suggested change
aiKey = e.target.value.trim();
// This will throw a TypeError. Instead, call a setter function exported from ai.js
// e.g., setAiKey(e.target.value.trim());
aiKey = e.target.value.trim();

};
document.getElementById('settings-theme').onchange = e => {
document.body.style.background = e.target.value === "light" ? "#f9fafb" : "#111827";
document.body.style.color = e.target.value === "light" ? "#181f2a" : "#e5e7eb";
};
}
7 changes: 7 additions & 0 deletions shell.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import subprocess
def run_shell_command(cmd):
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=120)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Use of shell=True may introduce security risks if input is not sanitized.

If cmd comes from user input or external sources, this can enable shell injection. Prefer shell=False with argument lists, or ensure thorough sanitization.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.subprocess-shell-true): Found 'subprocess' function 'run' with 'shell=True'. This is dangerous because this call will spawn the command using a shell process. Doing so propagates current shell settings and variables, which makes it much easier for a malicious actor to execute commands. Use 'shell=False' instead.

Suggested change
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=120)
result = subprocess.run(cmd, shell=False, capture_output=True, text=True, timeout=120)

Source: opengrep

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Using shell=True with subprocess.run is extremely dangerous when the command string can be influenced by external input, as it is in agent.py. This is a classic shell injection vulnerability. An attacker could provide a command like ; rm -rf / to execute arbitrary, malicious commands. You should always avoid shell=True and instead pass command arguments as a list to subprocess.run.

return result.stdout if result.returncode == 0 else result.stderr
except Exception as e:
return f"Shell error: {e}"
Loading
Loading