@kitecd/cli 1.1.0 → 1.2.0

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 (80) hide show
  1. package/dist/doctor.js +181 -0
  2. package/dist/export.js +144 -0
  3. package/dist/ignore.js +38 -0
  4. package/dist/import.js +333 -0
  5. package/dist/index.js +201 -3
  6. package/dist/ops.js +338 -0
  7. package/dist/pack.js +15 -5
  8. package/dist/serve.js +27 -1
  9. package/dist/server/index.js +14578 -5647
  10. package/dist/upload.js +9 -3
  11. package/dist/verify.js +161 -0
  12. package/dist/web/assets/AuditLog-BFmJfgzL.js +1 -0
  13. package/dist/web/assets/ConfirmDialog-CJ8lJeUc.js +1 -0
  14. package/dist/web/assets/ConfirmDialog-D8avT8FJ.css +1 -0
  15. package/dist/web/assets/Dashboard-BTliTkq1.js +1 -0
  16. package/dist/web/assets/DefaultLayout-BG_y85yG.js +1 -0
  17. package/dist/web/assets/DefaultLayout-CZENO67n.css +1 -0
  18. package/dist/web/assets/FileExplorer-Bf32_MMS.js +1 -0
  19. package/dist/web/assets/LogBoard-0so-XiW3.css +1 -0
  20. package/dist/web/assets/LogBoard-CDz4n0DY.js +6 -0
  21. package/dist/web/assets/Login-C3zfObzP.js +1 -0
  22. package/dist/web/assets/Migration-BaKlcCkB.js +1 -0
  23. package/dist/web/assets/ProjectDetail-BL_D0OJY.js +1 -0
  24. package/dist/web/assets/ProjectDetail-ia6-z1kZ.css +1 -0
  25. package/dist/web/assets/ProjectList-7AnhOS7h.css +1 -0
  26. package/dist/web/assets/ProjectList-C4KuZEq4.js +1 -0
  27. package/dist/web/assets/Settings-CzWG9312.js +1 -0
  28. package/dist/web/assets/Storage-BCao_e7Y.js +1 -0
  29. package/dist/web/assets/Storage-DNadqpUy.css +1 -0
  30. package/dist/web/assets/{activity-DItEGOtI.js → activity-Ba-YPfmn.js} +1 -1
  31. package/dist/web/assets/archive-CJ5gDBKY.js +1 -0
  32. package/dist/web/assets/arrow-left-WEOMLXIi.js +1 -0
  33. package/dist/web/assets/chevron-right-DLiDJVJl.js +1 -0
  34. package/dist/web/assets/{circle-alert-Bfrn_ovD.js → circle-alert-DQzM-U4P.js} +1 -1
  35. package/dist/web/assets/clock-IwxBKgP4.js +1 -0
  36. package/dist/web/assets/constants-Ch47JPs3.js +1 -0
  37. package/dist/web/assets/copy-C1rADgcQ.js +1 -0
  38. package/dist/web/assets/createLucideIcon-CE-ry2oA.js +1 -0
  39. package/dist/web/assets/database-CI6acTSY.js +1 -0
  40. package/dist/web/assets/eye-Cas8HsmK.js +1 -0
  41. package/dist/web/assets/eye-off-4PD31MPV.js +1 -0
  42. package/dist/web/assets/file-text-C6nzo1us.js +1 -0
  43. package/dist/web/assets/folder-D9pvA6oI.js +1 -0
  44. package/dist/web/assets/folder-open-CGeIri0U.js +1 -0
  45. package/dist/web/assets/hard-drive-CGWwV4Ei.js +1 -0
  46. package/dist/web/assets/house-8ZvvlaEh.js +1 -0
  47. package/dist/web/assets/index-BFE6PEIL.js +2 -0
  48. package/dist/web/assets/index-XrzJwjrk.css +1 -0
  49. package/dist/web/assets/loader-circle-uiC7GaCG.js +1 -0
  50. package/dist/web/assets/plus-CfMi1Jv1.js +1 -0
  51. package/dist/web/assets/refresh-cw-0yb8DZDc.js +1 -0
  52. package/dist/web/assets/rotate-ccw-YevaWXw9.js +1 -0
  53. package/dist/web/assets/save-BuzCP3T1.js +1 -0
  54. package/dist/web/assets/scroll-text-BIYMFNN5.js +1 -0
  55. package/dist/web/assets/{server-C33taHNn.js → server-B31hj0g7.js} +1 -1
  56. package/dist/web/assets/{square-terminal-C8toRwjx.js → square-terminal-DT6aoXm0.js} +1 -1
  57. package/dist/web/assets/sun-BaEbKNDV.js +1 -0
  58. package/dist/web/assets/trash-2-BWqtqkeW.js +1 -0
  59. package/dist/web/index.html +3 -3
  60. package/package.json +2 -2
  61. package/dist/web/assets/Dashboard-7wBCpwzp.js +0 -1
  62. package/dist/web/assets/DefaultLayout-Bj8fPWym.css +0 -1
  63. package/dist/web/assets/DefaultLayout-LaPhlICp.js +0 -1
  64. package/dist/web/assets/FileExplorer-DvzkxXvZ.js +0 -1
  65. package/dist/web/assets/LogBoard-BWAPesBz.js +0 -6
  66. package/dist/web/assets/LogBoard-DzW-cEqH.css +0 -1
  67. package/dist/web/assets/Login-BhNdUJs0.js +0 -1
  68. package/dist/web/assets/ProjectDetail-DZwMOEto.js +0 -1
  69. package/dist/web/assets/ProjectList-Czr-438J.js +0 -1
  70. package/dist/web/assets/Settings-OmPvcQbD.js +0 -1
  71. package/dist/web/assets/clock-BPXGSCIV.js +0 -1
  72. package/dist/web/assets/constants-iI5LEC2F.js +0 -1
  73. package/dist/web/assets/createLucideIcon-Cgv1AIRL.js +0 -1
  74. package/dist/web/assets/folder-open-jX-_Q7bA.js +0 -1
  75. package/dist/web/assets/index-C9LiRc31.css +0 -1
  76. package/dist/web/assets/index-eyx6wNyQ.js +0 -2
  77. package/dist/web/assets/project-BFuaDcvV.js +0 -1
  78. package/dist/web/assets/refresh-cw-DWmqwQRn.js +0 -1
  79. package/dist/web/assets/save-BkiMrL9q.js +0 -1
  80. package/dist/web/assets/settings-CrCWmNyB.js +0 -1
package/dist/ops.js ADDED
@@ -0,0 +1,338 @@
1
+ import chalk from 'chalk';
2
+ import readline from 'readline/promises';
3
+ import { stdin as input, stdout as output } from 'process';
4
+ import { randomUUID } from 'crypto';
5
+ import { readGlobalConfig, readLocalEnv, resolveProjectConfig, envTokenKey, listProjectEnvs, } from './home.js';
6
+ export function resolveOpsAuth(opts = {}) {
7
+ const globalCfg = readGlobalConfig();
8
+ const localEnv = readLocalEnv();
9
+ const serverUrl = opts.server ||
10
+ process.env.KITE_SERVER_URL ||
11
+ localEnv.KITE_SERVER_URL ||
12
+ globalCfg.serverUrl;
13
+ if (!serverUrl) {
14
+ throw new Error('Server URL not configured. Use --server <url> or run `kite config:set serverUrl <url> --global`.');
15
+ }
16
+ if (opts.token) {
17
+ return { serverUrl, token: opts.token, source: 'cli flag' };
18
+ }
19
+ if (opts.requireAdmin) {
20
+ const adminTok = process.env.KITE_TOKEN || globalCfg.token;
21
+ if (!adminTok) {
22
+ throw new Error('Admin token required. Run `kite config:set token <admin> --global` or pass --token.');
23
+ }
24
+ return { serverUrl, token: adminTok, source: 'global admin token' };
25
+ }
26
+ const envTok = process.env.KITE_TOKEN || process.env.KITE_DEPLOY_TOKEN || localEnv.KITE_TOKEN || localEnv.KITE_DEPLOY_TOKEN;
27
+ if (envTok)
28
+ return { serverUrl, token: envTok, source: 'env var' };
29
+ const allEnvs = listProjectEnvs();
30
+ let resolved = null;
31
+ if (opts.env) {
32
+ resolved = resolveProjectConfig(opts.env);
33
+ }
34
+ else if (allEnvs.length === 1) {
35
+ resolved = allEnvs[0];
36
+ }
37
+ if (resolved?.config?.projectId) {
38
+ const key = envTokenKey(resolved.config.projectId, resolved.env);
39
+ const projTok = globalCfg.projectToken?.[key];
40
+ if (projTok)
41
+ return { serverUrl, token: projTok, source: `project token (${key})` };
42
+ }
43
+ if (globalCfg.token) {
44
+ return { serverUrl, token: globalCfg.token, source: 'global admin token' };
45
+ }
46
+ throw new Error('Token not found. Configure via `kite config:set token <value>` (project) or `kite config:set token <admin> --global`.');
47
+ }
48
+ async function readJson(res) {
49
+ const text = await res.text();
50
+ try {
51
+ return JSON.parse(text);
52
+ }
53
+ catch {
54
+ return { error: text };
55
+ }
56
+ }
57
+ async function expectOk(res, label) {
58
+ if (res.status === 401)
59
+ throw new Error(`Unauthorized: invalid token (${label})`);
60
+ if (res.status === 404) {
61
+ const body = await readJson(res);
62
+ throw new Error(`Not found: ${body.error || label}`);
63
+ }
64
+ if (!res.ok) {
65
+ const body = await readJson(res);
66
+ throw new Error(`[${res.status}] ${body.error || label}`);
67
+ }
68
+ return readJson(res);
69
+ }
70
+ function statusColor(status) {
71
+ switch (status) {
72
+ case 'success': return chalk.green(status);
73
+ case 'failed': return chalk.red(status);
74
+ case 'running': return chalk.yellow(status);
75
+ case 'idle': return chalk.gray(status);
76
+ default: return chalk.gray(status || '-');
77
+ }
78
+ }
79
+ function padCell(text, width) {
80
+ const visible = text.replace(/\x1b\[[0-9;]*m/g, '');
81
+ if (visible.length >= width)
82
+ return text;
83
+ return text + ' '.repeat(width - visible.length);
84
+ }
85
+ function printTable(rows, headers) {
86
+ const cols = headers.length;
87
+ const widths = new Array(cols).fill(0);
88
+ for (const r of [headers, ...rows]) {
89
+ for (let i = 0; i < cols; i++) {
90
+ const visible = (r[i] || '').replace(/\x1b\[[0-9;]*m/g, '');
91
+ if (visible.length > widths[i])
92
+ widths[i] = visible.length;
93
+ }
94
+ }
95
+ const headerLine = headers.map((h, i) => padCell(chalk.bold(h), widths[i])).join(' ');
96
+ console.log(headerLine);
97
+ console.log(chalk.gray(widths.map((w) => '-'.repeat(w)).join(' ')));
98
+ for (const r of rows) {
99
+ console.log(r.map((c, i) => padCell(c, widths[i])).join(' '));
100
+ }
101
+ }
102
+ export async function runList(opts) {
103
+ const auth = resolveOpsAuth({ ...opts, requireAdmin: true });
104
+ const res = await fetch(`${auth.serverUrl.replace(/\/$/, '')}/api/projects`, {
105
+ headers: { Authorization: `Bearer ${auth.token}` },
106
+ });
107
+ const projects = await expectOk(res, 'GET /api/projects');
108
+ let filtered = projects;
109
+ if (opts.env)
110
+ filtered = projects.filter((p) => (p.env || '') === opts.env);
111
+ if (opts.json) {
112
+ process.stdout.write(JSON.stringify(filtered, null, 2) + '\n');
113
+ return 0;
114
+ }
115
+ if (filtered.length === 0) {
116
+ console.log(chalk.gray('No projects found.'));
117
+ return 0;
118
+ }
119
+ const rows = filtered.map((p) => [
120
+ p.id,
121
+ p.name,
122
+ p.env || chalk.gray('-'),
123
+ statusColor(p.status),
124
+ chalk.gray(p.updatedAt ? new Date(p.updatedAt).toISOString().slice(0, 19).replace('T', ' ') : '-'),
125
+ ]);
126
+ printTable(rows, ['ID', 'NAME', 'ENV', 'STATUS', 'UPDATED']);
127
+ console.log(chalk.gray(`\n${filtered.length} project(s)`));
128
+ return 0;
129
+ }
130
+ export async function runStatus(projectIdArg, opts) {
131
+ const auth = resolveOpsAuth({ ...opts, requireAdmin: true });
132
+ const limit = Math.min(Math.max(Number(opts.limit) || 5, 1), 50);
133
+ let projectId = projectIdArg;
134
+ if (!projectId) {
135
+ const all = listProjectEnvs();
136
+ const resolved = opts.env ? resolveProjectConfig(opts.env) : (all.length === 1 ? all[0] : null);
137
+ if (resolved?.config?.projectId) {
138
+ projectId = resolved.config.projectId;
139
+ }
140
+ }
141
+ if (!projectId) {
142
+ throw new Error('projectId required. Pass <projectId> or run inside a project directory with kite.config*.json');
143
+ }
144
+ const base = auth.serverUrl.replace(/\/$/, '');
145
+ const [projectRes, logsRes] = await Promise.all([
146
+ fetch(`${base}/api/projects/${projectId}`, { headers: { Authorization: `Bearer ${auth.token}` } }),
147
+ fetch(`${base}/api/logs`, { headers: { Authorization: `Bearer ${auth.token}` } }),
148
+ ]);
149
+ const project = await expectOk(projectRes, `GET /api/projects/${projectId}`);
150
+ const logs = await expectOk(logsRes, 'GET /api/logs');
151
+ const projectLogs = logs
152
+ .filter((l) => l.projectId === projectId)
153
+ .sort((a, b) => (b.startTime || '').localeCompare(a.startTime || ''))
154
+ .slice(0, limit);
155
+ if (opts.json) {
156
+ process.stdout.write(JSON.stringify({ project, deployments: projectLogs }, null, 2) + '\n');
157
+ return 0;
158
+ }
159
+ console.log(chalk.bold(`Project: ${project.name}`) + chalk.gray(` (${project.id})`));
160
+ console.log(chalk.gray(`Status: `) + statusColor(project.status));
161
+ console.log('');
162
+ if (projectLogs.length === 0) {
163
+ console.log(chalk.gray('No deployments yet.'));
164
+ return 0;
165
+ }
166
+ const rows = projectLogs.map((d) => [
167
+ d.id.slice(0, 12),
168
+ statusColor(d.status),
169
+ d.triggerSource,
170
+ chalk.gray(d.startTime ? d.startTime.slice(0, 19).replace('T', ' ') : '-'),
171
+ d.duration || chalk.gray('-'),
172
+ d.rollbackOf ? chalk.gray(`← ${d.rollbackOf.slice(0, 8)}`) : '',
173
+ ]);
174
+ printTable(rows, ['DEPLOY ID', 'STATUS', 'TRIGGER', 'STARTED', 'DURATION', 'ROLLBACK OF']);
175
+ return 0;
176
+ }
177
+ const STATUS_EXIT = { success: 0, failed: 1, running: 0 };
178
+ export async function runLogs(deployId, opts) {
179
+ const auth = resolveOpsAuth({ ...opts, requireAdmin: true });
180
+ const base = auth.serverUrl.replace(/\/$/, '');
181
+ if (!opts.follow) {
182
+ const res = await fetch(`${base}/api/logs/${deployId}`, {
183
+ headers: { Authorization: `Bearer ${auth.token}` },
184
+ });
185
+ const log = await expectOk(res, `GET /api/logs/${deployId}`);
186
+ if (opts.json) {
187
+ process.stdout.write(JSON.stringify(log, null, 2) + '\n');
188
+ return STATUS_EXIT[log.status] ?? 1;
189
+ }
190
+ console.log(chalk.bold(`Deployment ${log.id.slice(0, 12)}`) + chalk.gray(` (${log.projectName})`));
191
+ console.log(chalk.gray('Status: ') + statusColor(log.status) + chalk.gray(` duration=${log.duration || '-'} trigger=${log.triggerSource}`));
192
+ console.log('');
193
+ if (log.output)
194
+ process.stdout.write(log.output);
195
+ if (!log.output?.endsWith('\n'))
196
+ process.stdout.write('\n');
197
+ return STATUS_EXIT[log.status] ?? 1;
198
+ }
199
+ const res = await fetch(`${base}/api/logs/${deployId}/stream`, {
200
+ headers: {
201
+ Authorization: `Bearer ${auth.token}`,
202
+ Accept: 'text/event-stream',
203
+ },
204
+ });
205
+ if (res.status === 401)
206
+ throw new Error('Unauthorized: invalid token');
207
+ if (res.status === 404)
208
+ throw new Error('Deployment not found');
209
+ if (!res.ok || !res.body)
210
+ throw new Error(`SSE failed: HTTP ${res.status}`);
211
+ const reader = res.body.getReader();
212
+ const decoder = new TextDecoder();
213
+ let buffer = '';
214
+ let lastStatus = null;
215
+ let printedOutputLen = 0;
216
+ while (true) {
217
+ const { done, value } = await reader.read();
218
+ if (done)
219
+ break;
220
+ buffer += decoder.decode(value, { stream: true });
221
+ const events = buffer.split('\n\n');
222
+ buffer = events.pop();
223
+ for (const block of events) {
224
+ if (!block.trim())
225
+ continue;
226
+ let event = 'message';
227
+ let data = '';
228
+ for (const line of block.split('\n')) {
229
+ if (line.startsWith('event: '))
230
+ event = line.slice(7).trim();
231
+ else if (line.startsWith('data: '))
232
+ data += line.slice(6);
233
+ }
234
+ let parsed = data;
235
+ try {
236
+ parsed = JSON.parse(data);
237
+ }
238
+ catch { }
239
+ if (event === 'log') {
240
+ // First log event from server contains the full historical output.
241
+ // Subsequent events are incremental lines.
242
+ if (printedOutputLen === 0 && typeof parsed === 'string' && parsed.includes('\n')) {
243
+ process.stdout.write(parsed);
244
+ if (!parsed.endsWith('\n'))
245
+ process.stdout.write('\n');
246
+ printedOutputLen = parsed.length;
247
+ }
248
+ else {
249
+ process.stdout.write(parsed + '\n');
250
+ }
251
+ }
252
+ else if (event === 'status') {
253
+ let payload = parsed;
254
+ if (typeof payload === 'string') {
255
+ try {
256
+ payload = JSON.parse(payload);
257
+ }
258
+ catch { }
259
+ }
260
+ lastStatus = payload?.status || null;
261
+ const dur = payload?.duration || '-';
262
+ console.log(chalk.gray(`\n[Kite Logs] Deployment finished: `) + statusColor(lastStatus) + chalk.gray(` duration=${dur}`));
263
+ try {
264
+ reader.cancel();
265
+ }
266
+ catch { }
267
+ break;
268
+ }
269
+ }
270
+ if (lastStatus)
271
+ break;
272
+ }
273
+ return STATUS_EXIT[lastStatus || ''] ?? 0;
274
+ }
275
+ export async function runRollback(projectIdArg, opts) {
276
+ const auth = resolveOpsAuth({ ...opts, requireAdmin: true });
277
+ const base = auth.serverUrl.replace(/\/$/, '');
278
+ let projectId = projectIdArg;
279
+ if (!projectId) {
280
+ const all = listProjectEnvs();
281
+ const resolved = opts.env ? resolveProjectConfig(opts.env) : (all.length === 1 ? all[0] : null);
282
+ if (resolved?.config?.projectId)
283
+ projectId = resolved.config.projectId;
284
+ }
285
+ if (!projectId)
286
+ throw new Error('projectId required for rollback.');
287
+ let targetDeployId = opts.to;
288
+ if (!targetDeployId) {
289
+ const logsRes = await fetch(`${base}/api/logs`, { headers: { Authorization: `Bearer ${auth.token}` } });
290
+ const logs = await expectOk(logsRes, 'GET /api/logs');
291
+ const candidates = logs
292
+ .filter((l) => l.projectId === projectId && l.status === 'success' && !!l.artifactPath && l.triggerSource !== 'rollback')
293
+ .sort((a, b) => (b.startTime || '').localeCompare(a.startTime || ''));
294
+ if (candidates.length === 0) {
295
+ throw new Error('No successful deployment with archived artifact found. Pass --to <deployId> explicitly.');
296
+ }
297
+ targetDeployId = candidates[0].id;
298
+ console.log(chalk.gray(`Selected latest success deploy: ${targetDeployId.slice(0, 12)} (${candidates[0].startTime.slice(0, 19).replace('T', ' ')})`));
299
+ }
300
+ if (!opts.yes) {
301
+ if (!process.stdin.isTTY) {
302
+ throw new Error('Refusing to rollback in non-TTY mode without --yes. Add --yes to confirm.');
303
+ }
304
+ const rl = readline.createInterface({ input, output });
305
+ const answer = await rl.question(chalk.yellow(`About to rollback project ${projectId} to deploy ${targetDeployId.slice(0, 12)}. Proceed? [y/N] `));
306
+ rl.close();
307
+ if (!/^y(es)?$/i.test(answer.trim())) {
308
+ console.log(chalk.gray('Rollback cancelled.'));
309
+ return 1;
310
+ }
311
+ }
312
+ const traceId = randomUUID();
313
+ const res = await fetch(`${base}/api/deployments/${targetDeployId}/rollback`, {
314
+ method: 'POST',
315
+ headers: {
316
+ Authorization: `Bearer ${auth.token}`,
317
+ 'X-Kite-Trace-Id': traceId,
318
+ },
319
+ });
320
+ const body = await readJson(res);
321
+ if (res.status === 401)
322
+ throw new Error('Unauthorized: invalid token');
323
+ if (res.status === 404)
324
+ throw new Error(body?.error || 'Deployment or artifact not found');
325
+ if (!res.ok || !body?.success) {
326
+ throw new Error(body?.error || `Rollback failed: HTTP ${res.status}`);
327
+ }
328
+ if (opts.json) {
329
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
330
+ return 0;
331
+ }
332
+ console.log(chalk.green(`Rollback succeeded.`));
333
+ console.log(chalk.gray(` new deployId : ${body.deployId}`));
334
+ console.log(chalk.gray(` rolled back : ${body.rollbackOf}`));
335
+ console.log(chalk.gray(` duration : ${body.duration}`));
336
+ console.log(chalk.gray(` traceId : ${body.traceId}`));
337
+ return 0;
338
+ }
package/dist/pack.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import archiver from 'archiver';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
- const IGNORED = ['node_modules/**', '.git/**', '*.zip', '.env*'];
4
+ import { mergeIgnore } from './ignore.js';
5
5
  /**
6
6
  * 将路径列表折叠:如果一个目录下的所有文件都在列表中,则只展示目录
7
7
  */
@@ -78,9 +78,19 @@ function collapseEntries(rawEntries) {
78
78
  * 将指定目录或文件打包为 zip 文件
79
79
  * @param sourceDir 要打包的源目录
80
80
  * @param destZip 目标 zip 文件的路径
81
- * @param files 允许上传的特定文件或目录列表(可选,如果提供则仅打包这些内容)
81
+ * @param filesOrOptions 兼容旧签名 string[],或新版 PackOptions
82
+ *
83
+ * 忽略规则语义:
84
+ * - 显式 files 条目命中真实路径(archive.file / archive.directory)→ 不应用 ignore,
85
+ * 用户明确指定即表示需要。
86
+ * - glob 通配(archive.glob 含默认全量)→ 应用 mergeIgnore({ custom, ignoreBuiltin })。
82
87
  */
83
- export async function packProject(sourceDir, destZip, files) {
88
+ export async function packProject(sourceDir, destZip, filesOrOptions) {
89
+ const opts = Array.isArray(filesOrOptions)
90
+ ? { files: filesOrOptions }
91
+ : (filesOrOptions || {});
92
+ const files = opts.files;
93
+ const ignoreList = mergeIgnore({ custom: opts.ignore, ignoreBuiltin: opts.ignoreBuiltin });
84
94
  return new Promise((resolve, reject) => {
85
95
  const output = fs.createWriteStream(destZip);
86
96
  const archive = archiver('zip', {
@@ -125,12 +135,12 @@ export async function packProject(sourceDir, destZip, files) {
125
135
  }
126
136
  }
127
137
  else {
128
- archive.glob(pattern, { cwd: sourceDir, ignore: IGNORED });
138
+ archive.glob(pattern, { cwd: sourceDir, ignore: ignoreList });
129
139
  }
130
140
  });
131
141
  }
132
142
  else {
133
- archive.glob('**/*', { cwd: sourceDir, ignore: IGNORED });
143
+ archive.glob('**/*', { cwd: sourceDir, ignore: ignoreList });
134
144
  }
135
145
  archive.finalize();
136
146
  });
package/dist/serve.js CHANGED
@@ -34,6 +34,17 @@ function detectRuntime(preferred) {
34
34
  process.exit(1);
35
35
  });
36
36
  }
37
+ function isWeakAdminToken(token) {
38
+ if (typeof token !== 'string')
39
+ return { weak: true, reason: 'token 必须是字符串' };
40
+ if (token.length < 24)
41
+ return { weak: true, reason: '长度不足 24' };
42
+ if (!/[A-Za-z]/.test(token) || !/[0-9]/.test(token))
43
+ return { weak: true, reason: '需同时包含字母和数字' };
44
+ if (new Set(token).size < 8)
45
+ return { weak: true, reason: '字符多样性不足(去重 < 8)' };
46
+ return { weak: false };
47
+ }
37
48
  function ensureAdminToken() {
38
49
  const localEnv = readLocalEnv();
39
50
  if (localEnv.KITE_DEPLOY_TOKEN) {
@@ -46,7 +57,12 @@ function ensureAdminToken() {
46
57
  const content = fs.readFileSync(envPath, 'utf-8');
47
58
  const match = content.match(/^ADMIN_TOKEN=(.+)$/m);
48
59
  if (match) {
49
- return match[1].replace(/^['"]|['"]$/g, '');
60
+ const existing = match[1].replace(/^['"]|['"]$/g, '');
61
+ const check = isWeakAdminToken(existing);
62
+ if (check.weak) {
63
+ console.warn(chalk.yellow(`[warn] 当前 ADMIN_TOKEN 强度不足(${check.reason}),建议执行 kite rotate-token 或在 .env.local 中替换为长度 ≥ 24 且包含字母和数字、去重字符 ≥ 8 的随机字符串。`));
64
+ }
65
+ return existing;
50
66
  }
51
67
  }
52
68
  // Generate a new admin token
@@ -66,6 +82,14 @@ function buildServerEnv(options, adminToken) {
66
82
  KITE_SERVER_VERSION: cliPkg.version || '1.0.0',
67
83
  };
68
84
  }
85
+ function isLocalHost(host) {
86
+ return host === '127.0.0.1' || host === 'localhost' || host === '::1';
87
+ }
88
+ function warnRemoteHost(host) {
89
+ if (!isLocalHost(host)) {
90
+ console.warn(chalk.yellow(`[warn] 当前监听 host=${host},将对外网络暴露 Kite 管理端。请确保已在前置代理(Nginx/Caddy)配置 TLS 与限速,否则建议使用 --host 127.0.0.1。`));
91
+ }
92
+ }
69
93
  function startForeground(options, env, runtime) {
70
94
  const bundlePath = getServerBundlePath();
71
95
  if (!fs.existsSync(bundlePath)) {
@@ -81,6 +105,7 @@ function startForeground(options, env, runtime) {
81
105
  console.log(chalk.gray(` DB Dir: ${env.KITE_DB_DIR}`));
82
106
  console.log(chalk.yellow(` Admin Token: ${env.ADMIN_TOKEN}`));
83
107
  console.log();
108
+ warnRemoteHost(options.host);
84
109
  const args = runtime.name === 'bun' ? ['run', bundlePath] : [bundlePath];
85
110
  const child = spawn(runtime.name, args, {
86
111
  stdio: 'inherit',
@@ -181,6 +206,7 @@ function startPm2(options, env, runtime) {
181
206
  console.log(chalk.gray(' pm2 logs kite-server # View logs'));
182
207
  console.log(chalk.gray(' pm2 status # Check status'));
183
208
  console.log(chalk.gray(' kite serve --pm2 stop # Stop server'));
209
+ warnRemoteHost(options.host);
184
210
  }
185
211
  function stopPm2() {
186
212
  const result = spawnSync('pm2', ['delete', 'kite-server'], { stdio: 'inherit' });