@zhongqian97-code/ecode 0.5.22 → 0.5.24
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 +124 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5574,6 +5574,43 @@ function generateAdminHtml(version2) {
|
|
|
5574
5574
|
.num { color: #79c0ff; }
|
|
5575
5575
|
.fn { color: #d2a8ff; }
|
|
5576
5576
|
|
|
5577
|
+
/* \u2500\u2500 Toast notifications \u2500\u2500 */
|
|
5578
|
+
#toast-container {
|
|
5579
|
+
position: fixed;
|
|
5580
|
+
top: 16px;
|
|
5581
|
+
right: 16px;
|
|
5582
|
+
z-index: 200;
|
|
5583
|
+
display: flex;
|
|
5584
|
+
flex-direction: column;
|
|
5585
|
+
gap: 8px;
|
|
5586
|
+
pointer-events: none;
|
|
5587
|
+
}
|
|
5588
|
+
.toast {
|
|
5589
|
+
padding: 10px 16px;
|
|
5590
|
+
border-radius: 4px;
|
|
5591
|
+
font-size: 13px;
|
|
5592
|
+
max-width: 320px;
|
|
5593
|
+
background: #21262d;
|
|
5594
|
+
color: #c9d1d9;
|
|
5595
|
+
border: 1px solid #30363d;
|
|
5596
|
+
pointer-events: auto;
|
|
5597
|
+
animation: toast-in .2s ease;
|
|
5598
|
+
}
|
|
5599
|
+
.toast-error {
|
|
5600
|
+
background: #2d1b1b;
|
|
5601
|
+
color: #ff7b72;
|
|
5602
|
+
border-color: #f85149;
|
|
5603
|
+
}
|
|
5604
|
+
.toast-success {
|
|
5605
|
+
background: #1a2d1a;
|
|
5606
|
+
color: #3fb950;
|
|
5607
|
+
border-color: #2ea043;
|
|
5608
|
+
}
|
|
5609
|
+
@keyframes toast-in {
|
|
5610
|
+
from { opacity: 0; transform: translateX(20px); }
|
|
5611
|
+
to { opacity: 1; transform: translateX(0); }
|
|
5612
|
+
}
|
|
5613
|
+
|
|
5577
5614
|
/* \u2500\u2500 Mobile \u2500\u2500 */
|
|
5578
5615
|
@media (max-width: 600px) {
|
|
5579
5616
|
#hamburger { display: block; }
|
|
@@ -5593,6 +5630,7 @@ function generateAdminHtml(version2) {
|
|
|
5593
5630
|
<button id="hamburger" aria-label="Toggle sidebar">\u2630</button>
|
|
5594
5631
|
<h1>\u26A1 ecode web admin</h1>
|
|
5595
5632
|
<span class="version">v${version2}</span>
|
|
5633
|
+
<span id="topbar-model" class="version" style="display:none"></span>
|
|
5596
5634
|
<button id="config-btn" class="btn">\u914D\u7F6E</button>
|
|
5597
5635
|
<button id="upgrade-btn" class="btn">\u5347\u7EA7</button>
|
|
5598
5636
|
</div>
|
|
@@ -5657,6 +5695,9 @@ function generateAdminHtml(version2) {
|
|
|
5657
5695
|
</div>
|
|
5658
5696
|
</div>
|
|
5659
5697
|
|
|
5698
|
+
<!-- Toast notifications -->
|
|
5699
|
+
<div id="toast-container"></div>
|
|
5700
|
+
|
|
5660
5701
|
<!-- Bash approval modal -->
|
|
5661
5702
|
<div id="approval-modal" role="dialog" aria-modal="true">
|
|
5662
5703
|
<div id="approval-box">
|
|
@@ -5683,6 +5724,17 @@ function generateAdminHtml(version2) {
|
|
|
5683
5724
|
streamingMsgEl: null, // DOM element currently receiving delta tokens
|
|
5684
5725
|
};
|
|
5685
5726
|
|
|
5727
|
+
// \u2500\u2500 Toast \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
|
|
5728
|
+
function showToast(msg, type) {
|
|
5729
|
+
const container = document.getElementById('toast-container');
|
|
5730
|
+
if (!container) return;
|
|
5731
|
+
const el = document.createElement('div');
|
|
5732
|
+
el.className = 'toast toast-' + (type || 'info');
|
|
5733
|
+
el.textContent = msg;
|
|
5734
|
+
container.appendChild(el);
|
|
5735
|
+
setTimeout(() => el.remove(), 4000);
|
|
5736
|
+
}
|
|
5737
|
+
|
|
5686
5738
|
// \u2500\u2500 Helpers \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
|
|
5687
5739
|
function apiUrl(path) {
|
|
5688
5740
|
const sep = path.includes('?') ? '&' : '?';
|
|
@@ -6032,7 +6084,7 @@ function generateAdminHtml(version2) {
|
|
|
6032
6084
|
selectSession(session.id);
|
|
6033
6085
|
}
|
|
6034
6086
|
} catch (e) {
|
|
6035
|
-
|
|
6087
|
+
showToast('\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25: ' + e.message, 'error');
|
|
6036
6088
|
}
|
|
6037
6089
|
});
|
|
6038
6090
|
|
|
@@ -6087,7 +6139,7 @@ function generateAdminHtml(version2) {
|
|
|
6087
6139
|
document.getElementById('cfg-logdir').value = data.logDir || '';
|
|
6088
6140
|
document.getElementById('cfg-systemprompt').value = data.systemPrompt || '';
|
|
6089
6141
|
} catch (e) {
|
|
6090
|
-
|
|
6142
|
+
showToast('\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25: ' + e.message, 'error');
|
|
6091
6143
|
}
|
|
6092
6144
|
document.getElementById('config-modal').classList.add('open');
|
|
6093
6145
|
});
|
|
@@ -6107,8 +6159,10 @@ function generateAdminHtml(version2) {
|
|
|
6107
6159
|
if (apiKeyVal) body.apiKey = apiKeyVal;
|
|
6108
6160
|
try {
|
|
6109
6161
|
await apiFetch('/api/config', { method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body) });
|
|
6162
|
+
updateTopbarModel(body.model);
|
|
6163
|
+
showToast('\u914D\u7F6E\u5DF2\u4FDD\u5B58', 'success');
|
|
6110
6164
|
} catch (e) {
|
|
6111
|
-
|
|
6165
|
+
showToast('\u4FDD\u5B58\u5931\u8D25: ' + e.message, 'error');
|
|
6112
6166
|
}
|
|
6113
6167
|
document.getElementById('config-modal').classList.remove('open');
|
|
6114
6168
|
});
|
|
@@ -6181,7 +6235,28 @@ function generateAdminHtml(version2) {
|
|
|
6181
6235
|
};
|
|
6182
6236
|
|
|
6183
6237
|
// \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
|
|
6238
|
+
function updateTopbarModel(model) {
|
|
6239
|
+
const el = document.getElementById('topbar-model');
|
|
6240
|
+
if (!el) return;
|
|
6241
|
+
if (model) {
|
|
6242
|
+
el.textContent = model;
|
|
6243
|
+
el.style.display = '';
|
|
6244
|
+
} else {
|
|
6245
|
+
el.style.display = 'none';
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
|
|
6249
|
+
async function initModel() {
|
|
6250
|
+
try {
|
|
6251
|
+
const data = await apiFetch('/api/config');
|
|
6252
|
+
updateTopbarModel(data.model);
|
|
6253
|
+
} catch {
|
|
6254
|
+
// non-critical, ignore
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
|
|
6184
6258
|
loadSessions();
|
|
6259
|
+
initModel();
|
|
6185
6260
|
</script>
|
|
6186
6261
|
</body>
|
|
6187
6262
|
</html>`;
|
|
@@ -6209,33 +6284,48 @@ async function statusRoutes(app, opts) {
|
|
|
6209
6284
|
import { readFileSync as readFileSync5, existsSync as existsSync4 } from "fs";
|
|
6210
6285
|
import { join as join13 } from "path";
|
|
6211
6286
|
async function sessionsRoutes(app, opts) {
|
|
6212
|
-
app.get("/api/sessions", async (_request,
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6287
|
+
app.get("/api/sessions", async (_request, _reply) => {
|
|
6288
|
+
const fileSessions = opts.config.logDir ? listSessions(opts.config.logDir) : [];
|
|
6289
|
+
if (!opts.manager) {
|
|
6290
|
+
return fileSessions;
|
|
6291
|
+
}
|
|
6292
|
+
const runningSnapshots = opts.manager.listRunning();
|
|
6293
|
+
const fileIds = new Set(fileSessions.map((s) => s.id));
|
|
6294
|
+
const runtimeSessions = runningSnapshots.filter((s) => !fileIds.has(s.id)).map((s) => ({
|
|
6295
|
+
id: s.id,
|
|
6296
|
+
title: s.title,
|
|
6297
|
+
model: s.model,
|
|
6298
|
+
status: s.status,
|
|
6299
|
+
turnCount: s.turnCount,
|
|
6300
|
+
totalTokens: s.totalTokens,
|
|
6301
|
+
startTime: s.startedAt,
|
|
6302
|
+
lastActivity: s.lastActivity,
|
|
6303
|
+
cwd: "",
|
|
6304
|
+
logFile: ""
|
|
6305
|
+
}));
|
|
6306
|
+
return [...fileSessions, ...runtimeSessions];
|
|
6217
6307
|
});
|
|
6218
6308
|
app.get(
|
|
6219
6309
|
"/api/sessions/:id/messages",
|
|
6220
6310
|
async (request, reply) => {
|
|
6221
6311
|
const { id } = request.params;
|
|
6222
|
-
if (
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6312
|
+
if (opts.config.logDir) {
|
|
6313
|
+
const session = findSession(opts.config.logDir, id);
|
|
6314
|
+
if (session) {
|
|
6315
|
+
const logFilePath2 = join13(opts.config.logDir, session.logFile);
|
|
6316
|
+
if (existsSync4(logFilePath2)) {
|
|
6317
|
+
try {
|
|
6318
|
+
const content = readFileSync5(logFilePath2, "utf-8");
|
|
6319
|
+
return reply.header("Content-Type", "text/plain; charset=utf-8").send(content);
|
|
6320
|
+
} catch {
|
|
6321
|
+
}
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6232
6324
|
}
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
return reply.header("Content-Type", "text/plain; charset=utf-8").send(content);
|
|
6236
|
-
} catch (_err) {
|
|
6237
|
-
return reply.code(404).send({ success: false, error: `Log file not found: ${session.logFile}` });
|
|
6325
|
+
if (opts.manager && opts.manager.getSession(id)) {
|
|
6326
|
+
return reply.header("Content-Type", "text/plain; charset=utf-8").send("");
|
|
6238
6327
|
}
|
|
6328
|
+
return reply.code(404).send({ success: false, error: `Session not found: ${id}` });
|
|
6239
6329
|
}
|
|
6240
6330
|
);
|
|
6241
6331
|
app.get(
|
|
@@ -6490,7 +6580,7 @@ async function systemRoutes(app, opts) {
|
|
|
6490
6580
|
try {
|
|
6491
6581
|
latest = execSync("npm view @zhongqian97-code/ecode version", {
|
|
6492
6582
|
encoding: "utf-8",
|
|
6493
|
-
timeout:
|
|
6583
|
+
timeout: 5e3
|
|
6494
6584
|
}).trim();
|
|
6495
6585
|
} catch {
|
|
6496
6586
|
latest = "unknown";
|
|
@@ -6539,7 +6629,7 @@ async function buildServer(opts) {
|
|
|
6539
6629
|
manager: opts.manager,
|
|
6540
6630
|
version: opts.version
|
|
6541
6631
|
});
|
|
6542
|
-
await app.register(sessionsRoutes, { config: opts.config });
|
|
6632
|
+
await app.register(sessionsRoutes, { config: opts.config, manager: opts.manager });
|
|
6543
6633
|
await app.register(configRoutes, { config: opts.config });
|
|
6544
6634
|
await app.register(automationRoutes, { config: opts.config });
|
|
6545
6635
|
await app.register(chatRoutes, { config: opts.config, manager: opts.manager });
|
|
@@ -6640,6 +6730,7 @@ var SessionRuntime = class {
|
|
|
6640
6730
|
config;
|
|
6641
6731
|
model;
|
|
6642
6732
|
abortController = null;
|
|
6733
|
+
_stopAfterToolRound = false;
|
|
6643
6734
|
_turnCount = 0;
|
|
6644
6735
|
_totalTokens = 0;
|
|
6645
6736
|
startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6758,6 +6849,10 @@ var SessionRuntime = class {
|
|
|
6758
6849
|
this.bus.emit({ type: "tool.completed", callId: tc.id, toolName: tc.name, output: toolResult });
|
|
6759
6850
|
this.messages.push({ role: "tool", tool_call_id: tc.id, content: toolResult });
|
|
6760
6851
|
}
|
|
6852
|
+
if (this._stopAfterToolRound) {
|
|
6853
|
+
this._stopAfterToolRound = false;
|
|
6854
|
+
break;
|
|
6855
|
+
}
|
|
6761
6856
|
} else {
|
|
6762
6857
|
if (assistantText) {
|
|
6763
6858
|
this.messages.push({
|
|
@@ -6792,7 +6887,10 @@ Proceed?`;
|
|
|
6792
6887
|
this.bus.emit({ type: "approval.requested", requestId: reqId, kind, prompt });
|
|
6793
6888
|
const approved = await promise;
|
|
6794
6889
|
this._status = "tool_calling";
|
|
6795
|
-
if (!approved)
|
|
6890
|
+
if (!approved) {
|
|
6891
|
+
this._stopAfterToolRound = true;
|
|
6892
|
+
return SKIP_MESSAGE;
|
|
6893
|
+
}
|
|
6796
6894
|
}
|
|
6797
6895
|
if (signal.aborted) return SKIP_MESSAGE;
|
|
6798
6896
|
const result = await executeBash(parsed.command);
|