neoagent 2.4.4-beta.7 → 2.4.4-beta.9
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/.env.example +17 -0
- package/docs/configuration.md +1 -0
- package/docs/supermemory-memory-review.md +1 -1
- package/flutter_app/lib/main_account_settings.dart +79 -15
- package/flutter_app/lib/main_chat.dart +155 -8
- package/flutter_app/lib/main_controller.dart +38 -4
- package/flutter_app/lib/main_devices.dart +9 -3
- package/flutter_app/lib/main_models.dart +32 -0
- package/flutter_app/lib/main_settings.dart +30 -35
- package/lib/manager.js +52 -0
- package/lib/schema_migrations.js +27 -2
- package/package.json +1 -1
- package/server/admin/admin.js +151 -0
- package/server/admin/index.html +55 -3
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +53288 -53147
- package/server/routes/account.js +2 -23
- package/server/routes/admin.js +18 -2
- package/server/routes/agents.js +5 -1
- package/server/routes/memory.js +2 -2
- package/server/services/account/service_email_settings.js +167 -0
- package/server/services/ai/engine.js +2 -17
- package/server/services/ai/rate_limits.js +150 -0
- package/server/services/ai/tools.js +11 -8
- package/server/services/memory/manager.js +0 -3
- package/server/services/websocket.js +5 -1
package/lib/manager.js
CHANGED
|
@@ -1693,6 +1693,54 @@ async function cmdRebuildWeb() {
|
|
|
1693
1693
|
buildBundledWebClientIfPossible();
|
|
1694
1694
|
}
|
|
1695
1695
|
|
|
1696
|
+
async function cmdFix() {
|
|
1697
|
+
cliBanner(`Fix ${APP_NAME}`, 'reset and recover');
|
|
1698
|
+
heading('Backup');
|
|
1699
|
+
backupRuntimeData();
|
|
1700
|
+
logOk(`Runtime data backed up to ${path.join(RUNTIME_HOME, 'backups')}`);
|
|
1701
|
+
|
|
1702
|
+
heading('Stop');
|
|
1703
|
+
cmdStop();
|
|
1704
|
+
|
|
1705
|
+
if (fs.existsSync(path.join(APP_DIR, '.git')) && commandExists('git')) {
|
|
1706
|
+
heading('Reset source files');
|
|
1707
|
+
const dirty = runQuiet('git', ['status', '--porcelain']);
|
|
1708
|
+
if (dirty.status === 0 && dirty.stdout.trim()) {
|
|
1709
|
+
runOrThrow('git', ['checkout', '--', '.']);
|
|
1710
|
+
logOk('Tracked source files reset to HEAD');
|
|
1711
|
+
} else {
|
|
1712
|
+
logOk('Working tree clean — nothing to reset');
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
heading('Dependencies');
|
|
1717
|
+
const nodeModulesDir = path.join(APP_DIR, 'node_modules');
|
|
1718
|
+
if (fs.existsSync(nodeModulesDir)) {
|
|
1719
|
+
logInfo('Removing node_modules…');
|
|
1720
|
+
fs.rmSync(nodeModulesDir, { recursive: true, force: true });
|
|
1721
|
+
logOk('node_modules removed');
|
|
1722
|
+
}
|
|
1723
|
+
installDependencies();
|
|
1724
|
+
|
|
1725
|
+
heading('Web Client');
|
|
1726
|
+
buildBundledWebClientIfPossible({ required: true });
|
|
1727
|
+
|
|
1728
|
+
heading('Start');
|
|
1729
|
+
const platform = detectPlatform();
|
|
1730
|
+
if (platform === 'macos' && commandExists('launchctl')) {
|
|
1731
|
+
installMacService();
|
|
1732
|
+
} else if (platform === 'linux' && commandExists('systemctl')) {
|
|
1733
|
+
installLinuxService();
|
|
1734
|
+
} else {
|
|
1735
|
+
startFallback();
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
const port = loadEnvPort();
|
|
1739
|
+
logOk(`Running on http://localhost:${port}`);
|
|
1740
|
+
heading('Ready');
|
|
1741
|
+
logInfo('Fix complete. Run `neoagent status` to verify.');
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1696
1744
|
function cmdUninstall() {
|
|
1697
1745
|
heading(`Uninstall ${APP_NAME}`);
|
|
1698
1746
|
const platform = detectPlatform();
|
|
@@ -1962,6 +2010,7 @@ function printHelp() {
|
|
|
1962
2010
|
row('restart', 'Stop, then start');
|
|
1963
2011
|
row('status', 'Health overview (server, service, config)');
|
|
1964
2012
|
row('logs', 'Tail server logs');
|
|
2013
|
+
row('fix', 'Backup, reset source, reinstall deps, restart');
|
|
1965
2014
|
row('uninstall', 'Remove the system service');
|
|
1966
2015
|
console.log('');
|
|
1967
2016
|
|
|
@@ -2035,6 +2084,9 @@ async function runCLI(argv) {
|
|
|
2035
2084
|
case 'logs':
|
|
2036
2085
|
cmdLogs();
|
|
2037
2086
|
break;
|
|
2087
|
+
case 'fix':
|
|
2088
|
+
await cmdFix();
|
|
2089
|
+
break;
|
|
2038
2090
|
case 'uninstall':
|
|
2039
2091
|
cmdUninstall();
|
|
2040
2092
|
break;
|
package/lib/schema_migrations.js
CHANGED
|
@@ -196,7 +196,7 @@ function migrateToolPermissions(db) {
|
|
|
196
196
|
CREATE TABLE IF NOT EXISTS tool_policies (
|
|
197
197
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
198
198
|
category TEXT NOT NULL,
|
|
199
|
-
policy TEXT NOT NULL CHECK(policy IN ('allow','deny','require_approval')),
|
|
199
|
+
policy TEXT NOT NULL CHECK(policy IN ('allow','deny','require_approval','allow_always')),
|
|
200
200
|
updated_at TEXT DEFAULT (datetime('now')),
|
|
201
201
|
PRIMARY KEY (user_id, category)
|
|
202
202
|
);
|
|
@@ -208,7 +208,7 @@ function migrateToolPermissions(db) {
|
|
|
208
208
|
tool_name TEXT NOT NULL,
|
|
209
209
|
tool_args_json TEXT,
|
|
210
210
|
decision TEXT NOT NULL CHECK(decision IN ('approved','denied','timeout')),
|
|
211
|
-
scope TEXT NOT NULL CHECK(scope IN ('once','session')),
|
|
211
|
+
scope TEXT NOT NULL CHECK(scope IN ('once','session','always')),
|
|
212
212
|
decided_at TEXT DEFAULT (datetime('now'))
|
|
213
213
|
);
|
|
214
214
|
|
|
@@ -217,6 +217,29 @@ function migrateToolPermissions(db) {
|
|
|
217
217
|
`);
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
function migrateToolPoliciesAllowAlways(db) {
|
|
221
|
+
// SQLite doesn't support ALTER COLUMN — recreate the table to add 'allow_always'
|
|
222
|
+
// to the CHECK constraint on existing installations.
|
|
223
|
+
const tableInfo = db.prepare("PRAGMA table_info(tool_policies)").all();
|
|
224
|
+
if (!tableInfo.length) return; // table doesn't exist yet; migrateToolPermissions will create it correctly
|
|
225
|
+
const checkRow = db.prepare(
|
|
226
|
+
"SELECT sql FROM sqlite_master WHERE type='table' AND name='tool_policies'",
|
|
227
|
+
).get();
|
|
228
|
+
if (checkRow && checkRow.sql.includes("'allow_always'")) return; // already migrated
|
|
229
|
+
db.exec(`
|
|
230
|
+
CREATE TABLE IF NOT EXISTS tool_policies_v2 (
|
|
231
|
+
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
232
|
+
category TEXT NOT NULL,
|
|
233
|
+
policy TEXT NOT NULL CHECK(policy IN ('allow','deny','require_approval','allow_always')),
|
|
234
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
235
|
+
PRIMARY KEY (user_id, category)
|
|
236
|
+
);
|
|
237
|
+
INSERT OR IGNORE INTO tool_policies_v2 SELECT * FROM tool_policies;
|
|
238
|
+
DROP TABLE tool_policies;
|
|
239
|
+
ALTER TABLE tool_policies_v2 RENAME TO tool_policies;
|
|
240
|
+
`);
|
|
241
|
+
}
|
|
242
|
+
|
|
220
243
|
function runSchemaMigrations(db) {
|
|
221
244
|
migrateMemoryEmbeddingIndex(db);
|
|
222
245
|
migrateMemoryProvenance(db);
|
|
@@ -224,6 +247,7 @@ function runSchemaMigrations(db) {
|
|
|
224
247
|
migrateMemoryRetrievalEvents(db);
|
|
225
248
|
migrateMemoryEmbeddingMetadata(db);
|
|
226
249
|
migrateToolPermissions(db);
|
|
250
|
+
migrateToolPoliciesAllowAlways(db);
|
|
227
251
|
}
|
|
228
252
|
|
|
229
253
|
module.exports = {
|
|
@@ -233,5 +257,6 @@ module.exports = {
|
|
|
233
257
|
migrateMemoryRetrievalEvents,
|
|
234
258
|
migrateMemoryEmbeddingMetadata,
|
|
235
259
|
migrateToolPermissions,
|
|
260
|
+
migrateToolPoliciesAllowAlways,
|
|
236
261
|
runSchemaMigrations,
|
|
237
262
|
};
|
package/package.json
CHANGED
package/server/admin/admin.js
CHANGED
|
@@ -225,6 +225,7 @@ async function triggerUpdate() {
|
|
|
225
225
|
async function loadConfig() {
|
|
226
226
|
const el = document.getElementById('config-content');
|
|
227
227
|
if (!el) return;
|
|
228
|
+
loadEmailConfig();
|
|
228
229
|
try {
|
|
229
230
|
const data = await api('/admin/api/config').then((r) => r.json());
|
|
230
231
|
const cfg = data.config || {};
|
|
@@ -246,6 +247,156 @@ async function loadConfig() {
|
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
async function loadEmailConfig() {
|
|
251
|
+
const el = document.getElementById('email-config-content');
|
|
252
|
+
if (!el) return;
|
|
253
|
+
try {
|
|
254
|
+
const data = await api('/admin/api/config/email').then((r) => r.json());
|
|
255
|
+
const s = data.settings || {};
|
|
256
|
+
const status = data.configured
|
|
257
|
+
? '<span class="badge badge-ok">configured</span><span>Account emails are enabled.</span>'
|
|
258
|
+
: `<span class="badge badge-idle">not configured</span><span>Missing: ${esc((data.missing || []).join(', '))}</span>`;
|
|
259
|
+
|
|
260
|
+
el.innerHTML = `
|
|
261
|
+
<div class="email-settings-status">${status}</div>
|
|
262
|
+
<form id="email-settings-form" onsubmit="saveEmailConfig(event)">
|
|
263
|
+
<div class="email-settings-grid">
|
|
264
|
+
<div class="field field-wide">
|
|
265
|
+
<label for="email-from">Sender address</label>
|
|
266
|
+
<input type="text" id="email-from" value="${esc(s.from)}" autocomplete="off">
|
|
267
|
+
</div>
|
|
268
|
+
<div class="field">
|
|
269
|
+
<label for="email-smtp-host">SMTP host</label>
|
|
270
|
+
<input type="text" id="email-smtp-host" value="${esc(s.smtpHost)}" autocomplete="off" spellcheck="false">
|
|
271
|
+
</div>
|
|
272
|
+
<div class="field">
|
|
273
|
+
<label for="email-smtp-port">SMTP port</label>
|
|
274
|
+
<input type="text" inputmode="numeric" id="email-smtp-port" value="${esc(s.smtpPort)}" autocomplete="off">
|
|
275
|
+
</div>
|
|
276
|
+
<div class="field">
|
|
277
|
+
<label for="email-smtp-user">SMTP username</label>
|
|
278
|
+
<input type="text" id="email-smtp-user" value="${esc(s.smtpUser)}" autocomplete="off" spellcheck="false">
|
|
279
|
+
</div>
|
|
280
|
+
<div class="field">
|
|
281
|
+
<label for="email-smtp-password">SMTP password</label>
|
|
282
|
+
<input type="password" id="email-smtp-password" value="" autocomplete="new-password">
|
|
283
|
+
<div style="font-size:11px;color:var(--text-muted);margin-top:6px;">
|
|
284
|
+
${s.smtpPasswordConfigured ? 'A password is stored. Leave blank to keep it.' : 'No password is stored.'}
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
<div class="field">
|
|
288
|
+
<label for="email-reply-to">Reply-To address</label>
|
|
289
|
+
<input type="text" id="email-reply-to" value="${esc(s.replyTo)}" autocomplete="off">
|
|
290
|
+
</div>
|
|
291
|
+
<div class="field">
|
|
292
|
+
<label for="email-brand-name">Brand name</label>
|
|
293
|
+
<input type="text" id="email-brand-name" value="${esc(s.brandName)}" autocomplete="off">
|
|
294
|
+
</div>
|
|
295
|
+
<div class="field">
|
|
296
|
+
<label for="email-public-url">Public URL override</label>
|
|
297
|
+
<input type="text" id="email-public-url" value="${esc(s.publicUrl)}" autocomplete="off" spellcheck="false">
|
|
298
|
+
</div>
|
|
299
|
+
<div class="field">
|
|
300
|
+
<label for="email-support-url">Support URL</label>
|
|
301
|
+
<input type="text" id="email-support-url" value="${esc(s.supportUrl)}" autocomplete="off" spellcheck="false">
|
|
302
|
+
</div>
|
|
303
|
+
<div class="field">
|
|
304
|
+
<label for="email-token-ttl">Link lifetime (hours)</label>
|
|
305
|
+
<input type="text" inputmode="numeric" id="email-token-ttl" value="${esc(s.tokenTtlHours)}" autocomplete="off">
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
<div class="email-settings-checks">
|
|
309
|
+
${emailConfigCheckbox('email-smtp-secure', 'Implicit TLS', s.smtpSecure)}
|
|
310
|
+
${emailConfigCheckbox('email-smtp-require-tls', 'Require STARTTLS', s.smtpRequireTls)}
|
|
311
|
+
${emailConfigCheckbox('email-smtp-reject-unauthorized', 'Reject invalid TLS certificates', s.smtpRejectUnauthorized)}
|
|
312
|
+
${emailConfigCheckbox('email-signup-confirmation', 'Require signup confirmation', s.requireSignupConfirmation)}
|
|
313
|
+
${emailConfigCheckbox('email-change-confirmation', 'Require email change confirmation', s.requireEmailChangeConfirmation)}
|
|
314
|
+
${emailConfigCheckbox('email-notify-login', 'Notify on unusual login', s.notifyUnusualLogin)}
|
|
315
|
+
${emailConfigCheckbox('email-notify-account', 'Notify on account changes', s.notifyAccountChanges)}
|
|
316
|
+
</div>
|
|
317
|
+
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
|
318
|
+
<button class="btn btn-primary" type="submit">Save Email Settings</button>
|
|
319
|
+
${s.smtpPasswordConfigured ? '<button class="btn btn-danger" type="button" onclick="clearEmailPassword(this)">Remove SMTP Password</button>' : ''}
|
|
320
|
+
</div>
|
|
321
|
+
</form>`;
|
|
322
|
+
} catch (err) {
|
|
323
|
+
if (err.message !== 'unauthorized') {
|
|
324
|
+
el.innerHTML = '<div class="empty">Failed to load email configuration</div>';
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function emailConfigCheckbox(id, label, checked) {
|
|
330
|
+
return `<label class="email-setting-check" for="${id}">
|
|
331
|
+
<input type="checkbox" id="${id}" ${checked ? 'checked' : ''}>
|
|
332
|
+
<span>${esc(label)}</span>
|
|
333
|
+
</label>`;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function emailConfigValue(id) {
|
|
337
|
+
return document.getElementById(id)?.value?.trim() || '';
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function collectEmailConfig(clearSmtpPassword = false) {
|
|
341
|
+
return {
|
|
342
|
+
from: emailConfigValue('email-from'),
|
|
343
|
+
smtpHost: emailConfigValue('email-smtp-host'),
|
|
344
|
+
smtpPort: emailConfigValue('email-smtp-port'),
|
|
345
|
+
smtpUser: emailConfigValue('email-smtp-user'),
|
|
346
|
+
smtpPassword: clearSmtpPassword ? '' : emailConfigValue('email-smtp-password'),
|
|
347
|
+
clearSmtpPassword,
|
|
348
|
+
smtpSecure: document.getElementById('email-smtp-secure')?.checked === true,
|
|
349
|
+
smtpRequireTls: document.getElementById('email-smtp-require-tls')?.checked === true,
|
|
350
|
+
smtpRejectUnauthorized: document.getElementById('email-smtp-reject-unauthorized')?.checked === true,
|
|
351
|
+
replyTo: emailConfigValue('email-reply-to'),
|
|
352
|
+
requireSignupConfirmation: document.getElementById('email-signup-confirmation')?.checked === true,
|
|
353
|
+
requireEmailChangeConfirmation: document.getElementById('email-change-confirmation')?.checked === true,
|
|
354
|
+
notifyUnusualLogin: document.getElementById('email-notify-login')?.checked === true,
|
|
355
|
+
notifyAccountChanges: document.getElementById('email-notify-account')?.checked === true,
|
|
356
|
+
brandName: emailConfigValue('email-brand-name'),
|
|
357
|
+
supportUrl: emailConfigValue('email-support-url'),
|
|
358
|
+
publicUrl: emailConfigValue('email-public-url'),
|
|
359
|
+
tokenTtlHours: emailConfigValue('email-token-ttl'),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function persistEmailConfig(payload, btn) {
|
|
364
|
+
const original = btn?.textContent;
|
|
365
|
+
if (btn) {
|
|
366
|
+
btn.disabled = true;
|
|
367
|
+
btn.textContent = 'Saving…';
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
const res = await api('/admin/api/config/email', {
|
|
371
|
+
method: 'PUT',
|
|
372
|
+
headers: { 'Content-Type': 'application/json' },
|
|
373
|
+
body: JSON.stringify(payload),
|
|
374
|
+
});
|
|
375
|
+
const body = await res.json().catch(() => ({}));
|
|
376
|
+
if (!res.ok) {
|
|
377
|
+
alert(body.error || 'Failed to save email settings');
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
await loadEmailConfig();
|
|
381
|
+
} catch (err) {
|
|
382
|
+
if (err.message !== 'unauthorized') alert('Network error');
|
|
383
|
+
} finally {
|
|
384
|
+
if (btn?.isConnected) {
|
|
385
|
+
btn.disabled = false;
|
|
386
|
+
btn.textContent = original;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async function saveEmailConfig(event) {
|
|
392
|
+
event.preventDefault();
|
|
393
|
+
await persistEmailConfig(collectEmailConfig(), event.submitter);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function clearEmailPassword(btn) {
|
|
397
|
+
await persistEmailConfig(collectEmailConfig(true), btn);
|
|
398
|
+
}
|
|
399
|
+
|
|
249
400
|
// ── Providers ──────────────────────────────────────────────────────────────
|
|
250
401
|
|
|
251
402
|
async function loadProviders() {
|
package/server/admin/index.html
CHANGED
|
@@ -326,6 +326,54 @@
|
|
|
326
326
|
color: var(--text-muted) !important;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
.email-settings-grid {
|
|
330
|
+
display: grid;
|
|
331
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
332
|
+
gap: 0 16px;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.email-settings-grid .field-wide {
|
|
336
|
+
grid-column: 1 / -1;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.email-settings-checks {
|
|
340
|
+
display: grid;
|
|
341
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
342
|
+
gap: 10px 16px;
|
|
343
|
+
margin: 4px 0 20px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.email-setting-check {
|
|
347
|
+
display: flex;
|
|
348
|
+
align-items: center;
|
|
349
|
+
gap: 9px;
|
|
350
|
+
color: var(--text-secondary);
|
|
351
|
+
font-size: 13px;
|
|
352
|
+
text-transform: none;
|
|
353
|
+
letter-spacing: 0;
|
|
354
|
+
margin: 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.email-setting-check input {
|
|
358
|
+
accent-color: var(--accent);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.email-settings-status {
|
|
362
|
+
display: flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
gap: 9px;
|
|
365
|
+
margin-bottom: 18px;
|
|
366
|
+
color: var(--text-muted);
|
|
367
|
+
font-size: 12px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@media (max-width: 760px) {
|
|
371
|
+
.email-settings-grid,
|
|
372
|
+
.email-settings-checks {
|
|
373
|
+
grid-template-columns: 1fr;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
329
377
|
/* ── Stats grid ────────────────────────────────────────────── */
|
|
330
378
|
.stat-grid {
|
|
331
379
|
display: grid;
|
|
@@ -948,7 +996,7 @@
|
|
|
948
996
|
<div>
|
|
949
997
|
<div style="font-size:15px;font-weight:700;color:var(--text);margin-bottom:4px;">Default Rate Limits</div>
|
|
950
998
|
<div style="font-size:12px;color:var(--text-muted);max-width:480px;">
|
|
951
|
-
Applied to every user unless they have a custom override.
|
|
999
|
+
Applied to every user unless they have a custom override. Empty values restore the built-in defaults.
|
|
952
1000
|
</div>
|
|
953
1001
|
</div>
|
|
954
1002
|
<button class="btn btn-primary" style="flex-shrink:0;" onclick="saveDefaultRateLimits(this)">Save Defaults</button>
|
|
@@ -956,12 +1004,12 @@
|
|
|
956
1004
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:18px;" id="default-limits-fields">
|
|
957
1005
|
<div>
|
|
958
1006
|
<label style="display:block;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted);margin-bottom:6px;">4-Hour Limit (tokens)</label>
|
|
959
|
-
<input type="number" min="0" step="1" id="default-limit-4h" placeholder="
|
|
1007
|
+
<input type="number" min="0" step="1" id="default-limit-4h" placeholder="Default: 2500000"
|
|
960
1008
|
style="width:100%;padding:9px 12px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;">
|
|
961
1009
|
</div>
|
|
962
1010
|
<div>
|
|
963
1011
|
<label style="display:block;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted);margin-bottom:6px;">Weekly Limit (tokens)</label>
|
|
964
|
-
<input type="number" min="0" step="1" id="default-limit-weekly" placeholder="
|
|
1012
|
+
<input type="number" min="0" step="1" id="default-limit-weekly" placeholder="Default: 10000000"
|
|
965
1013
|
style="width:100%;padding:9px 12px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;">
|
|
966
1014
|
</div>
|
|
967
1015
|
</div>
|
|
@@ -1051,6 +1099,10 @@
|
|
|
1051
1099
|
</div>
|
|
1052
1100
|
</div>
|
|
1053
1101
|
<div class="content">
|
|
1102
|
+
<div class="card" style="margin-bottom:20px;">
|
|
1103
|
+
<div class="card-title">Service Email</div>
|
|
1104
|
+
<div id="email-config-content"><div class="empty"><span class="spinner"></span></div></div>
|
|
1105
|
+
</div>
|
|
1054
1106
|
<div class="card">
|
|
1055
1107
|
<div class="card-title">Environment</div>
|
|
1056
1108
|
<div id="config-content"><div class="empty"><span class="spinner"></span></div></div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
0f41f70b1fd6912f77560d7afb5426b8
|
|
Binary file
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"77e2e94772b6eb43759e34ed1ad7da4674e19c
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "881551279" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|