mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
feat(web): ship kimi-web2 as the bundled web assets on this branch
- copy-web-assets.mjs now copies the static apps/kimi-web2 app into dist-web (no Vue build step needed); dev-only files are excluded. - live.js: on http(s) pages, try the same-origin server even without a token; a 401 opens the token dialog, which persists the token and reloads — so the server-hosted build connects out of the box. - Verified end-to-end against a protected local server (401 -> token dialog -> live workspaces/sessions/history over REST + WS).
This commit is contained in:
parent
f662febd2c
commit
92acd07452
6 changed files with 52 additions and 19 deletions
5
.changeset/web2-bundled-ui.md
Normal file
5
.changeset/web2-bundled-ui.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": minor
|
||||
---
|
||||
|
||||
web: Replace the bundled web UI with the design-first kimi-web2 prototype built from the Kimi Design System. Run kimi web and open the printed URL to try it.
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
"provenance": true
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm -C ../kimi-web run build && tsdown && node scripts/copy-native-assets.mjs && node scripts/copy-web-assets.mjs",
|
||||
"build": "tsdown && node scripts/copy-native-assets.mjs && node scripts/copy-web-assets.mjs",
|
||||
"prebuild": "node scripts/build-vis-asset.mjs",
|
||||
"catalog:update": "node scripts/update-catalog.mjs --out dist/built-in-catalog.json",
|
||||
"smoke": "node scripts/smoke.mjs",
|
||||
|
|
|
|||
|
|
@ -1,27 +1,33 @@
|
|||
import { cp, rm, stat } from 'node:fs/promises';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { basename, dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const repoRoot = resolve(appRoot, '../..');
|
||||
const source = resolve(repoRoot, 'apps/kimi-web/dist');
|
||||
// This branch ships the design-first kimi-web2 UI as the bundled web assets.
|
||||
// kimi-web2 is a static, no-build app, so it is copied directly (no dist step).
|
||||
const source = resolve(repoRoot, 'apps/kimi-web2');
|
||||
const target = resolve(appRoot, 'dist-web');
|
||||
|
||||
async function assertBuiltWeb() {
|
||||
// Dev/docs files that should not ship in the web asset bundle.
|
||||
const EXCLUDE = new Set(['serve.mjs', 'README.md', 'CONVENTIONS.md']);
|
||||
|
||||
async function assertWebSource() {
|
||||
try {
|
||||
const info = await stat(resolve(source, 'index.html'));
|
||||
if (!info.isFile()) {
|
||||
throw new Error('index.html is not a file');
|
||||
}
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Kimi web build output was not found at ${source}. Run \`pnpm --filter @moonshot-ai/kimi-web run build\` first.`,
|
||||
);
|
||||
throw new Error(`Kimi web assets were not found at ${source}.`);
|
||||
}
|
||||
}
|
||||
|
||||
await assertBuiltWeb();
|
||||
await assertWebSource();
|
||||
await rm(target, { recursive: true, force: true });
|
||||
await cp(source, target, { recursive: true });
|
||||
await cp(source, target, {
|
||||
recursive: true,
|
||||
filter: (src) => !EXCLUDE.has(basename(src)),
|
||||
});
|
||||
|
||||
console.log(`Copied Kimi web assets to ${target}`);
|
||||
console.log(`Copied Kimi web assets (kimi-web2) to ${target}`);
|
||||
|
|
|
|||
|
|
@ -517,9 +517,11 @@
|
|||
|
||||
/* ----------------------------- server auth ---------------------------- */
|
||||
function connectToken() {
|
||||
S.set({ authed: true });
|
||||
closeOverlays();
|
||||
toast('已连接(stub)', 'success');
|
||||
var inp = $('[data-token-input]');
|
||||
var v = inp && inp.value.trim();
|
||||
if (!v) { toast('请输入 Token', 'error'); return; }
|
||||
try { localStorage.setItem('kimi2-token', v); } catch (err) {}
|
||||
location.reload();
|
||||
}
|
||||
|
||||
/* ----------------------------- delegated clicks ----------------------- */
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@
|
|||
<div class="overlay" data-overlay="serverauth">
|
||||
<section class="modal confirm-modal" role="dialog" aria-modal="true">
|
||||
<div class="confirm-title">需要连接令牌</div>
|
||||
<div class="confirm-msg">此服务已开启保护。请输入本地服务启动时打印的 bearer token(原型中任意值均可通过)。</div>
|
||||
<div class="confirm-msg">此服务已开启保护。请输入本地服务启动时打印的 bearer token。</div>
|
||||
<div class="field" style="margin-top:14px">
|
||||
<svg><use href="#i-shield"/></svg>
|
||||
<input data-token-input type="password" placeholder="Token" autocomplete="off">
|
||||
|
|
|
|||
|
|
@ -64,7 +64,11 @@
|
|||
if (qsToken) { try { localStorage.setItem('kimi2-token', qsToken); } catch (e) {} }
|
||||
|
||||
var token = qsToken || safeGet('kimi2-token');
|
||||
if (!token) return; // no token → stay in stub mode, untouched.
|
||||
var httpPage = location.protocol.indexOf('http') === 0;
|
||||
// file:// without a token stays pure stub; an http(s) page always tries the
|
||||
// server — the same origin may be unprotected, and a 401 opens the token
|
||||
// dialog (wired in app.js) so the server-hosted build works out of the box.
|
||||
if (!token && !httpPage) return;
|
||||
|
||||
// Prefer explicit ?server=; else same-origin (proxy-friendly, no CORS need);
|
||||
// else the default local server (e.g. when opened via file://).
|
||||
|
|
@ -77,18 +81,27 @@
|
|||
function toast(msg, kind) { if (window.KP && window.KP.toast) window.KP.toast(msg, kind); }
|
||||
|
||||
/* ------------------------------ REST ---------------------------------- */
|
||||
function authHeaders(extra) {
|
||||
var h = extra || {};
|
||||
if (token) h.Authorization = 'Bearer ' + token;
|
||||
return h;
|
||||
}
|
||||
function get(path) {
|
||||
return fetch(api(path), { headers: { Authorization: 'Bearer ' + token } })
|
||||
.then(unwrap);
|
||||
return fetch(api(path), { headers: authHeaders() }).then(unwrap);
|
||||
}
|
||||
function post(path, body) {
|
||||
return fetch(api(path), {
|
||||
method: 'POST',
|
||||
headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
headers: authHeaders({ 'Content-Type': 'application/json' }),
|
||||
body: JSON.stringify(body || {}),
|
||||
}).then(unwrap);
|
||||
}
|
||||
function unwrap(res) {
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
var e = new Error('未授权(需要服务器 Token)');
|
||||
e.auth = true;
|
||||
throw e;
|
||||
}
|
||||
return res.json().then(function (env) {
|
||||
if (env.code !== 0) throw new Error(env.msg || ('服务端错误 ' + env.code));
|
||||
return env.data;
|
||||
|
|
@ -228,6 +241,13 @@
|
|||
toast('已连接服务器 ' + origin.replace(/^https?:\/\//, ''), 'success');
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (err && err.auth) {
|
||||
// Protected server, no/invalid token → ask for one (app.js persists it
|
||||
// to localStorage and reloads).
|
||||
console.warn('[live] 服务器需要 Token');
|
||||
if (window.KP && window.KP.openOverlay) window.KP.openOverlay('serverauth');
|
||||
return;
|
||||
}
|
||||
console.warn('[live] 连接服务器失败,保持离线模式:', err);
|
||||
toast('连接服务器失败,使用离线数据', 'error');
|
||||
});
|
||||
|
|
@ -296,7 +316,7 @@
|
|||
}
|
||||
function connectWs() {
|
||||
var url = origin.replace(/^http/, 'ws') + '/api/v1/ws?client_id=' + encodeURIComponent(clientId);
|
||||
try { ws = new WebSocket(url, ['kimi-code.bearer.' + token]); }
|
||||
try { ws = new WebSocket(url, token ? ['kimi-code.bearer.' + token] : undefined); }
|
||||
catch (e) { console.warn('[live] WS 创建失败:', e); return; }
|
||||
ws.onmessage = function (ev) {
|
||||
var f;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue