mdldm 1.0.0 → 1.0.1

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 (3) hide show
  1. package/chat.js +48 -10
  2. package/mdldm.js +24 -20
  3. package/package.json +1 -1
package/chat.js CHANGED
@@ -6,8 +6,9 @@ const path = require('path');
6
6
 
7
7
  // ── 配置 ──────────────────────────────────────────────
8
8
  const API_BASE = 'www.mdldm.club';
9
- const VERIFY_PATH = '/api/cli/verify';
10
- const CHAT_PATH = '/api/cli/chat';
9
+ const VERIFY_PATH = '/api/cli/verify';
10
+ const CHAT_PATH = '/api/cli/chat';
11
+ const COURSES_PATH = '/api/cli/courses';
11
12
  const CONFIG_FILE = path.join(process.env.HOME || '~', '.mdldm.json');
12
13
 
13
14
  // ── ANSI ──────────────────────────────────────────────
@@ -68,11 +69,43 @@ function post(urlPath, body) {
68
69
  });
69
70
  }
70
71
 
72
+ // ── HTTP GET ─────────────────────────────────────────
73
+ function get(urlPath) {
74
+ return new Promise((resolve, reject) => {
75
+ const isLocal = API_BASE.startsWith('localhost') || API_BASE.startsWith('127.');
76
+ const mod = isLocal ? http : https;
77
+ const port = isLocal ? 3000 : 443;
78
+
79
+ const req = mod.request({
80
+ hostname: isLocal ? 'localhost' : API_BASE,
81
+ port,
82
+ path: urlPath,
83
+ method: 'GET',
84
+ }, (res) => {
85
+ let data = '';
86
+ res.on('data', d => data += d);
87
+ res.on('end', () => {
88
+ try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
89
+ catch { reject(new Error(`响应解析失败: ${data}`)); }
90
+ });
91
+ });
92
+ req.on('error', reject);
93
+ req.end();
94
+ });
95
+ }
96
+
97
+ // ── 获取课程列表 ─────────────────────────────────────
98
+ async function getCourses() {
99
+ const res = await get(COURSES_PATH);
100
+ if (res.status !== 200) throw new Error(res.body?.error || '获取课程失败');
101
+ return res.body;
102
+ }
103
+
71
104
  // ── 流式 POST(SSE)─────────────────────────────────
72
105
  // onToken(str) 每个 token 回调
73
106
  // onDone({cost, pointsLeft}) 完成回调
74
107
  // returns Promise<void>
75
- function streamPost(urlPath, body, onToken, onDone) {
108
+ function streamPost(urlPath, body, onToken, onDone, onStatus) {
76
109
  return new Promise((resolve, reject) => {
77
110
  const payload = JSON.stringify(body);
78
111
  const isLocal = API_BASE.startsWith('localhost') || API_BASE.startsWith('127.');
@@ -112,9 +145,10 @@ function streamPost(urlPath, body, onToken, onDone) {
112
145
  if (raw === '[DONE]') { resolve(); return; }
113
146
  try {
114
147
  const evt = JSON.parse(raw);
115
- if (evt.type === 'token') onToken(evt.content);
116
- if (evt.type === 'done') onDone(evt);
117
- if (evt.type === 'error') reject(new Error(evt.message));
148
+ if (evt.type === 'token') onToken(evt.content);
149
+ if (evt.type === 'done') onDone(evt);
150
+ if (evt.type === 'status' && onStatus) onStatus(evt.message);
151
+ if (evt.type === 'error') reject(new Error(evt.message));
118
152
  } catch { /* 跳过不完整 chunk */ }
119
153
  }
120
154
  });
@@ -134,10 +168,13 @@ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
134
168
  function createSpinner(msg = '麦当mdldm寻思中...') {
135
169
  const frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
136
170
  let i = 0;
171
+ let currentMsg = msg;
137
172
  const id = setInterval(() => {
138
- process.stdout.write(`\r ${CY}${frames[i++ % frames.length]}${R} ${DM}${msg}${R}`);
173
+ process.stdout.write(`\r ${CY}${frames[i++ % frames.length]}${R} ${DM}${currentMsg}${R}`);
139
174
  }, 80);
140
- return () => { clearInterval(id); process.stdout.write('\r\x1b[K'); };
175
+ const stop = () => { clearInterval(id); process.stdout.write('\r\x1b[K'); };
176
+ stop.update = (newMsg) => { currentMsg = newMsg; };
177
+ return stop;
141
178
  }
142
179
 
143
180
  // ── 流式 Markdown 渲染器(逐行渲染,兼顾流式体验)────
@@ -445,7 +482,8 @@ async function chat() {
445
482
  renderer.push(tok);
446
483
  fullReply += tok;
447
484
  },
448
- (info) => { costInfo = info; }
485
+ (info) => { costInfo = info; },
486
+ (msg) => { stopSpin.update(msg); }
449
487
  );
450
488
 
451
489
  if (!renderer) {
@@ -488,4 +526,4 @@ async function chat() {
488
526
  ask();
489
527
  }
490
528
 
491
- module.exports = { login, logout, me, chat };
529
+ module.exports = { login, logout, me, chat, getCourses, createSpinner };
package/mdldm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const data = require('./data');
4
- const { chat, login, logout, me } = require('./chat');
4
+ const { chat, login, logout, me, getCourses, createSpinner } = require('./chat');
5
5
 
6
6
  // ── ANSI ──────────────────────────────────────────────
7
7
  const R = '\x1b[0m';
@@ -51,8 +51,19 @@ function viewProfile() {
51
51
  }
52
52
 
53
53
  // ── courses 视图 ──────────────────────────────────────
54
- function viewCourses() {
55
- const { courses } = data;
54
+ async function viewCourses() {
55
+ const stopSpin = createSpinner('拉取最新课程...');
56
+ let courses;
57
+ try {
58
+ courses = await getCourses();
59
+ stopSpin();
60
+ } catch (e) {
61
+ stopSpin();
62
+ console.log(RD + `\n ✗ 获取课程失败:${e.message}\n` + R);
63
+ return;
64
+ }
65
+
66
+ const levelMap = { beginner: '入门', intermediate: '进阶', advanced: '高级' };
56
67
 
57
68
  console.log();
58
69
  console.log(B + CY + ' 麦当mdldm — 课程体系' + R);
@@ -60,26 +71,19 @@ function viewCourses() {
60
71
 
61
72
  for (const course of courses) {
62
73
  console.log();
63
- console.log(B + MG + ` 【${course.name}】` + R + ` ${course.status}`);
64
- if (course.episodes) {
65
- console.log(DM + ` 共${course.episodes}集 · ${course.duration} · 难度:${course.level}` + R);
66
- }
67
- console.log(DM + ` ${course.desc}` + R);
68
-
69
- if (course.highlights.length) {
70
- console.log();
71
- for (const h of course.highlights) {
72
- console.log(GR + ` ✓ ` + R + h);
73
- }
74
- }
74
+ const statusTag = course.status === 'published' ? GR + '● 更新中' + R : DM + '○ 已完结' + R;
75
+ console.log(B + MG + ` 【${course.name}】` + R + ' ' + statusTag);
76
+ const level = levelMap[course.level] || course.level;
77
+ console.log(DM + ` 难度:${level}` + (course.isVip ? ' ' + GR + '[VIP]' + R : '') + R);
78
+ if (course.description) console.log(DM + ` ${course.description}` + R);
75
79
 
76
- if (course.chapters.length) {
80
+ if (course.episodes?.length) {
77
81
  console.log();
78
82
  console.log(B + ' 课程目录:' + R);
79
- for (const ch of course.chapters) {
80
- const tag = ch.paid ? RD + '[VIP]' + R : GR + '[免费]' + R;
81
- const title = `EP${String(ch.ep).padStart(2, '0')} · ${ch.title}`;
82
- const dur = ch.duration ? DM + ` ${ch.duration}` + R : '';
83
+ for (const ep of course.episodes) {
84
+ const tag = ep.paid ? RD + '[VIP]' + R : GR + '[免费]' + R;
85
+ const title = `EP${String(ep.ep).padStart(2, '0')} · ${ep.title}`;
86
+ const dur = ep.duration ? DM + ` ${ep.duration}` + R : '';
83
87
  console.log(` ${tag} ${title}${dur}`);
84
88
  }
85
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdldm",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "麦当mdldm — 专注0基础教学的AI技术博主 | AI Agent 实战派",
5
5
  "bin": {
6
6
  "mdldm": "./mdldm.js"