@zhongqian97-code/ecode 0.5.20 → 0.5.21
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/dist/index.js +172 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5552,6 +5552,21 @@ function generateAdminHtml(version2) {
|
|
|
5552
5552
|
}
|
|
5553
5553
|
#approve-btn:hover { background: #2ea043; color: #fff; }
|
|
5554
5554
|
|
|
5555
|
+
/* \u2500\u2500 Config & upgrade modals \u2500\u2500 */
|
|
5556
|
+
.modal-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,.7); z-index:100; align-items:center; justify-content:center; }
|
|
5557
|
+
.modal-overlay.open { display:flex; }
|
|
5558
|
+
.modal { background:#161b22; border:1px solid #30363d; border-radius:8px; padding:24px; width:min(480px,90vw); max-height:80vh; overflow-y:auto; }
|
|
5559
|
+
.modal h3 { color:#e6edf3; margin-bottom:16px; font-size:14px; }
|
|
5560
|
+
.form-row { margin-bottom:12px; }
|
|
5561
|
+
.form-row label { display:block; font-size:12px; color:#8b949e; margin-bottom:4px; }
|
|
5562
|
+
.form-row input, .form-row textarea { width:100%; background:#0d1117; border:1px solid #30363d; border-radius:4px; color:#c9d1d9; font-family:monospace; font-size:13px; padding:6px 8px; }
|
|
5563
|
+
.form-row textarea { resize:vertical; min-height:60px; }
|
|
5564
|
+
.btn { padding:6px 14px; border-radius:4px; border:1px solid #30363d; cursor:pointer; font-family:monospace; font-size:13px; }
|
|
5565
|
+
.btn-primary { background:#1f6feb; color:#fff; border-color:#1f6feb; }
|
|
5566
|
+
.btn-danger { background:#da3633; color:#fff; border-color:#da3633; }
|
|
5567
|
+
.modal-footer { display:flex; gap:8px; justify-content:flex-end; margin-top:16px; }
|
|
5568
|
+
#upgrade-output { display:none; background:#0d1117; border:1px solid #30363d; border-radius:4px; padding:8px; font-family:monospace; font-size:12px; color:#c9d1d9; white-space:pre-wrap; max-height:200px; overflow-y:auto; margin-top:8px; }
|
|
5569
|
+
|
|
5555
5570
|
/* \u2500\u2500 Mobile \u2500\u2500 */
|
|
5556
5571
|
@media (max-width: 600px) {
|
|
5557
5572
|
#hamburger { display: block; }
|
|
@@ -5571,6 +5586,8 @@ function generateAdminHtml(version2) {
|
|
|
5571
5586
|
<button id="hamburger" aria-label="Toggle sidebar">\u2630</button>
|
|
5572
5587
|
<h1>\u26A1 ecode web admin</h1>
|
|
5573
5588
|
<span class="version">v${version2}</span>
|
|
5589
|
+
<button id="config-btn" class="btn">\u914D\u7F6E</button>
|
|
5590
|
+
<button id="upgrade-btn" class="btn">\u5347\u7EA7</button>
|
|
5574
5591
|
</div>
|
|
5575
5592
|
|
|
5576
5593
|
<div id="app">
|
|
@@ -5604,6 +5621,35 @@ function generateAdminHtml(version2) {
|
|
|
5604
5621
|
</div>
|
|
5605
5622
|
</div>
|
|
5606
5623
|
|
|
5624
|
+
<!-- Config modal -->
|
|
5625
|
+
<div id="config-modal" class="modal-overlay">
|
|
5626
|
+
<div class="modal">
|
|
5627
|
+
<h3>\u2699 \u914D\u7F6E</h3>
|
|
5628
|
+
<div class="form-row"><label>Model</label><input id="cfg-model" name="model" type="text" /></div>
|
|
5629
|
+
<div class="form-row"><label>Base URL</label><input id="cfg-baseurl" name="baseUrl" type="text" /></div>
|
|
5630
|
+
<div class="form-row"><label>API Key</label><input id="cfg-apikey" name="apiKey" type="password" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" /></div>
|
|
5631
|
+
<div class="form-row"><label>Log Dir</label><input id="cfg-logdir" name="logDir" type="text" /></div>
|
|
5632
|
+
<div class="form-row"><label>System Prompt</label><textarea id="cfg-systemprompt"></textarea></div>
|
|
5633
|
+
<div class="modal-footer">
|
|
5634
|
+
<button id="cancel-config" class="btn">\u53D6\u6D88</button>
|
|
5635
|
+
<button id="save-config" class="btn btn-primary">\u4FDD\u5B58</button>
|
|
5636
|
+
</div>
|
|
5637
|
+
</div>
|
|
5638
|
+
</div>
|
|
5639
|
+
|
|
5640
|
+
<!-- Upgrade modal -->
|
|
5641
|
+
<div id="upgrade-modal" class="modal-overlay">
|
|
5642
|
+
<div class="modal">
|
|
5643
|
+
<h3>\u2B06 \u5347\u7EA7 ecode</h3>
|
|
5644
|
+
<div id="upgrade-status">\u6B63\u5728\u68C0\u67E5\u7248\u672C\u2026</div>
|
|
5645
|
+
<div id="upgrade-output"></div>
|
|
5646
|
+
<div class="modal-footer">
|
|
5647
|
+
<button id="cancel-upgrade" class="btn">\u5173\u95ED</button>
|
|
5648
|
+
<button id="confirm-upgrade" class="btn btn-primary" disabled>\u5347\u7EA7</button>
|
|
5649
|
+
</div>
|
|
5650
|
+
</div>
|
|
5651
|
+
</div>
|
|
5652
|
+
|
|
5607
5653
|
<!-- Bash approval modal -->
|
|
5608
5654
|
<div id="approval-modal" role="dialog" aria-modal="true">
|
|
5609
5655
|
<div id="approval-box">
|
|
@@ -5636,7 +5682,7 @@ function generateAdminHtml(version2) {
|
|
|
5636
5682
|
return path + sep + 'token=' + encodeURIComponent(state.token);
|
|
5637
5683
|
}
|
|
5638
5684
|
|
|
5639
|
-
async function apiFetch(path, opts) {
|
|
5685
|
+
async function apiFetch(path, opts = {}) {
|
|
5640
5686
|
const url = apiUrl(path);
|
|
5641
5687
|
const res = await fetch(url, opts);
|
|
5642
5688
|
if (!res.ok) throw new Error(res.status + ' ' + res.statusText);
|
|
@@ -5979,6 +6025,94 @@ function generateAdminHtml(version2) {
|
|
|
5979
6025
|
document.getElementById('sidebar').classList.toggle('open');
|
|
5980
6026
|
});
|
|
5981
6027
|
|
|
6028
|
+
// \u2500\u2500 \u914D\u7F6E\u6A21\u6001\u6846 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6029
|
+
document.getElementById('config-btn').addEventListener('click', async () => {
|
|
6030
|
+
try {
|
|
6031
|
+
const data = await apiFetch('/api/config');
|
|
6032
|
+
document.getElementById('cfg-model').value = data.model || '';
|
|
6033
|
+
document.getElementById('cfg-baseurl').value = data.baseUrl || '';
|
|
6034
|
+
document.getElementById('cfg-logdir').value = data.logDir || '';
|
|
6035
|
+
document.getElementById('cfg-systemprompt').value = data.systemPrompt || '';
|
|
6036
|
+
} catch (e) {
|
|
6037
|
+
// open modal even if fetch fails; fields will be empty
|
|
6038
|
+
}
|
|
6039
|
+
document.getElementById('config-modal').classList.add('open');
|
|
6040
|
+
});
|
|
6041
|
+
|
|
6042
|
+
document.getElementById('cancel-config').addEventListener('click', () => {
|
|
6043
|
+
document.getElementById('config-modal').classList.remove('open');
|
|
6044
|
+
});
|
|
6045
|
+
|
|
6046
|
+
document.getElementById('save-config').addEventListener('click', async () => {
|
|
6047
|
+
const body = {
|
|
6048
|
+
model: document.getElementById('cfg-model').value,
|
|
6049
|
+
baseUrl: document.getElementById('cfg-baseurl').value,
|
|
6050
|
+
logDir: document.getElementById('cfg-logdir').value,
|
|
6051
|
+
systemPrompt: document.getElementById('cfg-systemprompt').value,
|
|
6052
|
+
};
|
|
6053
|
+
const apiKeyVal = document.getElementById('cfg-apikey').value;
|
|
6054
|
+
if (apiKeyVal) body.apiKey = apiKeyVal;
|
|
6055
|
+
try {
|
|
6056
|
+
await apiFetch('/api/config', { method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body) });
|
|
6057
|
+
} catch (e) {
|
|
6058
|
+
// ignore errors silently for now
|
|
6059
|
+
}
|
|
6060
|
+
document.getElementById('config-modal').classList.remove('open');
|
|
6061
|
+
});
|
|
6062
|
+
|
|
6063
|
+
// \u2500\u2500 \u5347\u7EA7 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6064
|
+
document.getElementById('upgrade-btn').addEventListener('click', async () => {
|
|
6065
|
+
const modal = document.getElementById('upgrade-modal');
|
|
6066
|
+
const statusEl = document.getElementById('upgrade-status');
|
|
6067
|
+
const outputEl = document.getElementById('upgrade-output');
|
|
6068
|
+
const confirmBtn = document.getElementById('confirm-upgrade');
|
|
6069
|
+
outputEl.style.display = 'none';
|
|
6070
|
+
outputEl.textContent = '';
|
|
6071
|
+
confirmBtn.disabled = true;
|
|
6072
|
+
statusEl.textContent = '\u6B63\u5728\u68C0\u67E5\u7248\u672C\u2026';
|
|
6073
|
+
modal.classList.add('open');
|
|
6074
|
+
try {
|
|
6075
|
+
const r = await fetch(apiUrl('/api/version/check'));
|
|
6076
|
+
const v = await r.json();
|
|
6077
|
+
if (v.needsUpdate) {
|
|
6078
|
+
statusEl.textContent = '\u5F53\u524D v' + v.current + '\uFF0C\u6700\u65B0 v' + v.latest + '\uFF0C\u786E\u8BA4\u5347\u7EA7\uFF1F';
|
|
6079
|
+
confirmBtn.disabled = false;
|
|
6080
|
+
} else {
|
|
6081
|
+
statusEl.textContent = '\u5DF2\u662F\u6700\u65B0\u7248\u672C v' + v.current;
|
|
6082
|
+
}
|
|
6083
|
+
} catch(e) {
|
|
6084
|
+
statusEl.textContent = '\u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF1A' + e.message;
|
|
6085
|
+
}
|
|
6086
|
+
});
|
|
6087
|
+
|
|
6088
|
+
document.getElementById('cancel-upgrade').addEventListener('click', () => {
|
|
6089
|
+
document.getElementById('upgrade-modal').classList.remove('open');
|
|
6090
|
+
});
|
|
6091
|
+
|
|
6092
|
+
document.getElementById('confirm-upgrade').addEventListener('click', async () => {
|
|
6093
|
+
const outputEl = document.getElementById('upgrade-output');
|
|
6094
|
+
const confirmBtn = document.getElementById('confirm-upgrade');
|
|
6095
|
+
const statusEl = document.getElementById('upgrade-status');
|
|
6096
|
+
confirmBtn.disabled = true;
|
|
6097
|
+
outputEl.style.display = 'block';
|
|
6098
|
+
outputEl.textContent = '';
|
|
6099
|
+
statusEl.textContent = '\u5347\u7EA7\u4E2D\u2026';
|
|
6100
|
+
try {
|
|
6101
|
+
const r = await fetch(apiUrl('/api/system/upgrade'), { method: 'POST' });
|
|
6102
|
+
const reader = r.body.getReader();
|
|
6103
|
+
const decoder = new TextDecoder();
|
|
6104
|
+
while (true) {
|
|
6105
|
+
const { done, value } = await reader.read();
|
|
6106
|
+
if (done) break;
|
|
6107
|
+
outputEl.textContent += decoder.decode(value);
|
|
6108
|
+
outputEl.scrollTop = outputEl.scrollHeight;
|
|
6109
|
+
}
|
|
6110
|
+
statusEl.textContent = '\u5347\u7EA7\u5B8C\u6210\uFF0C\u8BF7\u91CD\u542F ecode web';
|
|
6111
|
+
} catch(e) {
|
|
6112
|
+
statusEl.textContent = '\u5347\u7EA7\u5931\u8D25\uFF1A' + e.message;
|
|
6113
|
+
}
|
|
6114
|
+
});
|
|
6115
|
+
|
|
5982
6116
|
// \u2500\u2500 Init \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5983
6117
|
loadSessions();
|
|
5984
6118
|
</script>
|
|
@@ -6279,6 +6413,42 @@ async function sessionHubRoutes(app, opts) {
|
|
|
6279
6413
|
);
|
|
6280
6414
|
}
|
|
6281
6415
|
|
|
6416
|
+
// src/web/routes/system.ts
|
|
6417
|
+
import { execSync } from "child_process";
|
|
6418
|
+
import { spawn } from "child_process";
|
|
6419
|
+
async function systemRoutes(app, opts) {
|
|
6420
|
+
app.get("/api/version/check", async (_request, _reply) => {
|
|
6421
|
+
const current = opts.version;
|
|
6422
|
+
let latest;
|
|
6423
|
+
try {
|
|
6424
|
+
latest = execSync("npm view @zhongqian97-code/ecode version", {
|
|
6425
|
+
encoding: "utf-8",
|
|
6426
|
+
timeout: 5e3
|
|
6427
|
+
}).trim();
|
|
6428
|
+
} catch {
|
|
6429
|
+
latest = "unknown";
|
|
6430
|
+
}
|
|
6431
|
+
const needsUpdate = latest !== "unknown" && latest !== current;
|
|
6432
|
+
return { current, latest, needsUpdate };
|
|
6433
|
+
});
|
|
6434
|
+
app.post("/api/system/upgrade", (_request, reply) => {
|
|
6435
|
+
reply.raw.setHeader("Content-Type", "text/plain");
|
|
6436
|
+
reply.raw.write("Upgrading @zhongqian97-code/ecode...\n");
|
|
6437
|
+
const child = spawn("npm", ["update", "-g", "@zhongqian97-code/ecode"]);
|
|
6438
|
+
child.stdout.on("data", (chunk) => {
|
|
6439
|
+
reply.raw.write(chunk);
|
|
6440
|
+
});
|
|
6441
|
+
child.stderr.on("data", (chunk) => {
|
|
6442
|
+
reply.raw.write(chunk);
|
|
6443
|
+
});
|
|
6444
|
+
child.on("close", () => {
|
|
6445
|
+
reply.raw.write("Upgrade complete. Please restart ecode web.\n");
|
|
6446
|
+
reply.raw.end();
|
|
6447
|
+
});
|
|
6448
|
+
return reply;
|
|
6449
|
+
});
|
|
6450
|
+
}
|
|
6451
|
+
|
|
6282
6452
|
// src/web/server.ts
|
|
6283
6453
|
async function buildServer(opts) {
|
|
6284
6454
|
const app = Fastify({ logger: false });
|
|
@@ -6306,6 +6476,7 @@ async function buildServer(opts) {
|
|
|
6306
6476
|
await app.register(configRoutes, { config: opts.config });
|
|
6307
6477
|
await app.register(automationRoutes, { config: opts.config });
|
|
6308
6478
|
await app.register(chatRoutes, { config: opts.config, manager: opts.manager });
|
|
6479
|
+
await app.register(systemRoutes, { version: opts.version });
|
|
6309
6480
|
await app.register(sessionHubRoutes, { manager: opts.manager });
|
|
6310
6481
|
return app;
|
|
6311
6482
|
}
|