@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 +2 -2
- package/package.json +14 -6
- package/src/VercelService.ts +0 -408
- package/src/index.ts +0 -117
- package/src/tools/configureDomain.ts +0 -91
- package/src/tools/deploy.ts +0 -59
- package/src/tools/getDeployment.ts +0 -35
- package/src/tools/listProjects.ts +0 -35
- package/src/tools/setEnvVars.ts +0 -65
- package/test/VercelService.test.ts +0 -464
- package/test/index.test.ts +0 -63
- package/test/tools.test.ts +0 -332
- package/tsconfig.json +0 -22
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.
|
|
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/
|
|
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.
|
|
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/
|
|
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": "
|
|
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": "^
|
|
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"
|
package/src/VercelService.ts
DELETED
|
@@ -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
|
-
}
|