v2.0.1: APK verification, stay awake fix, configurable auto-fix retries
This commit is contained in:
@@ -256,6 +256,11 @@
|
||||
</label>
|
||||
</div>
|
||||
<span class="input-hint">Prevents screen sleep while agent is working</span>
|
||||
<div class="input-group" style="margin-top:12px">
|
||||
<label>Max Auto-Fix Retries: <span id="retries-value">10</span></label>
|
||||
<input type="range" id="settings-maxretries" min="1" max="30" step="1" value="10">
|
||||
</div>
|
||||
<span class="input-hint">How many times AI will auto-retry after build failures</span>
|
||||
</div>
|
||||
<div class="settings-section">
|
||||
<h3>Appearance</h3>
|
||||
@@ -274,13 +279,22 @@
|
||||
</div>
|
||||
<div class="settings-section">
|
||||
<h3>About</h3>
|
||||
<p class="about-text">Z.AI Chat v2.0.0</p>
|
||||
<p class="about-text">Z.AI Chat v2.0.1</p>
|
||||
<p class="about-text">Built with Z.AI SDK & GLM-5.1</p>
|
||||
<p class="about-text">Compatible with Android 15/16</p>
|
||||
</div>
|
||||
<div class="settings-section">
|
||||
<h3>Changelog</h3>
|
||||
<ul class="changelog-list">
|
||||
<li>
|
||||
<span class="changelog-version">v2.0.1</span>
|
||||
<span class="changelog-date">2026-05-19</span>
|
||||
<ul>
|
||||
<li><strong>APK Verification</strong> — build output now verified: confirms APK file exists and shows size</li>
|
||||
<li><strong>Stay Awake Fix</strong> — dual wake locks (screen bright + CPU) keep device fully awake during builds</li>
|
||||
<li><strong>Configurable Retries</strong> — max auto-fix retries now adjustable (1–30) in Settings, default 10</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<span class="changelog-version">v2.0.0</span>
|
||||
<span class="changelog-date">2026-05-19</span>
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
streamingResponseDiv: null,
|
||||
terminalOpen: false,
|
||||
keepAwake: false,
|
||||
autoDeploy: true
|
||||
autoDeploy: true,
|
||||
maxRetries: 10
|
||||
};
|
||||
|
||||
function $(sel) { return document.querySelector(sel); }
|
||||
@@ -50,6 +51,7 @@
|
||||
state.terminalOpen = localStorage.getItem(STORAGE_KEY + 'terminalOpen') === 'true';
|
||||
state.keepAwake = localStorage.getItem(STORAGE_KEY + 'keepAwake') === 'true';
|
||||
state.autoDeploy = localStorage.getItem(STORAGE_KEY + 'autoDeploy') !== 'false';
|
||||
state.maxRetries = parseInt(localStorage.getItem(STORAGE_KEY + 'maxRetries')) || 10;
|
||||
var convData = localStorage.getItem(STORAGE_KEY + 'conversations');
|
||||
state.conversations = convData ? JSON.parse(convData) : [];
|
||||
state.activeConversationId = localStorage.getItem(STORAGE_KEY + 'activeConv') || null;
|
||||
@@ -70,6 +72,7 @@
|
||||
localStorage.setItem(STORAGE_KEY + 'terminalOpen', state.terminalOpen.toString());
|
||||
localStorage.setItem(STORAGE_KEY + 'keepAwake', state.keepAwake.toString());
|
||||
localStorage.setItem(STORAGE_KEY + 'autoDeploy', state.autoDeploy.toString());
|
||||
localStorage.setItem(STORAGE_KEY + 'maxRetries', state.maxRetries.toString());
|
||||
localStorage.setItem(STORAGE_KEY + 'conversations', JSON.stringify(state.conversations));
|
||||
localStorage.setItem(STORAGE_KEY + 'activeConv', state.activeConversationId || '');
|
||||
} catch(e) { console.error('Save state error:', e); }
|
||||
@@ -1415,7 +1418,10 @@
|
||||
}
|
||||
|
||||
var _agenticRetryCount = 0;
|
||||
var MAX_AGENTIC_RETRIES = 3;
|
||||
|
||||
function getMaxRetries() {
|
||||
return state.maxRetries || 10;
|
||||
}
|
||||
|
||||
async function autoExecuteActions(actions, conv) {
|
||||
var hasFiles = actions.some(function(a) { return a.type === 'create_file'; });
|
||||
@@ -1506,8 +1512,8 @@
|
||||
'AAPT2=$(which aapt2 2>/dev/null) && ' +
|
||||
'D8=$(which d8 2>/dev/null) && ' +
|
||||
'ECJ=$(which ecj 2>/dev/null) && ' +
|
||||
'if [ -z "$AAPT2" ]; then echo "[BUILD FAILED] aapt2 not found. Install Termux: pkg install aapt2"; exit 1; fi && ' +
|
||||
'if [ -z "$ECJ" ]; then echo "[BUILD FAILED] ecj not found. Install Termux: pkg install ecj"; exit 1; fi && ' +
|
||||
'if [ -z "$AAPT2" ]; then echo "[BUILD FAILED] aapt2 not found. Install via: pkg install aapt2"; exit 1; fi && ' +
|
||||
'if [ -z "$ECJ" ]; then echo "[BUILD FAILED] ecj not found. Install via: pkg install ecj"; exit 1; fi && ' +
|
||||
'mkdir -p build/gen build/classes && ' +
|
||||
'echo "[*] Compiling resources..." && ' +
|
||||
'$AAPT2 compile --dir app/src/main/res -o build/compiled_resources.zip 2>&1 && ' +
|
||||
@@ -1527,7 +1533,7 @@
|
||||
'else ' +
|
||||
' DX=$(which dx 2>/dev/null) && ' +
|
||||
' if [ -n "$DX" ]; then $DX --output build/classes.dex build/classes/ 2>&1; ' +
|
||||
' else echo "[BUILD FAILED] d8/dx not found. Install Termux: pkg install dx"; exit 1; fi; ' +
|
||||
' else echo "[BUILD FAILED] d8/dx not found. Install via: pkg install dx"; exit 1; fi; ' +
|
||||
'fi && ' +
|
||||
'echo "[*] Packaging..." && ' +
|
||||
'cd build && ' +
|
||||
@@ -1546,37 +1552,41 @@
|
||||
'fi && ' +
|
||||
'APK_PATH="' + projectDir + '/build/app-signed.apk" && ' +
|
||||
'APK_SIZE=$(du -h app-signed.apk 2>/dev/null | cut -f1) && ' +
|
||||
'echo "[BUILD OK] APK: $APK_PATH ($APK_SIZE)" && ' +
|
||||
'echo $APK_PATH';
|
||||
'echo "[BUILD OK] APK: $APK_PATH ($APK_SIZE)"';
|
||||
|
||||
var result = await shellExec(buildScript, termState.homeDir, false);
|
||||
var output = result.output || '';
|
||||
termPrint(output.replace(/\n$/, ''), result.exitCode === 0 ? '' : 'err');
|
||||
|
||||
if (output.indexOf('[BUILD OK]') >= 0) {
|
||||
var apkMatch = output.match(/\[BUILD OK\] APK: ([^\s]+)/);
|
||||
if (apkMatch) {
|
||||
termPrint('\nAPK ready: ' + apkMatch[1], 'success');
|
||||
termPrint('Run: install ' + apkMatch[1], 'info');
|
||||
var apkPath = projectDir + '/build/app-signed.apk';
|
||||
var verifyResult = await shellExec('ls -la ' + apkPath + ' 2>&1', termState.homeDir, false);
|
||||
if (verifyResult.output && verifyResult.output.indexOf('No such file') < 0) {
|
||||
var sizeMatch = verifyResult.output.match(/(\d+)\s+/);
|
||||
var sizeInfo = sizeMatch ? ' (' + Math.round(parseInt(sizeMatch[1]) / 1024) + ' KB)' : '';
|
||||
termPrint('\n[VERIFIED] APK built successfully: ' + apkPath + sizeInfo, 'success');
|
||||
return '[BUILD OK] ' + apkPath;
|
||||
} else {
|
||||
termPrint('\n[VERIFY FAILED] Build claimed success but APK not found at ' + apkPath, 'err');
|
||||
return '[BUILD FAILED] APK file not found after build. Output:\n' + output.substring(0, 1000);
|
||||
}
|
||||
return '[BUILD OK] ' + output.substring(output.indexOf('[BUILD OK]'));
|
||||
} else if (output.indexOf('[BUILD FAILED]') >= 0) {
|
||||
return '[BUILD FAILED] ' + output;
|
||||
} else if (result.exitCode !== 0) {
|
||||
return '[BUILD FAILED] exit=' + result.exitCode + '\n' + output.substring(0, 1000);
|
||||
return '[BUILD FAILED] exit=' + result.exitCode + '\n' + output.substring(0, 1500);
|
||||
}
|
||||
return output.substring(0, 500);
|
||||
}
|
||||
|
||||
async function agenticRetryOnError(errorOutput, conv) {
|
||||
_agenticRetryCount++;
|
||||
if (_agenticRetryCount > MAX_AGENTIC_RETRIES) {
|
||||
termPrint('\n[!] Max retries reached (' + MAX_AGENTIC_RETRIES + '). Fix manually or ask again.', 'err');
|
||||
showStatusToast('Build failed after ' + MAX_AGENTIC_RETRIES + ' retries', 'err');
|
||||
if (_agenticRetryCount > getMaxRetries()) {
|
||||
termPrint('\n[!] Max retries reached (' + getMaxRetries() + '). Fix manually or ask again.', 'err');
|
||||
showStatusToast('Build failed after ' + getMaxRetries() + ' retries', 'err');
|
||||
return;
|
||||
}
|
||||
|
||||
termPrint('\n[!] Build failed. Asking AI to fix (attempt ' + _agenticRetryCount + '/' + MAX_AGENTIC_RETRIES + ')...', 'warning');
|
||||
termPrint('\n[!] Build failed. Asking AI to fix (attempt ' + _agenticRetryCount + '/' + getMaxRetries() + ')...', 'warning');
|
||||
showStatusToast('Build failed — AI auto-fixing (attempt ' + _agenticRetryCount + ')...', 'err');
|
||||
|
||||
if (!conv || !state.apiKey) return;
|
||||
@@ -1644,7 +1654,7 @@
|
||||
if (state.keepAwake) setWakeLock(false);
|
||||
|
||||
var fixActions = parseAiActions(state.streamingContent || '');
|
||||
if (fixActions.length > 0 && _agenticRetryCount <= MAX_AGENTIC_RETRIES) {
|
||||
if (fixActions.length > 0 && _agenticRetryCount <= getMaxRetries()) {
|
||||
await autoExecuteActions(fixActions, conv);
|
||||
}
|
||||
}
|
||||
@@ -1982,6 +1992,8 @@
|
||||
$('#settings-streaming').checked = state.streaming;
|
||||
$('#settings-autodeploy').checked = state.autoDeploy;
|
||||
$('#settings-keepawake').checked = state.keepAwake;
|
||||
$('#settings-maxretries').value = state.maxRetries;
|
||||
$('#retries-value').textContent = state.maxRetries;
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
@@ -2135,6 +2147,14 @@
|
||||
saveState();
|
||||
});
|
||||
|
||||
$('#settings-maxretries').addEventListener('input', function() {
|
||||
$('#retries-value').textContent = this.value;
|
||||
});
|
||||
$('#settings-maxretries').addEventListener('change', function() {
|
||||
state.maxRetries = parseInt(this.value) || 10;
|
||||
saveState();
|
||||
});
|
||||
|
||||
$('#theme-toggle-header').addEventListener('click', toggleTheme);
|
||||
|
||||
$('#settings-darkmode').addEventListener('change', function() {
|
||||
|
||||
Reference in New Issue
Block a user