needware-cli 1.5.21 → 1.5.23

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.
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/commands/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,UAAU,cAAc;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAoKD,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,cAAc,GACxB,MAAM,CA+YR"}
1
+ {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/commands/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,UAAU,cAAc;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,cAAc,GACxB,MAAM,CA8YR"}
@@ -1,163 +1,6 @@
1
1
  /**
2
2
  * System prompt for Agent command
3
3
  */
4
- function getDatabaseInstructions(supabase) {
5
- const hasSupabaseProject = supabase?.hasSelectedProject && supabase?.projectId && supabase?.publishableKey;
6
- if (!hasSupabaseProject && !supabase) {
7
- return `
8
- ## Database Instructions
9
-
10
- CRITICAL: Use Supabase for databases by default, unless specified otherwise. Supabase can provide login, register, database query, authorization, storage, Edge Functions etc.
11
-
12
- Supabase project setup handled separately by user!
13
- `;
14
- }
15
- if (supabase && !supabase.hasSelectedProject) {
16
- return `
17
- ## Database Instructions
18
-
19
- CRITICAL: Use Supabase for databases by default, unless specified otherwise. Supabase can provide login, register, database query, authorization, storage, Edge Functions etc.
20
-
21
- Supabase project setup handled separately by user! No project selected. Remind user to select project in chat box.
22
- `;
23
- }
24
- const envConfig = hasSupabaseProject && supabase.url
25
- ? ` with:
26
- VITE_SUPABASE_PROJECT_ID="${supabase.projectId}"
27
- VITE_SUPABASE_PUBLISHABLE_KEY="${supabase.publishableKey}"
28
- VITE_SUPABASE_URL="${supabase.url}"`
29
- : '.';
30
- return `
31
- ## Database Instructions
32
-
33
- CRITICAL: Use Supabase for databases by default, unless specified otherwise. Supabase can provide login, register, database query, authorization, storage, Edge Functions etc.
34
-
35
- Create .env file if it doesn't exist${envConfig}
36
-
37
- ### IMPORTANT: If user says "Enable Cloud" or asks to initialize Supabase:
38
-
39
- You MUST immediately perform these steps WITHOUT asking for clarification:
40
-
41
- 1. **Install Supabase client** (if not already installed):
42
- Run: \`pnpm add @supabase/supabase-js\`
43
-
44
- 2. **Create/Update .env file** with project credentials:
45
- \`\`\`
46
- VITE_SUPABASE_PROJECT_ID=${supabase.projectId}
47
- VITE_SUPABASE_PUBLISHABLE_KEY=${supabase.publishableKey}
48
- VITE_SUPABASE_URL=${supabase.url}
49
- \`\`\`
50
-
51
- 3. **Create Supabase client file** at \`src/lib/supabase.ts\`:
52
- \`\`\`typescript
53
- import { createClient } from '@supabase/supabase-js';
54
-
55
- const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
56
- const supabaseKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;
57
-
58
- if (!supabaseUrl || !supabaseKey) {
59
- throw new Error('Missing Supabase environment variables');
60
- }
61
-
62
- export const supabase = createClient(supabaseUrl, supabaseKey);
63
- \`\`\`
64
-
65
- 4. **Create migrations directory**: \`supabase/migrations/\` (if not exists)
66
-
67
- 5. **Inform user** that Supabase is now initialized and ready to use
68
-
69
- ### DATA PRESERVATION REQUIREMENTS:
70
- - DATA INTEGRITY IS HIGHEST PRIORITY - users must NEVER lose data
71
- - FORBIDDEN: Destructive operations (DROP, DELETE) that could cause data loss
72
- - FORBIDDEN: Transaction control (BEGIN, COMMIT, ROLLBACK, END)
73
- Note: DO $$ BEGIN ... END $$ blocks (PL/pgSQL) are allowed
74
-
75
- ### SQL Migrations - CRITICAL:
76
- For EVERY database change, create a migration file in /supabase/migrations/ directory:
77
-
78
- 1. Create a new .sql file with descriptive name (e.g., create_users_table.sql)
79
- 2. Write complete SQL migration content with comments
80
- 3. Include safety checks (IF EXISTS/IF NOT EXISTS)
81
- 4. Always enable RLS and add appropriate policies
82
-
83
- ### Migration Rules:
84
- - NEVER use diffs, ALWAYS provide COMPLETE file content
85
- - Create new migration file for each change in /home/project/supabase/migrations
86
- - NEVER update existing migration files
87
- - Descriptive names without number prefix (e.g., create_users.sql)
88
- - ALWAYS enable RLS: \`alter table users enable row level security;\`
89
- - Add appropriate RLS policies for CRUD operations
90
- - Use default values: DEFAULT false/true, DEFAULT 0, DEFAULT '', DEFAULT now()
91
- - Start with markdown summary in multi-line comment explaining changes
92
- - Use IF EXISTS/IF NOT EXISTS for safe operations
93
-
94
- ### Migration Example:
95
- \`\`\`sql
96
- /*
97
- # Create users table
98
- 1. New Tables: users (id uuid, email text, created_at timestamp)
99
- 2. Security: Enable RLS, add read policy for authenticated users
100
- */
101
- CREATE TABLE IF NOT EXISTS users (
102
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
103
- email text UNIQUE NOT NULL,
104
- created_at timestamptz DEFAULT now()
105
- );
106
- ALTER TABLE users ENABLE ROW LEVEL SECURITY;
107
- CREATE POLICY "Users read own data" ON users FOR SELECT TO authenticated USING (auth.uid() = id);
108
- \`\`\`
109
-
110
- ### Client Setup:
111
- - Use @supabase/supabase-js
112
- - Create singleton client instance in \`src/lib/supabase.ts\`
113
- - Use environment variables: VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY
114
- - Example client setup:
115
- \`\`\`typescript
116
- import { createClient } from '@supabase/supabase-js';
117
-
118
- const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
119
- const supabaseKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;
120
-
121
- export const supabase = createClient(supabaseUrl, supabaseKey);
122
- \`\`\`
123
-
124
- ### Authentication:
125
- - ALWAYS use email/password signup
126
- - FORBIDDEN: magic links, social providers, SSO (unless explicitly stated)
127
- - FORBIDDEN: custom auth systems, ALWAYS use Supabase's built-in auth
128
- - Email confirmation ALWAYS disabled unless stated
129
-
130
- ### User Profiles:
131
- - ALWAYS create public.profiles table linked to auth.users
132
- - Use: \`user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE UNIQUE NOT NULL\`
133
- - Include common fields: username, avatar_url, created_at, updated_at
134
- - Create trigger to auto-insert profile on user signup (INSERT INTO profiles on auth.users INSERT)
135
-
136
- ### Storage (If the user needs):
137
- - Use Supabase Storage for file uploads (images, documents, etc.)
138
- - Create buckets in migrations: \`INSERT INTO storage.buckets (id, name, public) VALUES ('avatars', 'avatars', true)\`
139
- - ALWAYS add RLS policies for storage.objects table with bucket_id filter
140
- - Common operations: .upload(), .download(), .getPublicUrl(), .remove(), .list()
141
- - Organize files with user ID prefix: '{userId}/filename' for private files
142
-
143
- ### Edge Functions (If the user needs):
144
- - Use Supabase Edge Functions for server-side logic, background tasks, webhooks, and API integrations
145
- - Create functions in /supabase/functions/<function-name>/index.ts
146
- - Use Deno runtime with TypeScript support
147
- - Import from: 'https://esm.sh/@supabase/supabase-js@2' for Supabase client
148
- - Access environment variables via Deno.env.get('VARIABLE_NAME')
149
- - Invoke from client: supabase.functions.invoke('function-name', { body: { ... } })
150
- - Common use cases: webhooks, scheduled jobs, third-party API calls, complex business logic
151
- - Return Response object: new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } })
152
-
153
- ### Security:
154
- - ALWAYS enable RLS for every new table
155
- - Create policies based on user authentication
156
- - One migration per logical change
157
- - Use descriptive policy names
158
- - Add indexes for frequently queried columns
159
- `;
160
- }
161
4
  export function getSystemPrompt(workingDir, supabase) {
162
5
  return `
163
6
  ## Working Directory Context
@@ -194,7 +37,6 @@ CRITICAL SECURITY RULES:
194
37
  - FORBIDDEN: Spawning daemon processes or background jobs
195
38
  - You may run standard development commands (pnpm, node, etc.) but NEVER manage system processes
196
39
 
197
- ${getDatabaseInstructions(supabase)}
198
40
 
199
41
  ## Project Structure
200
42
 
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/commands/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,SAAS,uBAAuB,CAAC,QAAyB;IACxD,MAAM,kBAAkB,GAAG,QAAQ,EAAE,kBAAkB,IAAI,QAAQ,EAAE,SAAS,IAAI,QAAQ,EAAE,cAAc,CAAC;IAE3G,IAAI,CAAC,kBAAkB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACrC,OAAO;;;;;;CAMV,CAAC;IACA,CAAC;IAED,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QAC7C,OAAO;;;;;;CAMV,CAAC;IACA,CAAC;IAED,MAAM,SAAS,GAAG,kBAAkB,IAAI,QAAQ,CAAC,GAAG;QAClD,CAAC,CAAC;8BACwB,QAAQ,CAAC,SAAS;mCACb,QAAQ,CAAC,cAAc;uBACnC,QAAQ,CAAC,GAAG,GAAG;QAClC,CAAC,CAAC,GAAG,CAAC;IAER,OAAO;;;;;sCAK6B,SAAS;;;;;;;;;;;8BAWjB,QAAQ,CAAC,SAAS;mCACb,QAAQ,CAAC,cAAc;uBACnC,QAAQ,CAAC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+GlC,CAAC;AACF,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,QAAyB;IAEzB,OAAO;;;+CAGsC,UAAU;;wEAEe,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BhF,uBAAuB,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA0WxB,CAAC,IAAI,EAAE,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/commands/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,QAAyB;IAEzB,OAAO;;;+CAGsC,UAAU;;wEAEe,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAuYvE,CAAC,IAAI,EAAE,CAAC;AACnB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Add Secret Tool
3
+ * Tool for adding secrets to Supabase project
4
+ */
5
+ import { BaseTool } from './base-tool.js';
6
+ import { ToolDefinition, ToolResult } from '../types/tool.js';
7
+ import { z } from 'zod';
8
+ export declare class AddSecretTool extends BaseTool {
9
+ definition: ToolDefinition;
10
+ getZodSchema(): {
11
+ placeholder: z.ZodString;
12
+ };
13
+ execute(input: Record<string, any>): Promise<ToolResult>;
14
+ }
15
+ //# sourceMappingURL=add-secret-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-secret-tool.d.ts","sourceRoot":"","sources":["../../src/tools/add-secret-tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,qBAAa,aAAc,SAAQ,QAAQ;IACzC,UAAU,EAAE,cAAc,CAaxB;IAEF,YAAY;;;IAMN,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;CA4B/D"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Add Secret Tool
3
+ * Tool for adding secrets to Supabase project
4
+ */
5
+ import { BaseTool } from './base-tool.js';
6
+ import chalk from 'chalk';
7
+ import { z } from 'zod';
8
+ export class AddSecretTool extends BaseTool {
9
+ definition = {
10
+ name: 'add-secret',
11
+ description: 'Add a secret (environment variable) to the Supabase project for use in Edge Functions',
12
+ input_schema: {
13
+ type: 'object',
14
+ properties: {
15
+ placeholder: {
16
+ type: 'string',
17
+ description: 'The placeholder of the secret (environment variable), e.g., RESEND_API_KEY',
18
+ },
19
+ },
20
+ required: ['placeholder'],
21
+ },
22
+ };
23
+ getZodSchema() {
24
+ return {
25
+ placeholder: z.string().min(1).describe('The placeholder of the secret (environment variable)'),
26
+ };
27
+ }
28
+ async execute(input) {
29
+ try {
30
+ // 验证输入参数
31
+ this.validateInput(input, ['placeholder']);
32
+ const { placeholder } = input;
33
+ console.log(chalk.blue(`\n🔐 准备添加密钥: ${placeholder}...\n`));
34
+ console.log(chalk.green(`\n✓ 密钥 "${placeholder}" 准备就绪\n`));
35
+ // 构建返回的 JSON 数据
36
+ const resultData = {
37
+ placeholder: placeholder,
38
+ secret_added: false,
39
+ secret_names: [placeholder],
40
+ requires_approval: true,
41
+ message: '密钥等待用户提供值',
42
+ instructions: `请在 Supabase 项目设置中添加此密钥,或使用 Supabase CLI: supabase secrets set ${placeholder}=your_value_here`
43
+ };
44
+ return this.successWithContent(JSON.stringify(resultData, null, 2));
45
+ }
46
+ catch (error) {
47
+ const message = error instanceof Error ? error.message : '生成密钥请求失败';
48
+ console.log(chalk.red(`\n✖ ${message}\n`));
49
+ return this.errorWithContent(message);
50
+ }
51
+ }
52
+ }
53
+ //# sourceMappingURL=add-secret-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-secret-tool.js","sourceRoot":"","sources":["../../src/tools/add-secret-tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,OAAO,aAAc,SAAQ,QAAQ;IACzC,UAAU,GAAmB;QAC3B,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,uFAAuF;QACpG,YAAY,EAAE;YACZ,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,4EAA4E;iBAC1F;aACF;YACD,QAAQ,EAAE,CAAC,aAAa,CAAC;SAC1B;KACF,CAAC;IAEF,YAAY;QACV,OAAO;YACL,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,sDAAsD,CAAC;SAChG,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAA0B;QACtC,IAAI,CAAC;YACH,SAAS;YACT,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YAE3C,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;YAE9B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,WAAW,OAAO,CAAC,CAAC,CAAC;YAE5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,WAAW,UAAU,CAAC,CAAC,CAAC;YAE3D,gBAAgB;YAChB,MAAM,UAAU,GAAG;gBACjB,WAAW,EAAE,WAAW;gBACxB,YAAY,EAAE,KAAK;gBACnB,YAAY,EAAE,CAAC,WAAW,CAAC;gBAC3B,iBAAiB,EAAE,IAAI;gBACvB,OAAO,EAAE,WAAW;gBACpB,YAAY,EAAE,iEAAiE,WAAW,kBAAkB;aAC7G,CAAC;YAEF,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;YACpE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,OAAO,IAAI,CAAC,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;CACF"}
@@ -6,5 +6,6 @@ export { SupabaseEnableTool } from './supabase-enable-tool.js';
6
6
  export { SupabaseDeployFunctionsTool } from './supabase-deploy-functions-tool.js';
7
7
  export { SupabaseExecuteSqlTool } from './supabase-execute-sql-tool.js';
8
8
  export { AiGatewayEnableTool } from './ai-gateway-enable-tool.js';
9
+ export { AddSecretTool } from './add-secret-tool.js';
9
10
  export { ToolRegistry, ToolConfig } from './tool-registry.js';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
@@ -6,5 +6,6 @@ export { SupabaseEnableTool } from './supabase-enable-tool.js';
6
6
  export { SupabaseDeployFunctionsTool } from './supabase-deploy-functions-tool.js';
7
7
  export { SupabaseExecuteSqlTool } from './supabase-execute-sql-tool.js';
8
8
  export { AiGatewayEnableTool } from './ai-gateway-enable-tool.js';
9
+ export { AddSecretTool } from './add-secret-tool.js';
9
10
  export { ToolRegistry } from './tool-registry.js';
10
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAkB,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAc,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAkB,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAc,MAAM,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAMxD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAgC;IAEpD;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IAY5C;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAIjC;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAI1C;;OAEG;IACH,MAAM,CAAC,iBAAiB,IAAI,cAAc,EAAE;IAI5C;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE;IAIvB;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC;;;;OAIG;WACU,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAQ5E;;OAEG;IACH,MAAM,CAAC,gBAAgB,IAAI,GAAG,EAAE;CAOjC"}
1
+ {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAOxD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAgC;IAEpD;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IAa5C;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAIjC;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAI1C;;OAEG;IACH,MAAM,CAAC,iBAAiB,IAAI,cAAc,EAAE;IAI5C;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE;IAIvB;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC;;;;OAIG;WACU,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAQ5E;;OAEG;IACH,MAAM,CAAC,gBAAgB,IAAI,GAAG,EAAE;CAOjC"}
@@ -6,6 +6,7 @@ import { SupabaseEnableTool } from './supabase-enable-tool.js';
6
6
  import { SupabaseDeployFunctionsTool } from './supabase-deploy-functions-tool.js';
7
7
  import { SupabaseExecuteSqlTool } from './supabase-execute-sql-tool.js';
8
8
  import { AiGatewayEnableTool } from './ai-gateway-enable-tool.js';
9
+ import { AddSecretTool } from './add-secret-tool.js';
9
10
  export class ToolRegistry {
10
11
  static tools = new Map();
11
12
  /**
@@ -21,6 +22,7 @@ export class ToolRegistry {
21
22
  this.register(new SupabaseDeployFunctionsTool(config?.gateway));
22
23
  this.register(new SupabaseExecuteSqlTool(config?.gateway));
23
24
  this.register(new AiGatewayEnableTool(config?.gateway));
25
+ this.register(new AddSecretTool(config?.gateway));
24
26
  }
25
27
  /**
26
28
  * 注册工具
@@ -1 +1 @@
1
- {"version":3,"file":"tool-registry.js","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAQlE,MAAM,OAAO,YAAY;IACf,MAAM,CAAC,KAAK,GAAsB,IAAI,GAAG,EAAE,CAAC;IAEpD;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,MAAmB;QACnC,WAAW;QACX,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAEnB,cAAc;QACd,eAAe;QACf,IAAI,CAAC,QAAQ,CAAC,IAAI,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,CAAC,IAAI,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,CAAC,IAAI,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ,CAAC,IAAI,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAU;QACxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB;QACtB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,MAAM;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,KAA0B;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,gBAAgB;QACrB,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;SAC/B,CAAC,CAAC,CAAC;IACN,CAAC;;AAGH,qCAAqC;AACrC,YAAY,CAAC,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"tool-registry.js","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAQrD,MAAM,OAAO,YAAY;IACf,MAAM,CAAC,KAAK,GAAsB,IAAI,GAAG,EAAE,CAAC;IAEpD;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,MAAmB;QACnC,WAAW;QACX,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAEnB,cAAc;QACd,eAAe;QACf,IAAI,CAAC,QAAQ,CAAC,IAAI,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,CAAC,IAAI,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,CAAC,IAAI,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ,CAAC,IAAI,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAU;QACxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB;QACtB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,MAAM;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,KAA0B;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,gBAAgB;QACrB,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;SAC/B,CAAC,CAAC,CAAC;IACN,CAAC;;AAGH,qCAAqC;AACrC,YAAY,CAAC,UAAU,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "needware-cli",
3
- "version": "1.5.21",
3
+ "version": "1.5.23",
4
4
  "description": "一个功能强大的 Node.js 命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,657 @@
1
+ ---
2
+ name: resend-integration
3
+ description: Complete Resend email integration for sending transactional emails, notifications, and alerts. REQUIRES Supabase integration as prerequisite (Resend functions run on Supabase Edge Functions). Use this skill when users mention sending emails, email notifications, transactional emails, or email alerts. 使用此技能处理邮件发送、邮件通知、事务性邮件等功能。必须先集成 Supabase 才能使用此技能。
4
+ ---
5
+
6
+ # Resend Integration
7
+
8
+ ## Before Starting - Integration Check
9
+
10
+ **CRITICAL**: Resend integration requires Supabase Edge Functions. Before proceeding, always check Supabase integration status:
11
+
12
+ **Step 1: Check Supabase Integration**
13
+
14
+ Check if Supabase is already integrated:
15
+ - Look for `src/lib/supabase.ts` file
16
+ - Check `.env` file for Supabase environment variables:
17
+ - `VITE_SUPABASE_PROJECT_ID`
18
+ - `VITE_SUPABASE_PUBLISHABLE_KEY`
19
+ - `VITE_SUPABASE_URL`
20
+ - Check for `supabase/functions/` directory
21
+
22
+ **Step 2: Handle Based on Status**
23
+
24
+ **If Supabase is already integrated** (supabase.ts exists with valid configuration):
25
+ - ✓ Proceed with Resend integration
26
+ - Inform user: "✓ Supabase is integrated. Proceeding with Resend setup."
27
+
28
+ **If Supabase is NOT integrated** (no supabase.ts or missing environment variables):
29
+ - ❌ Stop immediately
30
+ - Inform user: "⚠️ Supabase integration is required before setting up Resend. Resend email functions run on Supabase Edge Functions."
31
+ - Suggest: "Please enable Supabase first by saying 'Enable Cloud' or use the supabase-integration skill."
32
+ - Do NOT proceed with Resend setup until Supabase is properly configured
33
+
34
+ **Step 3: Verify Edge Functions Support**
35
+
36
+ Once Supabase is confirmed:
37
+ - Check if `supabase/functions/` directory exists
38
+ - If not, create the directory structure
39
+ - Proceed with Resend Edge Function creation
40
+
41
+ ---
42
+
43
+ ## Quick Start
44
+
45
+ ### When to Use This Skill
46
+
47
+ Use this skill when working with:
48
+ - Transactional emails (welcome emails, password resets)
49
+ - Email notifications (alerts, reminders)
50
+ - Batch email sending
51
+ - HTML email templates
52
+ - Email verification and confirmation
53
+ - User communication
54
+
55
+ **关键原则**:
56
+ - ✅ 使用 Resend 作为邮件发送服务
57
+ - ✅ 所有邮件功能通过 Supabase Edge Functions 实现
58
+ - ⚠️ **必须先集成 Supabase** - Resend 依赖 Supabase Edge Functions 运行
59
+
60
+ ## Initial Setup
61
+
62
+ ### Prerequisites
63
+
64
+ **Required**:
65
+ 1. ✅ **Supabase must be integrated** - Resend functions run on Supabase Edge Functions
66
+ - Verify `src/lib/supabase.ts` exists
67
+ - Verify Supabase environment variables are configured
68
+ - Verify `supabase/functions/` directory exists
69
+
70
+ 2. 🔑 **Resend API key** - If no API key is available, remind user to:
71
+ - Visit [Resend Dashboard](https://resend.com/api-keys)
72
+ - Create a new API key
73
+ - Add key to Supabase Edge Functions environment variables (`supabase/.env.local`)
74
+
75
+ ### Installation in Edge Functions
76
+
77
+ **Step 1: Import Resend in Edge Function**
78
+
79
+ Create a new Edge Function file in `/supabase/functions/<function-name>/index.ts`:
80
+
81
+ ```typescript
82
+ import { serve } from "https://deno.land/std@0.190.0/http/server.ts";
83
+ import { Resend } from "https://esm.sh/resend@2.0.0";
84
+
85
+ const resend = new Resend(Deno.env.get("RESEND_API_KEY"));
86
+ ```
87
+
88
+ **Step 2: Configure Environment Variables**
89
+
90
+ Add to `/supabase/.env.local`:
91
+
92
+ ```env
93
+ RESEND_API_KEY=re_your_api_key_here
94
+ ```
95
+
96
+ **Step 3: CORS Headers Setup**
97
+
98
+ Always include CORS headers for client-side invocation:
99
+
100
+ ```typescript
101
+ const corsHeaders = {
102
+ "Access-Control-Allow-Origin": "*",
103
+ "Access-Control-Allow-Headers":
104
+ "authorization, x-client-info, apikey, content-type",
105
+ };
106
+ ```
107
+
108
+ ## Email Sending Patterns
109
+
110
+ ### Basic Email Template
111
+
112
+ ```typescript
113
+ import { serve } from "https://deno.land/std@0.190.0/http/server.ts";
114
+ import { Resend } from "https://esm.sh/resend@2.0.0";
115
+
116
+ const resend = new Resend(Deno.env.get("RESEND_API_KEY"));
117
+
118
+ const corsHeaders = {
119
+ "Access-Control-Allow-Origin": "*",
120
+ "Access-Control-Allow-Headers":
121
+ "authorization, x-client-info, apikey, content-type",
122
+ };
123
+
124
+ interface EmailRequest {
125
+ to: string;
126
+ subject: string;
127
+ message: string;
128
+ }
129
+
130
+ const handler = async (req: Request): Promise<Response> => {
131
+ // Handle CORS preflight requests
132
+ if (req.method === "OPTIONS") {
133
+ return new Response(null, { headers: corsHeaders });
134
+ }
135
+
136
+ try {
137
+ const { to, subject, message }: EmailRequest = await req.json();
138
+
139
+ const { data, error } = await resend.emails.send({
140
+ from: "Your App <onboarding@resend.dev>",
141
+ to: [to],
142
+ subject: subject,
143
+ html: `<p>${message}</p>`,
144
+ });
145
+
146
+ if (error) {
147
+ throw new Error(error.message);
148
+ }
149
+
150
+ return new Response(
151
+ JSON.stringify({ success: true, data }),
152
+ {
153
+ status: 200,
154
+ headers: { "Content-Type": "application/json", ...corsHeaders },
155
+ }
156
+ );
157
+ } catch (error: any) {
158
+ console.error("Error sending email:", error);
159
+ return new Response(
160
+ JSON.stringify({ error: error.message }),
161
+ {
162
+ status: 500,
163
+ headers: { "Content-Type": "application/json", ...corsHeaders },
164
+ }
165
+ );
166
+ }
167
+ };
168
+
169
+ serve(handler);
170
+ ```
171
+
172
+ ### Batch Email Sending
173
+
174
+ Send emails to multiple recipients:
175
+
176
+ ```typescript
177
+ interface BatchEmailRequest {
178
+ recipients: {
179
+ name: string;
180
+ email: string;
181
+ }[];
182
+ subject: string;
183
+ message: string;
184
+ }
185
+
186
+ const handler = async (req: Request): Promise<Response> => {
187
+ if (req.method === "OPTIONS") {
188
+ return new Response(null, { headers: corsHeaders });
189
+ }
190
+
191
+ try {
192
+ const { recipients, subject, message }: BatchEmailRequest = await req.json();
193
+
194
+ if (!recipients || recipients.length === 0) {
195
+ return new Response(
196
+ JSON.stringify({ error: "No recipients provided" }),
197
+ {
198
+ status: 400,
199
+ headers: { "Content-Type": "application/json", ...corsHeaders },
200
+ }
201
+ );
202
+ }
203
+
204
+ // Send emails in parallel
205
+ const results = await Promise.all(
206
+ recipients.map(async (recipient) => {
207
+ const emailResponse = await resend.emails.send({
208
+ from: "Your App <onboarding@resend.dev>",
209
+ to: [recipient.email],
210
+ subject: subject,
211
+ html: `<p>Hi ${recipient.name},</p><p>${message}</p>`,
212
+ });
213
+ return {
214
+ recipient: recipient.email,
215
+ response: emailResponse
216
+ };
217
+ })
218
+ );
219
+
220
+ return new Response(
221
+ JSON.stringify({ success: true, results }),
222
+ {
223
+ status: 200,
224
+ headers: { "Content-Type": "application/json", ...corsHeaders },
225
+ }
226
+ );
227
+ } catch (error: any) {
228
+ return new Response(
229
+ JSON.stringify({ error: error.message }),
230
+ {
231
+ status: 500,
232
+ headers: { "Content-Type": "application/json", ...corsHeaders },
233
+ }
234
+ );
235
+ }
236
+ };
237
+ ```
238
+
239
+ ### Alert Email Example
240
+
241
+ Complete example for sending alert emails:
242
+
243
+ ```typescript
244
+ import { serve } from "https://deno.land/std@0.190.0/http/server.ts";
245
+ import { Resend } from "https://esm.sh/resend@2.0.0";
246
+
247
+ const resend = new Resend(Deno.env.get("RESEND_API_KEY"));
248
+
249
+ const corsHeaders = {
250
+ "Access-Control-Allow-Origin": "*",
251
+ "Access-Control-Allow-Headers":
252
+ "authorization, x-client-info, apikey, content-type",
253
+ };
254
+
255
+ interface AlertEmailRequest {
256
+ contacts: {
257
+ name: string;
258
+ email: string;
259
+ }[];
260
+ userName?: string;
261
+ message?: string;
262
+ }
263
+
264
+ const handler = async (req: Request): Promise<Response> => {
265
+ if (req.method === "OPTIONS") {
266
+ return new Response(null, { headers: corsHeaders });
267
+ }
268
+
269
+ try {
270
+ const { contacts, userName = "用户", message }: AlertEmailRequest = await req.json();
271
+
272
+ if (!contacts || contacts.length === 0) {
273
+ return new Response(
274
+ JSON.stringify({ error: "No contacts provided" }),
275
+ {
276
+ status: 400,
277
+ headers: { "Content-Type": "application/json", ...corsHeaders },
278
+ }
279
+ );
280
+ }
281
+
282
+ const results = await Promise.all(
283
+ contacts.map(async (contact) => {
284
+ const emailResponse = await resend.emails.send({
285
+ from: "Your App <onboarding@resend.dev>",
286
+ to: [contact.email],
287
+ subject: `⚠️ Alert: ${userName} triggered notification`,
288
+ html: `
289
+ <div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
290
+ <h1 style="color: #f59e0b; border-bottom: 2px solid #f59e0b; padding-bottom: 10px;">
291
+ ⚠️ Alert Notification
292
+ </h1>
293
+ <p>Hi ${contact.name},</p>
294
+ <p>This is an alert notification from Your App.</p>
295
+ <p><strong>${userName}</strong> ${message || "has triggered an alert notification."}</p>
296
+ <div style="background: #fef3c7; padding: 15px; border-radius: 8px; margin: 20px 0;">
297
+ <p style="margin: 0; color: #92400e;">
298
+ Please take appropriate action.
299
+ </p>
300
+ </div>
301
+ <p style="color: #666; font-size: 14px;">
302
+ This is an automated email from Your App.
303
+ </p>
304
+ </div>
305
+ `,
306
+ });
307
+ return { contact: contact.email, response: emailResponse };
308
+ })
309
+ );
310
+
311
+ console.log("Emails sent successfully:", results);
312
+
313
+ return new Response(
314
+ JSON.stringify({ success: true, results }),
315
+ {
316
+ status: 200,
317
+ headers: { "Content-Type": "application/json", ...corsHeaders },
318
+ }
319
+ );
320
+ } catch (error: any) {
321
+ console.error("Error in send-alert-email function:", error);
322
+ return new Response(
323
+ JSON.stringify({ error: error.message }),
324
+ {
325
+ status: 500,
326
+ headers: { "Content-Type": "application/json", ...corsHeaders },
327
+ }
328
+ );
329
+ }
330
+ };
331
+
332
+ serve(handler);
333
+ ```
334
+
335
+ ## Email Templates
336
+
337
+ ### Welcome Email Template
338
+
339
+ ```typescript
340
+ const welcomeEmailHTML = (userName: string) => `
341
+ <div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
342
+ <h1>Welcome to Our App!</h1>
343
+ <p>Hi ${userName},</p>
344
+ <p>Thank you for signing up. We're excited to have you on board!</p>
345
+ <a href="https://yourapp.com/get-started"
346
+ style="background: #3b82f6; color: white; padding: 12px 24px;
347
+ text-decoration: none; border-radius: 6px; display: inline-block;">
348
+ Get Started
349
+ </a>
350
+ </div>
351
+ `;
352
+ ```
353
+
354
+ ### Password Reset Template
355
+
356
+ ```typescript
357
+ const resetPasswordHTML = (resetLink: string) => `
358
+ <div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
359
+ <h1>Reset Your Password</h1>
360
+ <p>Click the button below to reset your password:</p>
361
+ <a href="${resetLink}"
362
+ style="background: #ef4444; color: white; padding: 12px 24px;
363
+ text-decoration: none; border-radius: 6px; display: inline-block;">
364
+ Reset Password
365
+ </a>
366
+ <p style="color: #666; font-size: 14px; margin-top: 20px;">
367
+ If you didn't request this, please ignore this email.
368
+ </p>
369
+ </div>
370
+ `;
371
+ ```
372
+
373
+ ### Notification Email Template
374
+
375
+ ```typescript
376
+ const notificationEmailHTML = (title: string, content: string) => `
377
+ <div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
378
+ <h2 style="color: #1f2937; border-bottom: 2px solid #e5e7eb; padding-bottom: 10px;">
379
+ ${title}
380
+ </h2>
381
+ <div style="background: #f3f4f6; padding: 15px; border-radius: 8px; margin: 20px 0;">
382
+ <p style="margin: 0; color: #374151;">${content}</p>
383
+ </div>
384
+ <p style="color: #6b7280; font-size: 14px;">
385
+ This is an automated notification from Your App.
386
+ </p>
387
+ </div>
388
+ `;
389
+ ```
390
+
391
+ ## Invoking from Client
392
+
393
+ ### Using Supabase Functions
394
+
395
+ ```typescript
396
+ // In your React/Vue/etc. component
397
+ import { supabase } from '@/lib/supabase';
398
+
399
+ // Send single email
400
+ const sendEmail = async () => {
401
+ const { data, error } = await supabase.functions.invoke('send-email', {
402
+ body: {
403
+ to: 'user@example.com',
404
+ subject: 'Hello from Your App',
405
+ message: 'This is a test email'
406
+ }
407
+ });
408
+
409
+ if (error) {
410
+ console.error('Error sending email:', error);
411
+ return;
412
+ }
413
+
414
+ console.log('Email sent:', data);
415
+ };
416
+
417
+ // Send alert emails
418
+ const sendAlertEmails = async () => {
419
+ const { data, error } = await supabase.functions.invoke('send-alert-email', {
420
+ body: {
421
+ contacts: [
422
+ { name: 'John Doe', email: 'john@example.com' },
423
+ { name: 'Jane Smith', email: 'jane@example.com' }
424
+ ],
425
+ userName: '张三',
426
+ message: '触发了紧急通知'
427
+ }
428
+ });
429
+
430
+ if (error) {
431
+ console.error('Error sending alerts:', error);
432
+ return;
433
+ }
434
+
435
+ console.log('Alerts sent:', data);
436
+ };
437
+ ```
438
+
439
+ ## Common Use Cases
440
+
441
+ ### 1. Welcome Emails
442
+ Send when user signs up:
443
+ ```typescript
444
+ const { data, error } = await supabase.functions.invoke('send-welcome-email', {
445
+ body: { email: user.email, name: user.name }
446
+ });
447
+ ```
448
+
449
+ ### 2. Password Reset
450
+ Send password reset link:
451
+ ```typescript
452
+ const { data, error } = await supabase.functions.invoke('send-password-reset', {
453
+ body: { email: user.email, resetToken: token }
454
+ });
455
+ ```
456
+
457
+ ### 3. Email Verification
458
+ Send verification code:
459
+ ```typescript
460
+ const { data, error } = await supabase.functions.invoke('send-verification', {
461
+ body: { email: user.email, code: verificationCode }
462
+ });
463
+ ```
464
+
465
+ ### 4. Transaction Confirmations
466
+ Send order/payment confirmations:
467
+ ```typescript
468
+ const { data, error } = await supabase.functions.invoke('send-confirmation', {
469
+ body: { email: user.email, orderDetails: order }
470
+ });
471
+ ```
472
+
473
+ ### 5. Scheduled Reminders
474
+ Send reminder emails:
475
+ ```typescript
476
+ const { data, error } = await supabase.functions.invoke('send-reminder', {
477
+ body: { email: user.email, reminderText: reminder }
478
+ });
479
+ ```
480
+
481
+ ## Best Practices
482
+
483
+ ### 1. Email Sender Configuration
484
+
485
+ **✅ DO**:
486
+ - Use custom domain for production: `Your App <noreply@yourdomain.com>`
487
+ - Use descriptive sender names
488
+ - Keep sender email consistent
489
+
490
+ **❌ DON'T**:
491
+ - Use `onboarding@resend.dev` in production (for testing only)
492
+ - Change sender email frequently
493
+ - Use confusing sender names
494
+
495
+ ### 2. Error Handling
496
+
497
+ Always handle errors gracefully:
498
+
499
+ ```typescript
500
+ try {
501
+ const { data, error } = await resend.emails.send({...});
502
+
503
+ if (error) {
504
+ console.error("Resend API error:", error);
505
+ throw new Error(error.message);
506
+ }
507
+
508
+ return new Response(JSON.stringify({ success: true, data }), {...});
509
+ } catch (error: any) {
510
+ console.error("Unexpected error:", error);
511
+ return new Response(
512
+ JSON.stringify({ error: error.message }),
513
+ { status: 500, headers: {...} }
514
+ );
515
+ }
516
+ ```
517
+
518
+ ### 3. Rate Limiting
519
+
520
+ Implement rate limiting for batch sends:
521
+
522
+ ```typescript
523
+ // Send emails in batches of 10
524
+ const batchSize = 10;
525
+ for (let i = 0; i < recipients.length; i += batchSize) {
526
+ const batch = recipients.slice(i, i + batchSize);
527
+ await Promise.all(batch.map(recipient => sendEmail(recipient)));
528
+ // Add delay between batches if needed
529
+ await new Promise(resolve => setTimeout(resolve, 1000));
530
+ }
531
+ ```
532
+
533
+ ### 4. HTML Email Best Practices
534
+
535
+ **✅ DO**:
536
+ - Use inline CSS styles
537
+ - Keep width under 600px
538
+ - Test on multiple email clients
539
+ - Include plain text fallback
540
+ - Use responsive design
541
+ - Add unsubscribe links (if applicable)
542
+
543
+ **❌ DON'T**:
544
+ - Use external CSS files
545
+ - Use JavaScript
546
+ - Use complex layouts
547
+ - Forget alt text for images
548
+
549
+ ### 5. Security
550
+
551
+ **✅ DO**:
552
+ - Store API key in environment variables
553
+ - Validate input data
554
+ - Sanitize user-generated content
555
+ - Use HTTPS for all links
556
+ - Implement rate limiting
557
+
558
+ **❌ DON'T**:
559
+ - Hardcode API keys
560
+ - Trust user input without validation
561
+ - Include sensitive data in emails
562
+ - Allow arbitrary email sending
563
+
564
+ ## Debugging
565
+
566
+ ### Common Issues
567
+
568
+ **1. Email not received**:
569
+ - Check spam/junk folder
570
+ - Verify recipient email address
571
+ - Check Resend Dashboard for delivery status
572
+ - Verify sender domain configuration
573
+
574
+ **2. API Key errors**:
575
+ - Verify `RESEND_API_KEY` in environment variables
576
+ - Check API key is active in Resend Dashboard
577
+ - Ensure proper Deno.env.get() usage
578
+
579
+ **3. CORS errors**:
580
+ - Verify CORS headers are included
581
+ - Handle OPTIONS requests
582
+ - Check browser console for specific errors
583
+
584
+ **4. Function timeout**:
585
+ - Reduce batch size
586
+ - Optimize email sending logic
587
+ - Check for blocking operations
588
+
589
+ ### Logging
590
+
591
+ Add comprehensive logging:
592
+
593
+ ```typescript
594
+ console.log("Sending email to:", recipient.email);
595
+ console.log("Email response:", emailResponse);
596
+ console.error("Error sending email:", error);
597
+ ```
598
+
599
+ ### Testing
600
+
601
+ Test Edge Function locally:
602
+
603
+ ```bash
604
+ supabase functions serve send-email --env-file supabase/.env.local
605
+ ```
606
+
607
+ Test with curl:
608
+
609
+ ```bash
610
+ curl -i --location --request POST 'http://localhost:54321/functions/v1/send-email' \
611
+ --header 'Authorization: Bearer YOUR_ANON_KEY' \
612
+ --header 'Content-Type: application/json' \
613
+ --data '{"to":"test@example.com","subject":"Test","message":"Hello"}'
614
+ ```
615
+
616
+ ## Project Structure
617
+
618
+ ```
619
+ project-root/
620
+ ├── supabase/
621
+ │ ├── functions/
622
+ │ │ ├── send-email/
623
+ │ │ │ └── index.ts # Basic email sending
624
+ │ │ ├── send-alert-email/
625
+ │ │ │ └── index.ts # Alert emails
626
+ │ │ ├── send-welcome-email/
627
+ │ │ │ └── index.ts # Welcome emails
628
+ │ │ └── send-notification/
629
+ │ │ └── index.ts # Notification emails
630
+ │ └── .env.local # RESEND_API_KEY
631
+ ├── src/
632
+ │ ├── lib/
633
+ │ │ └── supabase.ts # Supabase Client
634
+ │ └── components/
635
+ │ └── EmailForm.tsx # Email sending UI
636
+ └── .env # Frontend env vars
637
+ ```
638
+
639
+ ## Additional Resources
640
+
641
+ - [Resend Documentation](https://resend.com/docs)
642
+ - [Resend API Reference](https://resend.com/docs/api-reference)
643
+ - [Email Templates Best Practices](https://resend.com/docs/send-with-react)
644
+ - [Supabase Edge Functions Guide](https://supabase.com/docs/guides/functions)
645
+
646
+ ## Migration Checklist
647
+
648
+ When moving to production:
649
+
650
+ - [ ] Register custom domain in Resend Dashboard
651
+ - [ ] Update sender email from `onboarding@resend.dev` to custom domain
652
+ - [ ] Configure SPF, DKIM, and DMARC records
653
+ - [ ] Update `RESEND_API_KEY` with production key
654
+ - [ ] Test all email flows
655
+ - [ ] Set up email delivery monitoring
656
+ - [ ] Implement unsubscribe functionality (if needed)
657
+ - [ ] Add email preference management