@zhongqian97-code/ecode 0.5.18 → 0.5.19

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.
Files changed (2) hide show
  1. package/dist/index.js +133 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5244,6 +5244,135 @@ function createAuthHook(token) {
5244
5244
  };
5245
5245
  }
5246
5246
 
5247
+ // src/web/admin-html.ts
5248
+ function generateAdminHtml(version2) {
5249
+ return `<!DOCTYPE html>
5250
+ <html lang="zh">
5251
+ <head>
5252
+ <meta charset="UTF-8">
5253
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5254
+ <title>ecode web admin</title>
5255
+ <style>
5256
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
5257
+ body {
5258
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
5259
+ background: #0d1117; color: #c9d1d9; min-height: 100vh; padding: 24px;
5260
+ }
5261
+ header {
5262
+ display: flex; align-items: center; gap: 12px;
5263
+ padding-bottom: 16px; border-bottom: 1px solid #21262d; margin-bottom: 24px;
5264
+ }
5265
+ header h1 { font-size: 18px; font-weight: 600; color: #e6edf3; }
5266
+ header .version { font-size: 12px; color: #8b949e; background: #21262d;
5267
+ padding: 2px 8px; border-radius: 20px; }
5268
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; }
5269
+ .card {
5270
+ background: #161b22; border: 1px solid #21262d; border-radius: 8px; padding: 16px;
5271
+ }
5272
+ .card h2 { font-size: 13px; font-weight: 600; color: #8b949e;
5273
+ text-transform: uppercase; letter-spacing: .05em; margin-bottom: 12px; }
5274
+ .stat { display: flex; justify-content: space-between; align-items: center;
5275
+ padding: 6px 0; border-bottom: 1px solid #21262d; font-size: 13px; }
5276
+ .stat:last-child { border-bottom: none; }
5277
+ .stat .label { color: #8b949e; }
5278
+ .stat .value { color: #e6edf3; font-weight: 500; }
5279
+ .badge { display: inline-block; padding: 2px 8px; border-radius: 20px;
5280
+ font-size: 11px; font-weight: 600; }
5281
+ .badge.ok { background: #1a4731; color: #3fb950; }
5282
+ .badge.err { background: #4d1f24; color: #f85149; }
5283
+ .session-item {
5284
+ padding: 8px 0; border-bottom: 1px solid #21262d; font-size: 13px;
5285
+ }
5286
+ .session-item:last-child { border-bottom: none; }
5287
+ .session-id { color: #79c0ff; font-family: monospace; font-size: 12px; }
5288
+ .session-title { color: #e6edf3; margin-top: 2px; }
5289
+ .empty { color: #8b949e; font-size: 13px; font-style: italic; }
5290
+ .error { color: #f85149; font-size: 13px; }
5291
+ .loading { color: #8b949e; font-size: 13px; font-style: italic; }
5292
+ .api-row { font-size: 12px; font-family: monospace; padding: 4px 0;
5293
+ border-bottom: 1px solid #21262d; display: flex; gap: 8px; }
5294
+ .api-row:last-child { border-bottom: none; }
5295
+ .method { color: #79c0ff; min-width: 50px; }
5296
+ .path { color: #e6edf3; }
5297
+ </style>
5298
+ </head>
5299
+ <body>
5300
+ <header>
5301
+ <h1>\u26A1 ecode web admin</h1>
5302
+ <span class="version">v${version2}</span>
5303
+ </header>
5304
+
5305
+ <div class="grid">
5306
+ <div class="card">
5307
+ <h2>\u670D\u52A1\u72B6\u6001</h2>
5308
+ <div id="status-content"><span class="loading">\u52A0\u8F7D\u4E2D\u2026</span></div>
5309
+ </div>
5310
+
5311
+ <div class="card">
5312
+ <h2>\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD</h2>
5313
+ <div id="sessions-content"><span class="loading">\u52A0\u8F7D\u4E2D\u2026</span></div>
5314
+ </div>
5315
+
5316
+ <div class="card">
5317
+ <h2>API \u7AEF\u70B9</h2>
5318
+ <div class="api-row"><span class="method">GET</span><span class="path">/api/status</span></div>
5319
+ <div class="api-row"><span class="method">GET</span><span class="path">/api/sessions</span></div>
5320
+ <div class="api-row"><span class="method">GET</span><span class="path">/api/config</span></div>
5321
+ <div class="api-row"><span class="method">GET</span><span class="path">/api/automation/jobs</span></div>
5322
+ <div class="api-row"><span class="method">POST</span><span class="path">/api/chat/sessions</span></div>
5323
+ <div class="api-row"><span class="method">WS</span><span class="path">/api/ws/sessions/:id</span></div>
5324
+ </div>
5325
+ </div>
5326
+
5327
+ <script>
5328
+ const token = new URLSearchParams(location.search).get('token') || '';
5329
+
5330
+ async function apiFetch(path) {
5331
+ const sep = path.includes('?') ? '&' : '?';
5332
+ const r = await fetch(path + sep + 'token=' + encodeURIComponent(token));
5333
+ if (!r.ok) throw new Error(r.status + ' ' + r.statusText);
5334
+ return r.json();
5335
+ }
5336
+
5337
+ function setHtml(id, html) {
5338
+ document.getElementById(id).innerHTML = html;
5339
+ }
5340
+
5341
+ // \u72B6\u6001\u5361
5342
+ apiFetch('/api/status').then(d => {
5343
+ const rows = [
5344
+ ['\u72B6\u6001', '<span class="badge ok">\u8FD0\u884C\u4E2D</span>'],
5345
+ ['\u7248\u672C', d.version ?? '-'],
5346
+ ['\u6D3B\u8DC3\u4F1A\u8BDD', d.activeSessions ?? 0],
5347
+ ];
5348
+ setHtml('status-content',
5349
+ rows.map(([l,v]) =>
5350
+ '<div class="stat"><span class="label">' + l + '</span><span class="value">' + v + '</span></div>'
5351
+ ).join('')
5352
+ );
5353
+ }).catch(e => setHtml('status-content', '<span class="error">' + e.message + '</span>'));
5354
+
5355
+ // \u4F1A\u8BDD\u5217\u8868
5356
+ apiFetch('/api/sessions').then(d => {
5357
+ const sessions = d.sessions ?? d.data ?? d ?? [];
5358
+ if (!sessions.length) {
5359
+ setHtml('sessions-content', '<span class="empty">\u6682\u65E0\u4F1A\u8BDD</span>');
5360
+ return;
5361
+ }
5362
+ setHtml('sessions-content',
5363
+ sessions.slice(0, 10).map(s =>
5364
+ '<div class="session-item">' +
5365
+ '<div class="session-id">' + s.id + '</div>' +
5366
+ '<div class="session-title">' + (s.title || '(\u65E0\u6807\u9898)') + '</div>' +
5367
+ '</div>'
5368
+ ).join('')
5369
+ );
5370
+ }).catch(e => setHtml('sessions-content', '<span class="error">' + e.message + '</span>'));
5371
+ </script>
5372
+ </body>
5373
+ </html>`;
5374
+ }
5375
+
5247
5376
  // src/web/routes/status.ts
5248
5377
  async function statusRoutes(app, opts) {
5249
5378
  app.get(
@@ -5551,6 +5680,10 @@ async function buildServer(opts) {
5551
5680
  authHook(request, reply, done);
5552
5681
  });
5553
5682
  app.get("/health", async () => ({ ok: true }));
5683
+ const adminHtml = generateAdminHtml(opts.version);
5684
+ app.get("/", async (_req, reply) => {
5685
+ return reply.type("text/html").send(adminHtml);
5686
+ });
5554
5687
  await app.register(statusRoutes, {
5555
5688
  config: opts.config,
5556
5689
  manager: opts.manager,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhongqian97-code/ecode",
3
- "version": "0.5.18",
3
+ "version": "0.5.19",
4
4
  "description": "A minimal Claude Code clone with REPL interface and bash tool calling",
5
5
  "type": "module",
6
6
  "author": "zhongqian97-code",