@astro-minimax/cli 0.5.0 → 0.7.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 (174) hide show
  1. package/README.md +69 -0
  2. package/dist/commands/ai.d.ts +2 -0
  3. package/dist/commands/ai.d.ts.map +1 -0
  4. package/dist/commands/ai.js +99 -0
  5. package/dist/commands/ai.js.map +1 -0
  6. package/dist/commands/data.d.ts +2 -0
  7. package/dist/commands/data.d.ts.map +1 -0
  8. package/dist/commands/data.js +111 -0
  9. package/dist/commands/data.js.map +1 -0
  10. package/dist/commands/hooks.d.ts +2 -0
  11. package/dist/commands/hooks.d.ts.map +1 -0
  12. package/dist/commands/hooks.js +378 -0
  13. package/dist/commands/hooks.js.map +1 -0
  14. package/dist/commands/init.d.ts +2 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +50 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/podcast.d.ts +2 -0
  19. package/dist/commands/podcast.d.ts.map +1 -0
  20. package/dist/commands/podcast.js +89 -0
  21. package/dist/commands/podcast.js.map +1 -0
  22. package/dist/commands/post.d.ts +2 -0
  23. package/dist/commands/post.d.ts.map +1 -0
  24. package/dist/commands/post.js +190 -0
  25. package/dist/commands/post.js.map +1 -0
  26. package/dist/commands/profile.d.ts +2 -0
  27. package/dist/commands/profile.d.ts.map +1 -0
  28. package/dist/commands/profile.js +88 -0
  29. package/dist/commands/profile.js.map +1 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +81 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/tools/ai-process.d.ts +20 -0
  35. package/dist/tools/ai-process.d.ts.map +1 -0
  36. package/dist/tools/ai-process.js +607 -0
  37. package/dist/tools/ai-process.js.map +1 -0
  38. package/dist/tools/build-author-context.d.ts +13 -0
  39. package/dist/tools/build-author-context.d.ts.map +1 -0
  40. package/dist/tools/build-author-context.js +313 -0
  41. package/dist/tools/build-author-context.js.map +1 -0
  42. package/dist/tools/build-voice-profile.d.ts +12 -0
  43. package/dist/tools/build-voice-profile.d.ts.map +1 -0
  44. package/dist/tools/build-voice-profile.js +270 -0
  45. package/dist/tools/build-voice-profile.js.map +1 -0
  46. package/dist/tools/eval-ai-chat.d.ts +17 -0
  47. package/dist/tools/eval-ai-chat.d.ts.map +1 -0
  48. package/dist/tools/eval-ai-chat.js +362 -0
  49. package/dist/tools/eval-ai-chat.js.map +1 -0
  50. package/dist/tools/generate-author-profile.d.ts +14 -0
  51. package/dist/tools/generate-author-profile.d.ts.map +1 -0
  52. package/dist/tools/generate-author-profile.js +289 -0
  53. package/dist/tools/generate-author-profile.js.map +1 -0
  54. package/dist/tools/generate-cover.d.ts +14 -0
  55. package/dist/tools/generate-cover.d.ts.map +1 -0
  56. package/dist/tools/generate-cover.js +95 -0
  57. package/dist/tools/generate-cover.js.map +1 -0
  58. package/dist/tools/generate-og.d.ts +3 -0
  59. package/dist/tools/generate-og.d.ts.map +1 -0
  60. package/dist/tools/generate-og.js +254 -0
  61. package/dist/tools/generate-og.js.map +1 -0
  62. package/dist/tools/generate-related.d.ts +11 -0
  63. package/dist/tools/generate-related.d.ts.map +1 -0
  64. package/dist/tools/generate-related.js +124 -0
  65. package/dist/tools/generate-related.js.map +1 -0
  66. package/dist/tools/generate-tags.d.ts +14 -0
  67. package/dist/tools/generate-tags.d.ts.map +1 -0
  68. package/dist/tools/generate-tags.js +182 -0
  69. package/dist/tools/generate-tags.js.map +1 -0
  70. package/dist/tools/lib/ai-provider.d.ts +43 -0
  71. package/dist/tools/lib/ai-provider.d.ts.map +1 -0
  72. package/dist/tools/lib/ai-provider.js +146 -0
  73. package/dist/tools/lib/ai-provider.js.map +1 -0
  74. package/dist/tools/lib/audio-processor.d.ts +46 -0
  75. package/dist/tools/lib/audio-processor.d.ts.map +1 -0
  76. package/dist/tools/lib/audio-processor.js +188 -0
  77. package/dist/tools/lib/audio-processor.js.map +1 -0
  78. package/dist/tools/lib/frontmatter.d.ts +11 -0
  79. package/dist/tools/lib/frontmatter.d.ts.map +1 -0
  80. package/dist/tools/lib/frontmatter.js +80 -0
  81. package/dist/tools/lib/frontmatter.js.map +1 -0
  82. package/dist/tools/lib/index.d.ts +7 -0
  83. package/dist/tools/lib/index.d.ts.map +1 -0
  84. package/{template/tools/lib/index.ts → dist/tools/lib/index.js} +1 -0
  85. package/dist/tools/lib/index.js.map +1 -0
  86. package/dist/tools/lib/markdown.d.ts +6 -0
  87. package/dist/tools/lib/markdown.d.ts.map +1 -0
  88. package/dist/tools/lib/markdown.js +34 -0
  89. package/dist/tools/lib/markdown.js.map +1 -0
  90. package/dist/tools/lib/posts.d.ts +25 -0
  91. package/dist/tools/lib/posts.d.ts.map +1 -0
  92. package/dist/tools/lib/posts.js +63 -0
  93. package/dist/tools/lib/posts.js.map +1 -0
  94. package/dist/tools/lib/script-generator.d.ts +61 -0
  95. package/dist/tools/lib/script-generator.d.ts.map +1 -0
  96. package/dist/tools/lib/script-generator.js +182 -0
  97. package/dist/tools/lib/script-generator.js.map +1 -0
  98. package/dist/tools/lib/tts-provider.d.ts +65 -0
  99. package/dist/tools/lib/tts-provider.d.ts.map +1 -0
  100. package/dist/tools/lib/tts-provider.js +116 -0
  101. package/dist/tools/lib/tts-provider.js.map +1 -0
  102. package/dist/tools/lib/types.d.ts +129 -0
  103. package/dist/tools/lib/types.d.ts.map +1 -0
  104. package/dist/tools/lib/types.js +64 -0
  105. package/dist/tools/lib/types.js.map +1 -0
  106. package/dist/tools/lib/utils.d.ts +18 -0
  107. package/dist/tools/lib/utils.d.ts.map +1 -0
  108. package/dist/tools/lib/utils.js +121 -0
  109. package/dist/tools/lib/utils.js.map +1 -0
  110. package/dist/tools/lib/vectors.d.ts +27 -0
  111. package/dist/tools/lib/vectors.d.ts.map +1 -0
  112. package/dist/tools/lib/vectors.js +64 -0
  113. package/dist/tools/lib/vectors.js.map +1 -0
  114. package/dist/tools/podcast-feed.d.ts +6 -0
  115. package/dist/tools/podcast-feed.d.ts.map +1 -0
  116. package/dist/tools/podcast-feed.js +121 -0
  117. package/dist/tools/podcast-feed.js.map +1 -0
  118. package/dist/tools/podcast-generate.d.ts +15 -0
  119. package/dist/tools/podcast-generate.d.ts.map +1 -0
  120. package/dist/tools/podcast-generate.js +318 -0
  121. package/dist/tools/podcast-generate.js.map +1 -0
  122. package/dist/tools/podcast-list.d.ts +6 -0
  123. package/dist/tools/podcast-list.d.ts.map +1 -0
  124. package/dist/tools/podcast-list.js +66 -0
  125. package/dist/tools/podcast-list.js.map +1 -0
  126. package/dist/tools/summarize.d.ts +16 -0
  127. package/dist/tools/summarize.d.ts.map +1 -0
  128. package/dist/tools/summarize.js +108 -0
  129. package/dist/tools/summarize.js.map +1 -0
  130. package/dist/tools/translate.d.ts +13 -0
  131. package/dist/tools/translate.d.ts.map +1 -0
  132. package/dist/tools/translate.js +46 -0
  133. package/dist/tools/translate.js.map +1 -0
  134. package/dist/tools/vectorize.d.ts +13 -0
  135. package/dist/tools/vectorize.d.ts.map +1 -0
  136. package/dist/tools/vectorize.js +87 -0
  137. package/dist/tools/vectorize.js.map +1 -0
  138. package/package.json +14 -9
  139. package/template/astro.config.ts +8 -28
  140. package/template/datas/ai-seo.json +8 -0
  141. package/template/datas/ai-skip-list.json +1 -0
  142. package/template/datas/author-profile-context.json +21 -0
  143. package/template/datas/author-profile-report.json +21 -0
  144. package/template/datas/eval/gold-set.json +72 -0
  145. package/template/functions/README.md +82 -0
  146. package/template/functions/api/ai-info.ts +2 -2
  147. package/template/functions/api/chat.ts +4 -1
  148. package/template/functions/api/notify/comment.ts +140 -68
  149. package/template/functions/api/notify/debug.ts +41 -0
  150. package/template/functions/api/notify/status.ts +97 -0
  151. package/template/functions/api/notify/test-ai-chat.ts +67 -0
  152. package/template/package.json +22 -25
  153. package/template/src/config.ts +11 -0
  154. package/template/src/content.config.ts +29 -16
  155. package/template/src/env.d.ts +0 -5
  156. package/index.js +0 -36
  157. package/template/tools/README.md +0 -169
  158. package/template/tools/ai-process.ts +0 -816
  159. package/template/tools/build-author-context.ts +0 -405
  160. package/template/tools/build-voice-profile.ts +0 -322
  161. package/template/tools/generate-author-profile.ts +0 -369
  162. package/template/tools/generate-cover.ts +0 -123
  163. package/template/tools/generate-og.ts +0 -280
  164. package/template/tools/generate-related.ts +0 -146
  165. package/template/tools/generate-tags.ts +0 -251
  166. package/template/tools/lib/ai-provider.ts +0 -240
  167. package/template/tools/lib/frontmatter.ts +0 -94
  168. package/template/tools/lib/markdown.ts +0 -40
  169. package/template/tools/lib/posts.ts +0 -89
  170. package/template/tools/lib/utils.ts +0 -138
  171. package/template/tools/lib/vectors.ts +0 -96
  172. package/template/tools/summarize.ts +0 -142
  173. package/template/tools/translate.ts +0 -60
  174. package/template/tools/vectorize.ts +0 -105
@@ -1,22 +1,23 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
- import { createNotifier } from '@astro-minimax/notify';
2
+ import { getNotifier } from '@astro-minimax/notify';
3
3
 
4
4
  interface WalineComment {
5
- objectId?: string;
6
- url: string;
7
- nick: string;
8
- mail: string;
5
+ objectId?: string | number;
6
+ url?: string;
7
+ nick?: string;
8
+ mail?: string;
9
9
  link?: string;
10
- comment: string;
10
+ comment?: string;
11
+ rawComment?: string;
11
12
  ip?: string;
12
13
  ua?: string;
13
14
  insertedAt?: string;
15
+ createdAt?: string;
16
+ updatedAt?: string;
14
17
  status?: string;
15
- }
16
-
17
- interface WalineWebhookPayload {
18
- type: 'new_comment' | 'new_reply';
19
- data: WalineComment;
18
+ type?: string;
19
+ user_id?: number;
20
+ rid?: string | number;
20
21
  }
21
22
 
22
23
  interface FunctionEnv {
@@ -33,77 +34,148 @@ interface FunctionEnv {
33
34
  export const onRequest: PagesFunction<FunctionEnv> = async (context) => {
34
35
  const { env, request } = context;
35
36
 
36
- if (!env.NOTIFY_TELEGRAM_BOT_TOKEN && !env.NOTIFY_WEBHOOK_URL && !env.NOTIFY_RESEND_API_KEY) {
37
- return new Response(JSON.stringify({ error: 'No notification providers configured' }), {
38
- status: 400,
39
- headers: { 'Content-Type': 'application/json' },
37
+ try {
38
+ if (!env.NOTIFY_TELEGRAM_BOT_TOKEN && !env.NOTIFY_WEBHOOK_URL && !env.NOTIFY_RESEND_API_KEY) {
39
+ console.warn('[notify/comment] No providers configured');
40
+ return jsonError('No notification providers configured', 400);
41
+ }
42
+
43
+ let rawData: unknown;
44
+ try {
45
+ rawData = await request.json();
46
+ } catch (parseError) {
47
+ console.error('[notify/comment] Failed to parse JSON:', parseError);
48
+ return jsonError('Invalid JSON payload', 400);
49
+ }
50
+
51
+ console.log('[notify/comment] Raw payload:', JSON.stringify(rawData).slice(0, 500));
52
+
53
+ const { commentData, eventType } = parseWalinePayload(rawData);
54
+
55
+ if (!commentData) {
56
+ console.error('[notify/comment] Could not extract comment data from payload');
57
+ return jsonError('Invalid payload structure', 400);
58
+ }
59
+
60
+ const siteUrl = env.SITE_URL || 'https://your-blog.pages.dev';
61
+ const urlPath = getStringValue(commentData.url) || '';
62
+ const postUrl = urlPath.startsWith('http')
63
+ ? urlPath
64
+ : `${siteUrl}${urlPath}`;
65
+
66
+ const author = getStringValue(commentData.nick) || '匿名用户';
67
+ const content = getStringValue(commentData.rawComment) || getStringValue(commentData.comment) || '';
68
+ const postTitle = extractPostTitle(urlPath);
69
+
70
+ console.log('[notify/comment] Processing:', { urlPath, author, postTitle });
71
+
72
+ const notifier = getNotifier({
73
+ telegram: env.NOTIFY_TELEGRAM_BOT_TOKEN && env.NOTIFY_TELEGRAM_CHAT_ID ? {
74
+ botToken: env.NOTIFY_TELEGRAM_BOT_TOKEN,
75
+ chatId: env.NOTIFY_TELEGRAM_CHAT_ID,
76
+ } : undefined,
77
+ webhook: env.NOTIFY_WEBHOOK_URL ? {
78
+ url: env.NOTIFY_WEBHOOK_URL,
79
+ } : undefined,
80
+ email: env.NOTIFY_RESEND_API_KEY && env.NOTIFY_RESEND_FROM && env.NOTIFY_RESEND_TO ? {
81
+ provider: 'resend',
82
+ apiKey: env.NOTIFY_RESEND_API_KEY,
83
+ from: env.NOTIFY_RESEND_FROM,
84
+ to: env.NOTIFY_RESEND_TO,
85
+ } : undefined,
40
86
  });
41
- }
42
87
 
43
- let payload: WalineWebhookPayload;
44
- try {
45
- payload = await request.json() as WalineWebhookPayload;
46
- } catch {
47
- return new Response(JSON.stringify({ error: 'Invalid JSON payload' }), {
48
- status: 400,
49
- headers: { 'Content-Type': 'application/json' },
88
+ const result = await notifier.comment({
89
+ author,
90
+ content,
91
+ postTitle,
92
+ postUrl,
50
93
  });
51
- }
52
94
 
53
- if (payload.type !== 'new_comment' && payload.type !== 'new_reply') {
54
- return new Response(JSON.stringify({ error: 'Unsupported event type' }), {
55
- status: 400,
95
+ console.log('[notify/comment] Result:', { success: result.success, channels: result.results.length });
96
+
97
+ return new Response(JSON.stringify({
98
+ success: result.success,
99
+ event: eventType,
100
+ channels: result.results.map(r => ({
101
+ channel: r.channel,
102
+ success: r.success,
103
+ })),
104
+ }), {
105
+ status: 200,
56
106
  headers: { 'Content-Type': 'application/json' },
57
107
  });
108
+ } catch (error) {
109
+ console.error('[notify/comment] Unexpected error:', error);
110
+ return jsonError(
111
+ error instanceof Error ? error.message : 'Unknown error',
112
+ 500
113
+ );
58
114
  }
115
+ };
59
116
 
60
- const { data } = payload;
61
- const siteUrl = env.SITE_URL || 'https://your-blog.pages.dev';
62
-
63
- const postUrl = data.url.startsWith('http')
64
- ? data.url
65
- : `${siteUrl}${data.url}`;
66
-
67
- const notifier = createNotifier({
68
- telegram: env.NOTIFY_TELEGRAM_BOT_TOKEN && env.NOTIFY_TELEGRAM_CHAT_ID ? {
69
- botToken: env.NOTIFY_TELEGRAM_BOT_TOKEN,
70
- chatId: env.NOTIFY_TELEGRAM_CHAT_ID,
71
- } : undefined,
72
- webhook: env.NOTIFY_WEBHOOK_URL ? {
73
- url: env.NOTIFY_WEBHOOK_URL,
74
- } : undefined,
75
- email: env.NOTIFY_RESEND_API_KEY && env.NOTIFY_RESEND_FROM && env.NOTIFY_RESEND_TO ? {
76
- provider: 'resend',
77
- apiKey: env.NOTIFY_RESEND_API_KEY,
78
- from: env.NOTIFY_RESEND_FROM,
79
- to: env.NOTIFY_RESEND_TO,
80
- } : undefined,
81
- });
117
+ function parseWalinePayload(raw: unknown): { commentData: WalineComment | null; eventType: string } {
118
+ if (!raw || typeof raw !== 'object') {
119
+ return { commentData: null, eventType: 'unknown' };
120
+ }
82
121
 
83
- const result = await notifier.comment({
84
- author: data.nick,
85
- content: data.comment,
86
- postTitle: extractPostTitle(data.url),
87
- postUrl,
88
- });
122
+ const data = raw as Record<string, unknown>;
123
+
124
+ // Waline format: { type: 'new_comment', data: { comment: {...} } }
125
+ if (data.type === 'new_comment' || data.type === 'new_reply') {
126
+ const dataObj = data.data as Record<string, unknown> | undefined;
127
+ // Check if data contains a 'comment' field (Waline's nested structure)
128
+ if (dataObj && dataObj.comment && typeof dataObj.comment === 'object') {
129
+ return {
130
+ commentData: dataObj.comment as WalineComment,
131
+ eventType: data.type as string
132
+ };
133
+ }
134
+ // Fallback: data is the comment object directly
135
+ return {
136
+ commentData: dataObj as WalineComment,
137
+ eventType: data.type as string
138
+ };
139
+ }
140
+
141
+ // Direct comment object format
142
+ if (data.url || data.nick || data.comment || data.mail) {
143
+ const type = data.rid ? 'new_reply' : 'new_comment';
144
+ return {
145
+ commentData: data as WalineComment,
146
+ eventType: type
147
+ };
148
+ }
89
149
 
90
- return new Response(JSON.stringify({
91
- success: result.success,
92
- event: 'comment',
93
- channels: result.results.map(r => ({
94
- channel: r.channel,
95
- success: r.success,
96
- })),
97
- }), {
98
- status: 200,
150
+ return { commentData: null, eventType: 'unknown' };
151
+ }
152
+
153
+ function getStringValue(value: unknown): string | null {
154
+ if (value === null || value === undefined) return null;
155
+ if (typeof value === 'string') return value;
156
+ return String(value);
157
+ }
158
+
159
+ function jsonError(message: string, status: number): Response {
160
+ return new Response(JSON.stringify({ error: message }), {
161
+ status,
99
162
  headers: { 'Content-Type': 'application/json' },
100
163
  });
101
- };
164
+ }
102
165
 
103
166
  function extractPostTitle(url: string): string {
104
- const match = url.match(/\/posts\/([^/]+)/);
105
- if (match) {
106
- return decodeURIComponent(match[1].replace(/-/g, ' '));
167
+ if (!url || typeof url !== 'string') {
168
+ return '博客文章';
169
+ }
170
+
171
+ // Match /posts/xxx, /zh/posts/xxx, or /post/xxx formats
172
+ const match = url.match(/\/(?:[a-z]{2}\/)?posts?\/([^/]+)/);
173
+ if (match && match[1]) {
174
+ try {
175
+ return decodeURIComponent(match[1].replace(/-/g, ' '));
176
+ } catch {
177
+ return match[1].replace(/-/g, ' ');
178
+ }
107
179
  }
108
180
  return '博客文章';
109
181
  }
@@ -0,0 +1,41 @@
1
+ /// <reference types="@cloudflare/workers-types" />
2
+
3
+ interface DebugEnv {
4
+ [key: string]: unknown;
5
+ }
6
+
7
+ export const onRequest: PagesFunction<DebugEnv> = async (context) => {
8
+ const { request } = context;
9
+
10
+ const headers: Record<string, string> = {};
11
+ request.headers.forEach((value, key) => {
12
+ headers[key] = value;
13
+ });
14
+
15
+ let body = null;
16
+ let bodyError = null;
17
+ try {
18
+ body = await request.json();
19
+ } catch (e) {
20
+ bodyError = e instanceof Error ? e.message : String(e);
21
+ }
22
+
23
+ const debug = {
24
+ timestamp: new Date().toISOString(),
25
+ method: request.method,
26
+ url: request.url,
27
+ headers,
28
+ body,
29
+ bodyError,
30
+ };
31
+
32
+ console.log('[notify/debug] Request received:', JSON.stringify(debug, null, 2));
33
+
34
+ return new Response(JSON.stringify(debug, null, 2), {
35
+ status: 200,
36
+ headers: {
37
+ 'Content-Type': 'application/json',
38
+ 'Access-Control-Allow-Origin': '*',
39
+ },
40
+ });
41
+ };
@@ -0,0 +1,97 @@
1
+ /// <reference types="@cloudflare/workers-types" />
2
+
3
+ interface NotifyStatusEnv {
4
+ NOTIFY_TELEGRAM_BOT_TOKEN?: string;
5
+ NOTIFY_TELEGRAM_CHAT_ID?: string;
6
+ NOTIFY_WEBHOOK_URL?: string;
7
+ NOTIFY_RESEND_API_KEY?: string;
8
+ NOTIFY_RESEND_FROM?: string;
9
+ NOTIFY_RESEND_TO?: string;
10
+ SITE_URL?: string;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ interface ProviderStatus {
15
+ configured: boolean;
16
+ missingFields?: string[];
17
+ }
18
+
19
+ interface NotifyStatusResponse {
20
+ timestamp: string;
21
+ environment: 'production' | 'preview' | 'unknown';
22
+ providers: {
23
+ telegram: ProviderStatus;
24
+ email: ProviderStatus;
25
+ webhook: ProviderStatus;
26
+ };
27
+ siteUrl: {
28
+ configured: boolean;
29
+ value?: string;
30
+ };
31
+ summary: {
32
+ totalProviders: number;
33
+ configuredProviders: number;
34
+ hasAnyProvider: boolean;
35
+ };
36
+ }
37
+
38
+ export const onRequest: PagesFunction<NotifyStatusEnv> = async (context) => {
39
+ const env = context.env;
40
+
41
+ const telegramMissing = [
42
+ !env.NOTIFY_TELEGRAM_BOT_TOKEN && 'NOTIFY_TELEGRAM_BOT_TOKEN',
43
+ !env.NOTIFY_TELEGRAM_CHAT_ID && 'NOTIFY_TELEGRAM_CHAT_ID',
44
+ ].filter(Boolean) as string[];
45
+
46
+ const emailMissing = [
47
+ !env.NOTIFY_RESEND_API_KEY && 'NOTIFY_RESEND_API_KEY',
48
+ !env.NOTIFY_RESEND_FROM && 'NOTIFY_RESEND_FROM',
49
+ !env.NOTIFY_RESEND_TO && 'NOTIFY_RESEND_TO',
50
+ ].filter(Boolean) as string[];
51
+
52
+ const webhookMissing = [
53
+ !env.NOTIFY_WEBHOOK_URL && 'NOTIFY_WEBHOOK_URL',
54
+ ].filter(Boolean) as string[];
55
+
56
+ const environment = context.request.headers.get('x-cf-env') || 'unknown';
57
+
58
+ const providers = {
59
+ telegram: {
60
+ configured: telegramMissing.length === 0,
61
+ missingFields: telegramMissing.length > 0 ? telegramMissing : undefined,
62
+ },
63
+ email: {
64
+ configured: emailMissing.length === 0,
65
+ missingFields: emailMissing.length > 0 ? emailMissing : undefined,
66
+ },
67
+ webhook: {
68
+ configured: webhookMissing.length === 0,
69
+ missingFields: webhookMissing.length > 0 ? webhookMissing : undefined,
70
+ },
71
+ };
72
+
73
+ const configuredProviders = Object.values(providers).filter(p => p.configured).length;
74
+
75
+ const response: NotifyStatusResponse = {
76
+ timestamp: new Date().toISOString(),
77
+ environment: environment as 'production' | 'preview' | 'unknown',
78
+ providers,
79
+ siteUrl: {
80
+ configured: !!env.SITE_URL,
81
+ value: env.SITE_URL ? '[CONFIGURED]' : undefined,
82
+ },
83
+ summary: {
84
+ totalProviders: 3,
85
+ configuredProviders,
86
+ hasAnyProvider: configuredProviders > 0,
87
+ },
88
+ };
89
+
90
+ return new Response(JSON.stringify(response, null, 2), {
91
+ status: 200,
92
+ headers: {
93
+ 'Content-Type': 'application/json',
94
+ 'Cache-Control': 'no-store, no-cache, must-revalidate',
95
+ },
96
+ });
97
+ };
@@ -0,0 +1,67 @@
1
+ /// <reference types="@cloudflare/workers-types" />
2
+
3
+ import { notifyAiChat } from '@astro-minimax/ai/server';
4
+
5
+ interface TestEnv {
6
+ NOTIFY_TELEGRAM_BOT_TOKEN?: string;
7
+ NOTIFY_TELEGRAM_CHAT_ID?: string;
8
+ NOTIFY_WEBHOOK_URL?: string;
9
+ NOTIFY_RESEND_API_KEY?: string;
10
+ NOTIFY_RESEND_FROM?: string;
11
+ NOTIFY_RESEND_TO?: string;
12
+ SITE_URL?: string;
13
+ [key: string]: unknown;
14
+ }
15
+
16
+ export const onRequest: PagesFunction<TestEnv> = async (context) => {
17
+ const env = context.env;
18
+
19
+ const result = await notifyAiChat({
20
+ env,
21
+ sessionId: `test-${Date.now()}`,
22
+ messages: [
23
+ { id: '1', role: 'user', parts: [{ type: 'text', text: '这是一条测试消息' }] }
24
+ ],
25
+ aiResponse: '这是一条测试 AI 回复',
26
+ referencedArticles: [
27
+ { title: '测试文章', url: '/posts/test' }
28
+ ],
29
+ model: {
30
+ name: 'test-model',
31
+ provider: 'test',
32
+ },
33
+ usage: {
34
+ total: 150,
35
+ input: 100,
36
+ output: 50,
37
+ },
38
+ timing: {
39
+ total: 1000,
40
+ keywordExtraction: 100,
41
+ search: 200,
42
+ evidenceAnalysis: 100,
43
+ generation: 600,
44
+ },
45
+ });
46
+
47
+ return new Response(JSON.stringify({
48
+ success: !!result,
49
+ result: result ? {
50
+ event: result.event,
51
+ success: result.success,
52
+ channels: result.results.map(r => ({
53
+ channel: r.channel,
54
+ success: r.success,
55
+ error: r.error,
56
+ })),
57
+ } : null,
58
+ envCheck: {
59
+ hasTelegram: !!env.NOTIFY_TELEGRAM_BOT_TOKEN,
60
+ hasChatId: !!env.NOTIFY_TELEGRAM_CHAT_ID,
61
+ hasResend: !!env.NOTIFY_RESEND_API_KEY,
62
+ },
63
+ }, null, 2), {
64
+ status: 200,
65
+ headers: { 'Content-Type': 'application/json' },
66
+ });
67
+ };
@@ -5,29 +5,32 @@
5
5
  "scripts": {
6
6
  "dev": "astro dev",
7
7
  "dev:ai": "pnpm exec astro-ai-dev",
8
- "dev:full": "concurrently -n \"astro,ai\" -c \"blue,green\" \"astro dev\" \"pnpm exec astro-ai-dev\"",
9
8
  "build": "astro check && astro build && pagefind --site dist && cp -r dist/pagefind public/",
10
9
  "preview": "wrangler pages dev dist",
11
10
  "preview:astro": "astro preview",
12
11
  "lint": "eslint .",
13
- "tools:summarize": "tsx tools/summarize.ts",
14
- "tools:generate-related": "tsx tools/generate-related.ts",
15
- "tools:translate": "tsx tools/translate.ts",
16
- "tools:vectorize": "tsx tools/vectorize.ts",
17
- "tools:cover": "tsx tools/generate-cover.ts",
18
- "tools:tags": "tsx tools/generate-tags.ts",
19
- "ai:process": "tsx tools/ai-process.ts",
20
- "context:build": "tsx tools/build-author-context.ts",
21
- "voice:build": "tsx tools/build-voice-profile.ts",
22
- "profile:generate": "tsx tools/generate-author-profile.ts"
12
+ "cli": "astro-minimax",
13
+ "ai:process": "astro-minimax ai process",
14
+ "ai:seo": "astro-minimax ai seo",
15
+ "ai:summary": "astro-minimax ai summary",
16
+ "ai:eval": "astro-minimax ai eval",
17
+ "profile:build": "astro-minimax profile build",
18
+ "profile:context": "astro-minimax profile context",
19
+ "profile:voice": "astro-minimax profile voice",
20
+ "profile:report": "astro-minimax profile report",
21
+ "post:new": "astro-minimax post new",
22
+ "post:list": "astro-minimax post list",
23
+ "post:stats": "astro-minimax post stats",
24
+ "data:status": "astro-minimax data status",
25
+ "data:clear": "astro-minimax data clear"
23
26
  },
24
27
  "dependencies": {
25
28
  "@ai-sdk/openai-compatible": "^2.0.35",
26
29
  "@ai-sdk/react": "^3.0.118",
27
- "@astro-minimax/ai": "^0.1.0",
28
- "@astro-minimax/core": "^0.1.0",
29
- "@astro-minimax/notify": "^0.1.0",
30
- "@astro-minimax/viz": "^0.1.4",
30
+ "@astro-minimax/ai": "^0.5.0",
31
+ "@astro-minimax/core": "^0.5.0",
32
+ "@astro-minimax/notify": "^0.5.0",
33
+ "@astro-minimax/viz": "^0.5.0",
31
34
  "@astrojs/mdx": "^5.0.0",
32
35
  "@astrojs/preact": "^5.0.0",
33
36
  "@astrojs/rss": "^4.0.17",
@@ -44,34 +47,28 @@
44
47
  "mermaid": "^11.12.3",
45
48
  "preact": "^10.29.0",
46
49
  "rehype-katex": "^7.0.1",
47
- "remark-collapse": "^0.1.2",
48
50
  "remark-emoji": "^5.0.2",
49
51
  "remark-github-alerts": "^0.1.1",
50
52
  "remark-markmap": "^1.1.5",
51
53
  "remark-math": "^6.0.0",
52
- "remark-toc": "^9.0.0",
53
54
  "satori": "^0.18.0",
54
55
  "sharp": "^0.34.5",
55
56
  "slugify": "^1.6.6",
56
57
  "tailwindcss": "^4.2.1"
57
58
  },
58
59
  "devDependencies": {
60
+ "@astro-minimax/cli": "^0.6.0",
59
61
  "@astrojs/check": "^0.9.6",
60
62
  "@cloudflare/workers-types": "^4.20260313.1",
61
63
  "@pagefind/default-ui": "^1.4.0",
62
- "@shikijs/transformers": "^4.0.0",
64
+ "@shikijs/transformers": "^4.0.2",
63
65
  "@types/lodash.kebabcase": "^4.1.9",
64
- "@types/mdast": "^4.0.4",
65
- "concurrently": "^9.1.2",
66
66
  "eslint": "^9.39.3",
67
67
  "eslint-plugin-astro": "^1.6.0",
68
- "globals": "^16.2.0",
68
+ "globals": "^16.5.0",
69
69
  "pagefind": "^1.4.0",
70
- "tsx": "^4.21.0",
71
70
  "typescript": "^5.9.3",
72
- "typescript-eslint": "^8.39.0",
73
- "undici": "^7.24.1",
74
- "unist-util-visit": "^5.1.0",
71
+ "typescript-eslint": "^8.56.1",
75
72
  "wrangler": "^4.73.0"
76
73
  }
77
74
  }
@@ -87,6 +87,17 @@ export const SITE: SiteConfig = {
87
87
  // src: "https://your-umami-instance.com/script.js",
88
88
  // },
89
89
 
90
+ // Search provider: 'pagefind' (default, static) or 'docsearch' (Algolia)
91
+ // search: {
92
+ // provider: 'docsearch',
93
+ // docsearch: {
94
+ // appId: 'YOUR_ALGOLIA_APP_ID',
95
+ // apiKey: 'YOUR_ALGOLIA_SEARCH_API_KEY',
96
+ // indexName: 'YOUR_INDEX_NAME',
97
+ // placeholder: 'Search docs...',
98
+ // },
99
+ // },
100
+
90
101
  copyright: {
91
102
  license: "CC BY-NC-SA 4.0",
92
103
  licenseUrl: "https://creativecommons.org/licenses/by-nc-sa/4.0/",
@@ -1,27 +1,40 @@
1
1
  import { defineCollection } from "astro:content";
2
2
  import { z } from "astro/zod";
3
3
  import { glob } from "astro/loaders";
4
+ import { SITE } from "@/config";
5
+
6
+ export const BLOG_PATH = "src/data/blog";
4
7
 
5
8
  const blog = defineCollection({
6
9
  loader: glob({
7
10
  pattern: "**/[^_]*.{md,mdx}",
8
- base: "./src/data/blog",
9
- generateId: ({ entry }) => entry.replace(/\.(md|mdx)$/, ""),
10
- }),
11
- schema: z.object({
12
- title: z.string(),
13
- description: z.string(),
14
- pubDatetime: z.date(),
15
- modDatetime: z.date().optional().nullable(),
16
- author: z.string().optional(),
17
- tags: z.array(z.string()).default(["others"]),
18
- category: z.string().optional(),
19
- series: z.object({ name: z.string(), order: z.number() }).optional(),
20
- featured: z.boolean().optional(),
21
- draft: z.boolean().optional(),
22
- ogImage: z.string().optional(),
23
- canonicalURL: z.string().optional(),
11
+ base: `./${BLOG_PATH}`,
12
+ generateId: ({ entry }) => {
13
+ return entry.replace(/\.(md|mdx)$/, "");
14
+ },
24
15
  }),
16
+ schema: ({ image }) =>
17
+ z.object({
18
+ author: z.string().default(SITE.author),
19
+ pubDatetime: z.date(),
20
+ modDatetime: z.date().optional().nullable(),
21
+ title: z.string(),
22
+ featured: z.boolean().optional(),
23
+ draft: z.boolean().optional(),
24
+ tags: z.array(z.string()).default(["others"]),
25
+ category: z.string().optional(),
26
+ series: z
27
+ .object({
28
+ name: z.string(),
29
+ order: z.number(),
30
+ })
31
+ .optional(),
32
+ ogImage: image().or(z.string()).optional(),
33
+ description: z.string(),
34
+ canonicalURL: z.string().optional(),
35
+ hideEditPost: z.boolean().optional(),
36
+ timezone: z.string().optional(),
37
+ }),
25
38
  });
26
39
 
27
40
  export const collections = { blog };
@@ -1,9 +1,4 @@
1
1
  /// <reference types="astro/content" />
2
- declare module "remark-collapse" {
3
- import type { Plugin } from "unified";
4
- const plugin: Plugin;
5
- export default plugin;
6
- }
7
2
  declare module "remark-github-alerts" {
8
3
  import type { Plugin } from "unified";
9
4
  const plugin: Plugin;
package/index.js DELETED
@@ -1,36 +0,0 @@
1
- #!/usr/bin/env node
2
- import { cpSync, mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
3
- import { join, resolve, basename } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const __dirname = fileURLToPath(new URL(".", import.meta.url));
7
- const templateDir = join(__dirname, "template");
8
-
9
- const projectName = process.argv[2];
10
- if (!projectName) {
11
- console.error("Usage: astro-minimax <project-name>");
12
- console.error(" npx @astro-minimax/cli <project-name>");
13
- process.exit(1);
14
- }
15
-
16
- const targetDir = resolve(process.cwd(), projectName);
17
-
18
- if (existsSync(targetDir)) {
19
- console.error(`Directory "${projectName}" already exists.`);
20
- process.exit(1);
21
- }
22
-
23
- mkdirSync(targetDir, { recursive: true });
24
- cpSync(templateDir, targetDir, { recursive: true });
25
-
26
- const pkgPath = join(targetDir, "package.json");
27
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
28
- pkg.name = basename(targetDir);
29
- writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
30
-
31
- console.log(`\n ✅ Created "${projectName}" successfully!\n`);
32
- console.log(" Next steps:\n");
33
- console.log(` cd ${projectName}`);
34
- console.log(" pnpm install");
35
- console.log(" pnpm dev\n");
36
- console.log(" Docs: https://github.com/souloss/astro-minimax\n");