@framers/agentos-ext-cloud-vercel 0.1.0 → 0.1.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.
package/manifest.json CHANGED
@@ -2,12 +2,12 @@
2
2
  "$schema": "https://agentos.sh/schemas/extension-manifest-v1.json",
3
3
  "id": "com.framers.cloud.vercel",
4
4
  "name": "Vercel Cloud Deployment",
5
- "version": "0.1.0",
5
+ "version": "0.1.1",
6
6
  "description": "Deploy projects to Vercel, manage deployments, configure custom domains, and set environment variables. Supports Next.js, React, static sites, and serverless functions.",
7
7
  "author": {
8
8
  "name": "Frame.dev",
9
9
  "email": "support@frame.dev",
10
- "url": "https://github.com/framersai"
10
+ "url": "https://github.com/framerslab"
11
11
  },
12
12
  "license": "MIT",
13
13
  "keywords": ["cloud", "vercel", "deploy", "hosting", "nextjs", "serverless"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@framers/agentos-ext-cloud-vercel",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Vercel cloud deployment extension — deploy projects, manage domains, and configure environment variables via the Vercel REST API.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,26 +37,34 @@
37
37
  "license": "MIT",
38
38
  "repository": {
39
39
  "type": "git",
40
- "url": "https://github.com/framersai/agentos-extensions.git",
40
+ "url": "git+https://github.com/framerslab/agentos-extensions.git",
41
41
  "directory": "registry/curated/cloud/vercel"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public"
45
45
  },
46
46
  "peerDependencies": {
47
- "@framers/agentos": "^0.5.2"
47
+ "@framers/agentos": ">=0.10.40"
48
48
  },
49
49
  "devDependencies": {
50
- "@framers/agentos": "^0.6.6",
51
50
  "@types/node": "^20.12.12",
52
51
  "rimraf": "^5.0.7",
53
52
  "typescript": "^5.4.5",
54
- "vitest": "^1.6.0"
53
+ "vitest": "^3.2.7",
54
+ "@framers/agentos": "^0.10.40"
55
55
  },
56
+ "homepage": "https://github.com/framerslab/agentos-extensions/tree/master/registry/curated/cloud/vercel#readme",
57
+ "bugs": {
58
+ "url": "https://github.com/framerslab/agentos-extensions/issues"
59
+ },
60
+ "files": [
61
+ "dist",
62
+ "manifest.json"
63
+ ],
56
64
  "scripts": {
57
65
  "build": "tsc",
58
66
  "dev": "tsc --watch",
59
- "test": "vitest run",
67
+ "test": "vitest run --passWithNoTests",
60
68
  "test:watch": "vitest",
61
69
  "typecheck": "tsc --noEmit",
62
70
  "clean": "rimraf dist"
@@ -1,408 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview Vercel REST API service layer.
4
- *
5
- * Wraps the Vercel API v13 for project deployment, domain management,
6
- * environment variable configuration, and deployment status tracking.
7
- */
8
-
9
- // ---------------------------------------------------------------------------
10
- // Types
11
- // ---------------------------------------------------------------------------
12
-
13
- export interface VercelConfig {
14
- /** Vercel API token (from https://vercel.com/account/tokens) */
15
- token: string;
16
- /** Team ID / scope (optional — omit for personal account) */
17
- teamId?: string;
18
- /** API base URL (defaults to https://api.vercel.com) */
19
- baseUrl?: string;
20
- }
21
-
22
- export interface VercelProject {
23
- id: string;
24
- name: string;
25
- framework: string | null;
26
- createdAt: number;
27
- updatedAt: number;
28
- latestDeployments?: VercelDeployment[];
29
- link?: { type: string; repo: string; org: string };
30
- }
31
-
32
- export interface VercelDeployment {
33
- uid: string;
34
- name: string;
35
- url: string;
36
- state: 'BUILDING' | 'ERROR' | 'INITIALIZING' | 'QUEUED' | 'READY' | 'CANCELED';
37
- created: number;
38
- readyState?: string;
39
- inspectorUrl?: string;
40
- meta?: Record<string, string>;
41
- }
42
-
43
- export interface VercelDomain {
44
- name: string;
45
- verified: boolean;
46
- configured: boolean;
47
- gitBranch?: string;
48
- redirect?: string;
49
- redirectStatusCode?: number;
50
- }
51
-
52
- export interface VercelEnvVar {
53
- key: string;
54
- value: string;
55
- target: ('production' | 'preview' | 'development')[];
56
- type?: 'plain' | 'encrypted' | 'secret' | 'system';
57
- }
58
-
59
- export interface DeployFromGitOptions {
60
- /** Git repository URL (e.g. "https://github.com/user/repo") */
61
- gitUrl: string;
62
- /** Project name (auto-detected from repo if omitted) */
63
- projectName?: string;
64
- /** Framework preset (auto-detected if omitted) */
65
- framework?: string;
66
- /** Build command override */
67
- buildCommand?: string;
68
- /** Output directory override */
69
- outputDirectory?: string;
70
- /** Environment variables to set */
71
- envVars?: Record<string, string>;
72
- }
73
-
74
- export interface DeployResult {
75
- id: string;
76
- url: string;
77
- readyState: string;
78
- inspectorUrl: string;
79
- projectId: string;
80
- }
81
-
82
- // ---------------------------------------------------------------------------
83
- // Service
84
- // ---------------------------------------------------------------------------
85
-
86
- export class VercelService {
87
- private config: VercelConfig;
88
- private running = false;
89
-
90
- constructor(config: VercelConfig) {
91
- this.config = {
92
- ...config,
93
- baseUrl: config.baseUrl ?? 'https://api.vercel.com',
94
- };
95
- }
96
-
97
- async initialize(): Promise<void> {
98
- // Validate token by fetching user info
99
- const res = await this.fetch('/v2/user');
100
- if (!res.ok) {
101
- const body = await res.text();
102
- throw new Error(`Vercel auth failed: ${res.status} ${body}`);
103
- }
104
- this.running = true;
105
- }
106
-
107
- async shutdown(): Promise<void> {
108
- this.running = false;
109
- }
110
-
111
- get isRunning(): boolean {
112
- return this.running;
113
- }
114
-
115
- // ── Projects ────────────────────────────────────────────────────────────
116
-
117
- /** List all projects. */
118
- async listProjects(limit = 20): Promise<VercelProject[]> {
119
- const res = await this.fetch(`/v9/projects?limit=${limit}`);
120
- const data = await this.json(res);
121
- return (data.projects ?? []).map((p: any) => this.mapProject(p));
122
- }
123
-
124
- /** Get a project by ID or name. */
125
- async getProject(idOrName: string): Promise<VercelProject> {
126
- const res = await this.fetch(`/v9/projects/${encodeURIComponent(idOrName)}`);
127
- const data = await this.json(res);
128
- return this.mapProject(data);
129
- }
130
-
131
- /** Create a new project linked to a Git repository. */
132
- async createProject(name: string, gitUrl: string, opts?: {
133
- framework?: string;
134
- buildCommand?: string;
135
- outputDirectory?: string;
136
- }): Promise<VercelProject> {
137
- const { owner, repo, provider } = this.parseGitUrl(gitUrl);
138
-
139
- const body: Record<string, unknown> = {
140
- name,
141
- gitRepository: {
142
- type: provider,
143
- repo: `${owner}/${repo}`,
144
- },
145
- };
146
-
147
- if (opts?.framework) body.framework = opts.framework;
148
- if (opts?.buildCommand) body.buildCommand = opts.buildCommand;
149
- if (opts?.outputDirectory) body.outputDirectory = opts.outputDirectory;
150
-
151
- const res = await this.fetch('/v10/projects', {
152
- method: 'POST',
153
- body: JSON.stringify(body),
154
- });
155
- const data = await this.json(res);
156
- return this.mapProject(data);
157
- }
158
-
159
- // ── Deployments ─────────────────────────────────────────────────────────
160
-
161
- /** Trigger a deployment from an existing project (triggers rebuild from latest commit). */
162
- async createDeployment(projectName: string, opts?: {
163
- target?: 'production' | 'preview';
164
- gitRef?: string;
165
- }): Promise<DeployResult> {
166
- const body: Record<string, unknown> = {
167
- name: projectName,
168
- target: opts?.target ?? 'production',
169
- };
170
-
171
- if (opts?.gitRef) {
172
- body.gitSource = { ref: opts.gitRef };
173
- }
174
-
175
- const res = await this.fetch('/v13/deployments', {
176
- method: 'POST',
177
- body: JSON.stringify(body),
178
- });
179
- const data = await this.json(res);
180
- return {
181
- id: data.id,
182
- url: `https://${data.url}`,
183
- readyState: data.readyState ?? data.status ?? 'BUILDING',
184
- inspectorUrl: data.inspectorUrl ?? '',
185
- projectId: data.projectId ?? '',
186
- };
187
- }
188
-
189
- /** Deploy from a Git URL — creates project if needed, then deploys. */
190
- async deployFromGit(opts: DeployFromGitOptions): Promise<DeployResult> {
191
- const projectName = opts.projectName ?? this.inferProjectName(opts.gitUrl);
192
-
193
- // Try to get existing project
194
- let project: VercelProject | null = null;
195
- try {
196
- project = await this.getProject(projectName);
197
- } catch {
198
- // Project doesn't exist — create it
199
- project = await this.createProject(projectName, opts.gitUrl, {
200
- framework: opts.framework,
201
- buildCommand: opts.buildCommand,
202
- outputDirectory: opts.outputDirectory,
203
- });
204
- }
205
-
206
- // Set env vars if provided
207
- if (opts.envVars && Object.keys(opts.envVars).length > 0) {
208
- await this.setEnvVars(project.id, opts.envVars);
209
- }
210
-
211
- // Trigger deployment
212
- return this.createDeployment(projectName);
213
- }
214
-
215
- /** Get deployment status. */
216
- async getDeployment(deploymentId: string): Promise<VercelDeployment> {
217
- const res = await this.fetch(`/v13/deployments/${deploymentId}`);
218
- const data = await this.json(res);
219
- return this.mapDeployment(data);
220
- }
221
-
222
- /** List recent deployments for a project. */
223
- async listDeployments(projectId: string, limit = 10): Promise<VercelDeployment[]> {
224
- const res = await this.fetch(`/v6/deployments?projectId=${projectId}&limit=${limit}`);
225
- const data = await this.json(res);
226
- return (data.deployments ?? []).map((d: any) => this.mapDeployment(d));
227
- }
228
-
229
- // ── Domains ─────────────────────────────────────────────────────────────
230
-
231
- /** Add a custom domain to a project. */
232
- async addDomain(projectId: string, domain: string, opts?: {
233
- gitBranch?: string;
234
- redirect?: string;
235
- redirectStatusCode?: number;
236
- }): Promise<VercelDomain> {
237
- const body: Record<string, unknown> = { name: domain };
238
- if (opts?.gitBranch) body.gitBranch = opts.gitBranch;
239
- if (opts?.redirect) body.redirect = opts.redirect;
240
- if (opts?.redirectStatusCode) body.redirectStatusCode = opts.redirectStatusCode;
241
-
242
- const res = await this.fetch(`/v10/projects/${projectId}/domains`, {
243
- method: 'POST',
244
- body: JSON.stringify(body),
245
- });
246
- const data = await this.json(res);
247
- return {
248
- name: data.name,
249
- verified: data.verified ?? false,
250
- configured: data.configured ?? false,
251
- gitBranch: data.gitBranch,
252
- redirect: data.redirect,
253
- redirectStatusCode: data.redirectStatusCode,
254
- };
255
- }
256
-
257
- /** Remove a custom domain from a project. */
258
- async removeDomain(projectId: string, domain: string): Promise<void> {
259
- await this.fetch(`/v9/projects/${projectId}/domains/${domain}`, {
260
- method: 'DELETE',
261
- });
262
- }
263
-
264
- /** List domains for a project. */
265
- async listDomains(projectId: string): Promise<VercelDomain[]> {
266
- const res = await this.fetch(`/v9/projects/${projectId}/domains`);
267
- const data = await this.json(res);
268
- return (data.domains ?? []).map((d: any) => ({
269
- name: d.name,
270
- verified: d.verified ?? false,
271
- configured: d.configured ?? false,
272
- gitBranch: d.gitBranch,
273
- redirect: d.redirect,
274
- redirectStatusCode: d.redirectStatusCode,
275
- }));
276
- }
277
-
278
- /** Get domain configuration info (for DNS setup). */
279
- async getDomainConfig(domain: string): Promise<{
280
- configuredBy: string | null;
281
- nameservers: string[];
282
- misconfigured: boolean;
283
- cnames: string[];
284
- aValues: string[];
285
- }> {
286
- const res = await this.fetch(`/v6/domains/${domain}/config`);
287
- const data = await this.json(res);
288
- return {
289
- configuredBy: data.configuredBy ?? null,
290
- nameservers: data.nameservers ?? [],
291
- misconfigured: data.misconfigured ?? true,
292
- cnames: data.cnames ?? [],
293
- aValues: data.aValues ?? [],
294
- };
295
- }
296
-
297
- // ── Environment Variables ───────────────────────────────────────────────
298
-
299
- /** Set environment variables on a project. */
300
- async setEnvVars(
301
- projectId: string,
302
- vars: Record<string, string>,
303
- target: ('production' | 'preview' | 'development')[] = ['production', 'preview', 'development'],
304
- ): Promise<void> {
305
- for (const [key, value] of Object.entries(vars)) {
306
- // Try to create; if already exists, update
307
- const res = await this.fetch(`/v10/projects/${projectId}/env`, {
308
- method: 'POST',
309
- body: JSON.stringify({ key, value, target, type: 'encrypted' }),
310
- });
311
-
312
- if (!res.ok) {
313
- const body = await res.json().catch(() => ({})) as Record<string, any>;
314
- // If already exists, try patching
315
- if (body.error?.code === 'ENV_ALREADY_EXISTS') {
316
- const envId = body.error?.envId ?? body.error?.envVarId;
317
- if (envId) {
318
- await this.fetch(`/v9/projects/${projectId}/env/${envId}`, {
319
- method: 'PATCH',
320
- body: JSON.stringify({ value, target, type: 'encrypted' }),
321
- });
322
- }
323
- }
324
- }
325
- }
326
- }
327
-
328
- /** List environment variables for a project. */
329
- async listEnvVars(projectId: string): Promise<VercelEnvVar[]> {
330
- const res = await this.fetch(`/v9/projects/${projectId}/env`);
331
- const data = await this.json(res);
332
- return (data.envs ?? []).map((e: any) => ({
333
- key: e.key,
334
- value: e.value ?? '(encrypted)',
335
- target: e.target ?? [],
336
- type: e.type ?? 'plain',
337
- }));
338
- }
339
-
340
- // ── Helpers ─────────────────────────────────────────────────────────────
341
-
342
- private async fetch(path: string, init?: RequestInit): Promise<Response> {
343
- const url = `${this.config.baseUrl}${path}${this.teamQuery(path)}`;
344
- return globalThis.fetch(url, {
345
- ...init,
346
- headers: {
347
- 'Authorization': `Bearer ${this.config.token}`,
348
- 'Content-Type': 'application/json',
349
- ...(init?.headers as Record<string, string> ?? {}),
350
- },
351
- });
352
- }
353
-
354
- private async json(res: Response): Promise<any> {
355
- if (!res.ok) {
356
- const text = await res.text();
357
- throw new Error(`Vercel API ${res.status}: ${text.slice(0, 500)}`);
358
- }
359
- return res.json();
360
- }
361
-
362
- private teamQuery(path: string): string {
363
- if (!this.config.teamId) return '';
364
- return path.includes('?') ? `&teamId=${this.config.teamId}` : `?teamId=${this.config.teamId}`;
365
- }
366
-
367
- private parseGitUrl(url: string): { owner: string; repo: string; provider: string } {
368
- // Handle github.com, gitlab.com, bitbucket.org
369
- const match = url.match(/(?:github\.com|gitlab\.com|bitbucket\.org)[/:]([^/]+)\/([^/.]+)/);
370
- if (!match) throw new Error(`Cannot parse git URL: ${url}`);
371
-
372
- const provider = url.includes('github.com') ? 'github'
373
- : url.includes('gitlab.com') ? 'gitlab'
374
- : 'bitbucket';
375
-
376
- return { owner: match[1], repo: match[2], provider };
377
- }
378
-
379
- private inferProjectName(gitUrl: string): string {
380
- const { repo } = this.parseGitUrl(gitUrl);
381
- return repo.toLowerCase().replace(/[^a-z0-9-]/g, '-');
382
- }
383
-
384
- private mapProject(data: any): VercelProject {
385
- return {
386
- id: data.id,
387
- name: data.name,
388
- framework: data.framework ?? null,
389
- createdAt: data.createdAt ?? 0,
390
- updatedAt: data.updatedAt ?? 0,
391
- latestDeployments: data.latestDeployments?.map((d: any) => this.mapDeployment(d)),
392
- link: data.link ? { type: data.link.type, repo: data.link.repo, org: data.link.org } : undefined,
393
- };
394
- }
395
-
396
- private mapDeployment(data: any): VercelDeployment {
397
- return {
398
- uid: data.uid ?? data.id,
399
- name: data.name ?? '',
400
- url: data.url ? (data.url.startsWith('http') ? data.url : `https://${data.url}`) : '',
401
- state: data.state ?? data.readyState ?? 'BUILDING',
402
- created: data.created ?? data.createdAt ?? 0,
403
- readyState: data.readyState,
404
- inspectorUrl: data.inspectorUrl,
405
- meta: data.meta,
406
- };
407
- }
408
- }
package/src/index.ts DELETED
@@ -1,117 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview Vercel Cloud Extension for AgentOS.
4
- *
5
- * Provides 5 tools for deploying projects, managing domains, and configuring
6
- * environment variables via the Vercel REST API.
7
- *
8
- * @module @framers/agentos-ext-cloud-vercel
9
- */
10
-
11
- import { VercelService } from './VercelService.js';
12
- import type { VercelConfig } from './VercelService.js';
13
- import { VercelDeployTool } from './tools/deploy.js';
14
- import { VercelListProjectsTool } from './tools/listProjects.js';
15
- import { VercelGetDeploymentTool } from './tools/getDeployment.js';
16
- import { VercelConfigureDomainTool } from './tools/configureDomain.js';
17
- import { VercelSetEnvVarsTool } from './tools/setEnvVars.js';
18
-
19
- // ---------------------------------------------------------------------------
20
- // Options
21
- // ---------------------------------------------------------------------------
22
-
23
- export interface VercelCloudOptions {
24
- token?: string;
25
- teamId?: string;
26
- secrets?: Record<string, string>;
27
- }
28
-
29
- // ---------------------------------------------------------------------------
30
- // Secret Resolution
31
- // ---------------------------------------------------------------------------
32
-
33
- function resolveConfig(opts: VercelCloudOptions, secrets: Record<string, string>): VercelConfig {
34
- return {
35
- token:
36
- opts.token ?? secrets['vercel.token']
37
- ?? process.env.VERCEL_TOKEN ?? '',
38
- teamId:
39
- opts.teamId ?? secrets['vercel.teamId']
40
- ?? process.env.VERCEL_TEAM_ID,
41
- };
42
- }
43
-
44
- // ---------------------------------------------------------------------------
45
- // Extension Context
46
- // ---------------------------------------------------------------------------
47
-
48
- export interface ExtensionContext {
49
- options?: Record<string, unknown>;
50
- secrets?: Record<string, string>;
51
- getSecret?: (key: string) => string | undefined;
52
- logger?: { info: (msg: string) => void };
53
- }
54
-
55
- export interface ExtensionPack {
56
- name: string;
57
- version: string;
58
- descriptors: Array<{ id: string; kind: string; priority?: number; payload: unknown }>;
59
- onActivate?: () => Promise<void>;
60
- onDeactivate?: () => Promise<void>;
61
- }
62
-
63
- // ---------------------------------------------------------------------------
64
- // Factory
65
- // ---------------------------------------------------------------------------
66
-
67
- export function createExtensionPack(context: ExtensionContext): ExtensionPack {
68
- const opts = (context.options ?? {}) as VercelCloudOptions;
69
- const secrets = opts.secrets ?? context.secrets ?? {};
70
- const config = resolveConfig(opts, secrets);
71
-
72
- const service = new VercelService(config);
73
-
74
- const deployTool = new VercelDeployTool(service);
75
- const listProjectsTool = new VercelListProjectsTool(service);
76
- const getDeploymentTool = new VercelGetDeploymentTool(service);
77
- const configureDomainTool = new VercelConfigureDomainTool(service);
78
- const setEnvVarsTool = new VercelSetEnvVarsTool(service);
79
-
80
- return {
81
- name: '@framers/agentos-ext-cloud-vercel',
82
- version: '0.1.0',
83
- descriptors: [
84
- { id: 'vercelDeploy', kind: 'tool', priority: 40, payload: deployTool },
85
- { id: 'vercelListProjects', kind: 'tool', priority: 40, payload: listProjectsTool },
86
- { id: 'vercelGetDeployment', kind: 'tool', priority: 40, payload: getDeploymentTool },
87
- { id: 'vercelConfigureDomain', kind: 'tool', priority: 40, payload: configureDomainTool },
88
- { id: 'vercelSetEnvVars', kind: 'tool', priority: 40, payload: setEnvVarsTool },
89
- ],
90
- onActivate: async () => {
91
- if (!config.token) {
92
- throw new Error(
93
- 'Vercel: no API token provided. Set VERCEL_TOKEN environment variable, '
94
- + 'or provide it via secrets["vercel.token"].',
95
- );
96
- }
97
- await service.initialize();
98
- },
99
- onDeactivate: async () => {
100
- await service.shutdown();
101
- },
102
- };
103
- }
104
-
105
- // ---------------------------------------------------------------------------
106
- // Public exports
107
- // ---------------------------------------------------------------------------
108
-
109
- export { VercelService } from './VercelService.js';
110
- export type { VercelConfig, VercelProject, VercelDeployment, VercelDomain, VercelEnvVar, DeployResult, DeployFromGitOptions } from './VercelService.js';
111
- export { VercelDeployTool } from './tools/deploy.js';
112
- export { VercelListProjectsTool } from './tools/listProjects.js';
113
- export { VercelGetDeploymentTool } from './tools/getDeployment.js';
114
- export { VercelConfigureDomainTool } from './tools/configureDomain.js';
115
- export { VercelSetEnvVarsTool } from './tools/setEnvVars.js';
116
-
117
- export default createExtensionPack;
@@ -1,91 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview VercelConfigureDomainTool — add or manage custom domains on a Vercel project.
4
- */
5
-
6
- import type { VercelService, VercelDomain } from '../VercelService.js';
7
-
8
- export interface ConfigureDomainInput {
9
- projectId: string;
10
- domain: string;
11
- action?: 'add' | 'remove' | 'list' | 'check';
12
- gitBranch?: string;
13
- }
14
-
15
- export class VercelConfigureDomainTool {
16
- readonly id = 'vercelConfigureDomain';
17
- readonly name = 'vercelConfigureDomain';
18
- readonly displayName = 'Configure Domain';
19
- readonly description = 'Add, remove, or list custom domains on a Vercel project. Also checks DNS configuration status and returns required CNAME/A records for setup.';
20
- readonly category = 'cloud';
21
- readonly version = '0.1.0';
22
- readonly hasSideEffects = true;
23
-
24
- readonly inputSchema = {
25
- type: 'object' as const,
26
- properties: {
27
- projectId: { type: 'string', description: 'Vercel project ID or name' },
28
- domain: { type: 'string', description: 'The custom domain (e.g. "example.com" or "www.example.com")' },
29
- action: { type: 'string', enum: ['add', 'remove', 'list', 'check'], description: 'Action to perform (default: add)' },
30
- gitBranch: { type: 'string', description: 'Git branch to link the domain to (optional)' },
31
- },
32
- required: ['projectId', 'domain'],
33
- };
34
-
35
- constructor(private service: VercelService) {}
36
-
37
- async execute(args: ConfigureDomainInput): Promise<{
38
- success: boolean;
39
- data?: VercelDomain | VercelDomain[] | { dnsInstructions: string };
40
- error?: string;
41
- }> {
42
- try {
43
- const action = args.action ?? 'add';
44
-
45
- switch (action) {
46
- case 'add': {
47
- const domain = await this.service.addDomain(args.projectId, args.domain, {
48
- gitBranch: args.gitBranch,
49
- });
50
-
51
- // Also get DNS config instructions
52
- let dnsNote = '';
53
- try {
54
- const config = await this.service.getDomainConfig(args.domain);
55
- if (config.misconfigured) {
56
- dnsNote = ` DNS setup required: set CNAME record for "${args.domain}" pointing to "cname.vercel-dns.com".`;
57
- }
58
- } catch { /* ignore — domain might not be resolvable yet */ }
59
-
60
- return {
61
- success: true,
62
- data: { ...domain, ...(dnsNote ? { dnsInstructions: dnsNote } as any : {}) },
63
- };
64
- }
65
-
66
- case 'remove': {
67
- await this.service.removeDomain(args.projectId, args.domain);
68
- return { success: true, data: { dnsInstructions: `Domain "${args.domain}" removed from project.` } };
69
- }
70
-
71
- case 'list': {
72
- const domains = await this.service.listDomains(args.projectId);
73
- return { success: true, data: domains };
74
- }
75
-
76
- case 'check': {
77
- const config = await this.service.getDomainConfig(args.domain);
78
- const instructions = config.misconfigured
79
- ? `DNS misconfigured. Set a CNAME record for "${args.domain}" pointing to "cname.vercel-dns.com". Or set an A record to 76.76.21.21.`
80
- : `Domain "${args.domain}" is properly configured.`;
81
- return { success: true, data: { dnsInstructions: instructions } };
82
- }
83
-
84
- default:
85
- return { success: false, error: `Unknown action: ${action}` };
86
- }
87
- } catch (err: any) {
88
- return { success: false, error: err.message };
89
- }
90
- }
91
- }