@digitalpresence/cliclaw 0.4.2 → 0.5.0
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/.turbo/turbo-build.log +4 -0
- package/dist/__tests__/vercel.integration.test.d.ts +2 -0
- package/dist/__tests__/vercel.integration.test.d.ts.map +1 -0
- package/dist/__tests__/vercel.integration.test.js +111 -0
- package/dist/__tests__/vercel.integration.test.js.map +1 -0
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/vercel.d.ts +6 -0
- package/dist/commands/vercel.d.ts.map +1 -0
- package/dist/commands/vercel.js +200 -0
- package/dist/commands/vercel.js.map +1 -0
- package/dist/vercel/api.d.ts +5 -0
- package/dist/vercel/api.d.ts.map +1 -0
- package/dist/vercel/api.js +68 -0
- package/dist/vercel/api.js.map +1 -0
- package/dist/vercel/handlers.d.ts +23 -0
- package/dist/vercel/handlers.d.ts.map +1 -0
- package/dist/vercel/handlers.js +253 -0
- package/dist/vercel/handlers.js.map +1 -0
- package/package.json +11 -11
- package/src/__tests__/vercel.integration.test.ts +164 -0
- package/src/cli.ts +3 -1
- package/src/commands/vercel.ts +236 -0
- package/src/vercel/api.ts +89 -0
- package/src/vercel/handlers.ts +369 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { TokenStore } from "@digitalpresence/cliclaw-auth";
|
|
3
|
+
import {
|
|
4
|
+
handleWhoami,
|
|
5
|
+
handleProjects, handleProject, handleProjectCreate, handleProjectDelete,
|
|
6
|
+
handleDeployments, handleDeployment, handleRedeploy, handlePromote, handleDeploymentDelete, handleBuildLogs,
|
|
7
|
+
handleDomains, handleDomain, handleDomainAdd, handleDomainRemove, handleDomainVerify,
|
|
8
|
+
handleEnvs, handleEnvGet, handleEnvCreate, handleEnvUpdate, handleEnvDelete,
|
|
9
|
+
} from "../vercel/handlers.js";
|
|
10
|
+
|
|
11
|
+
type TokenStoreFactory = () => TokenStore;
|
|
12
|
+
|
|
13
|
+
export function registerVercelCommands(program: Command, getTokenStore: TokenStoreFactory): void {
|
|
14
|
+
let cached: TokenStore | null = null;
|
|
15
|
+
function resolve() {
|
|
16
|
+
if (!cached) cached = getTokenStore();
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const vercel = program.command("vercel").description("Vercel commands");
|
|
21
|
+
|
|
22
|
+
// --- Account ---
|
|
23
|
+
|
|
24
|
+
vercel
|
|
25
|
+
.command("whoami")
|
|
26
|
+
.description("Show authenticated user info")
|
|
27
|
+
.option("--account <name>", "Account name", "default")
|
|
28
|
+
.action(async (opts) => {
|
|
29
|
+
await handleWhoami(resolve(), opts.account);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// --- Projects ---
|
|
33
|
+
|
|
34
|
+
vercel
|
|
35
|
+
.command("projects")
|
|
36
|
+
.description("List projects")
|
|
37
|
+
.option("--max-results <n>", "Maximum projects to return", "20")
|
|
38
|
+
.option("--account <name>", "Account name", "default")
|
|
39
|
+
.action(async (opts) => {
|
|
40
|
+
await handleProjects(resolve(), opts.account, parseInt(opts.maxResults));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
vercel
|
|
44
|
+
.command("project")
|
|
45
|
+
.description("Get project details")
|
|
46
|
+
.requiredOption("--project <id>", "Project ID")
|
|
47
|
+
.option("--account <name>", "Account name", "default")
|
|
48
|
+
.action(async (opts) => {
|
|
49
|
+
await handleProject(resolve(), opts.account, opts.project);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
vercel
|
|
53
|
+
.command("project-create")
|
|
54
|
+
.description("Create a project")
|
|
55
|
+
.requiredOption("--name <name>", "Project name")
|
|
56
|
+
.option("--framework <framework>", "Framework preset")
|
|
57
|
+
.option("--git-repo <owner/repo>", "Git repository (owner/repo)")
|
|
58
|
+
.option("--account <name>", "Account name", "default")
|
|
59
|
+
.action(async (opts) => {
|
|
60
|
+
await handleProjectCreate(resolve(), opts.account, opts.name, opts.framework, opts.gitRepo);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
vercel
|
|
64
|
+
.command("project-delete")
|
|
65
|
+
.description("Delete a project")
|
|
66
|
+
.requiredOption("--project <id>", "Project ID")
|
|
67
|
+
.option("--account <name>", "Account name", "default")
|
|
68
|
+
.action(async (opts) => {
|
|
69
|
+
await handleProjectDelete(resolve(), opts.account, opts.project);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// --- Deployments ---
|
|
73
|
+
|
|
74
|
+
vercel
|
|
75
|
+
.command("deployments")
|
|
76
|
+
.description("List deployments")
|
|
77
|
+
.option("--project <id>", "Project ID")
|
|
78
|
+
.option("--max-results <n>", "Maximum deployments to return", "20")
|
|
79
|
+
.option("--account <name>", "Account name", "default")
|
|
80
|
+
.action(async (opts) => {
|
|
81
|
+
await handleDeployments(resolve(), opts.account, opts.project, parseInt(opts.maxResults));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
vercel
|
|
85
|
+
.command("deployment")
|
|
86
|
+
.description("Get deployment details")
|
|
87
|
+
.requiredOption("--deployment-id <id>", "Deployment ID")
|
|
88
|
+
.option("--account <name>", "Account name", "default")
|
|
89
|
+
.action(async (opts) => {
|
|
90
|
+
await handleDeployment(resolve(), opts.account, opts.deploymentId);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
vercel
|
|
94
|
+
.command("redeploy")
|
|
95
|
+
.description("Redeploy an existing deployment")
|
|
96
|
+
.requiredOption("--deployment-id <id>", "Deployment ID")
|
|
97
|
+
.option("--target <target>", "Deployment target (production/preview)")
|
|
98
|
+
.option("--account <name>", "Account name", "default")
|
|
99
|
+
.action(async (opts) => {
|
|
100
|
+
await handleRedeploy(resolve(), opts.account, opts.deploymentId, opts.target);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
vercel
|
|
104
|
+
.command("promote")
|
|
105
|
+
.description("Promote deployment to production")
|
|
106
|
+
.requiredOption("--project <id>", "Project ID")
|
|
107
|
+
.requiredOption("--deployment-id <id>", "Deployment ID")
|
|
108
|
+
.option("--account <name>", "Account name", "default")
|
|
109
|
+
.action(async (opts) => {
|
|
110
|
+
await handlePromote(resolve(), opts.account, opts.project, opts.deploymentId);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
vercel
|
|
114
|
+
.command("deployment-delete")
|
|
115
|
+
.description("Delete a deployment")
|
|
116
|
+
.requiredOption("--deployment-id <id>", "Deployment ID")
|
|
117
|
+
.option("--account <name>", "Account name", "default")
|
|
118
|
+
.action(async (opts) => {
|
|
119
|
+
await handleDeploymentDelete(resolve(), opts.account, opts.deploymentId);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
vercel
|
|
123
|
+
.command("build-logs")
|
|
124
|
+
.description("Get build logs for a deployment")
|
|
125
|
+
.requiredOption("--deployment-id <id>", "Deployment ID")
|
|
126
|
+
.option("--account <name>", "Account name", "default")
|
|
127
|
+
.action(async (opts) => {
|
|
128
|
+
await handleBuildLogs(resolve(), opts.account, opts.deploymentId);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// --- Domains ---
|
|
132
|
+
|
|
133
|
+
vercel
|
|
134
|
+
.command("domains")
|
|
135
|
+
.description("List domains")
|
|
136
|
+
.option("--max-results <n>", "Maximum domains to return", "20")
|
|
137
|
+
.option("--account <name>", "Account name", "default")
|
|
138
|
+
.action(async (opts) => {
|
|
139
|
+
await handleDomains(resolve(), opts.account, parseInt(opts.maxResults));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
vercel
|
|
143
|
+
.command("domain")
|
|
144
|
+
.description("Get domain details")
|
|
145
|
+
.requiredOption("--domain <domain>", "Domain name")
|
|
146
|
+
.option("--account <name>", "Account name", "default")
|
|
147
|
+
.action(async (opts) => {
|
|
148
|
+
await handleDomain(resolve(), opts.account, opts.domain);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
vercel
|
|
152
|
+
.command("domain-add")
|
|
153
|
+
.description("Add domain to project")
|
|
154
|
+
.requiredOption("--project <id>", "Project ID")
|
|
155
|
+
.requiredOption("--domain <domain>", "Domain name")
|
|
156
|
+
.option("--account <name>", "Account name", "default")
|
|
157
|
+
.action(async (opts) => {
|
|
158
|
+
await handleDomainAdd(resolve(), opts.account, opts.project, opts.domain);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
vercel
|
|
162
|
+
.command("domain-remove")
|
|
163
|
+
.description("Remove domain from project")
|
|
164
|
+
.requiredOption("--project <id>", "Project ID")
|
|
165
|
+
.requiredOption("--domain <domain>", "Domain name")
|
|
166
|
+
.option("--account <name>", "Account name", "default")
|
|
167
|
+
.action(async (opts) => {
|
|
168
|
+
await handleDomainRemove(resolve(), opts.account, opts.project, opts.domain);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
vercel
|
|
172
|
+
.command("domain-verify")
|
|
173
|
+
.description("Verify domain on project")
|
|
174
|
+
.requiredOption("--project <id>", "Project ID")
|
|
175
|
+
.requiredOption("--domain <domain>", "Domain name")
|
|
176
|
+
.option("--account <name>", "Account name", "default")
|
|
177
|
+
.action(async (opts) => {
|
|
178
|
+
await handleDomainVerify(resolve(), opts.account, opts.project, opts.domain);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// --- Environment Variables ---
|
|
182
|
+
|
|
183
|
+
vercel
|
|
184
|
+
.command("envs")
|
|
185
|
+
.description("List environment variables")
|
|
186
|
+
.requiredOption("--project <id>", "Project ID")
|
|
187
|
+
.option("--account <name>", "Account name", "default")
|
|
188
|
+
.action(async (opts) => {
|
|
189
|
+
await handleEnvs(resolve(), opts.account, opts.project);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
vercel
|
|
193
|
+
.command("env")
|
|
194
|
+
.description("Get environment variable")
|
|
195
|
+
.requiredOption("--project <id>", "Project ID")
|
|
196
|
+
.requiredOption("--env-id <id>", "Environment variable ID")
|
|
197
|
+
.option("--account <name>", "Account name", "default")
|
|
198
|
+
.action(async (opts) => {
|
|
199
|
+
await handleEnvGet(resolve(), opts.account, opts.project, opts.envId);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
vercel
|
|
203
|
+
.command("env-create")
|
|
204
|
+
.description("Create environment variable")
|
|
205
|
+
.requiredOption("--project <id>", "Project ID")
|
|
206
|
+
.requiredOption("--key <key>", "Variable key")
|
|
207
|
+
.requiredOption("--value <value>", "Variable value")
|
|
208
|
+
.requiredOption("--target <targets>", "Comma-separated deployment targets")
|
|
209
|
+
.option("--type <type>", "Variable type", "encrypted")
|
|
210
|
+
.option("--account <name>", "Account name", "default")
|
|
211
|
+
.action(async (opts) => {
|
|
212
|
+
const target = opts.target.split(",").map((t: string) => t.trim());
|
|
213
|
+
await handleEnvCreate(resolve(), opts.account, opts.project, opts.key, opts.value, target, opts.type);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
vercel
|
|
217
|
+
.command("env-update")
|
|
218
|
+
.description("Update environment variable value")
|
|
219
|
+
.requiredOption("--project <id>", "Project ID")
|
|
220
|
+
.requiredOption("--env-id <id>", "Environment variable ID")
|
|
221
|
+
.requiredOption("--value <value>", "New variable value")
|
|
222
|
+
.option("--account <name>", "Account name", "default")
|
|
223
|
+
.action(async (opts) => {
|
|
224
|
+
await handleEnvUpdate(resolve(), opts.account, opts.project, opts.envId, opts.value);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
vercel
|
|
228
|
+
.command("env-delete")
|
|
229
|
+
.description("Delete environment variable")
|
|
230
|
+
.requiredOption("--project <id>", "Project ID")
|
|
231
|
+
.requiredOption("--env-id <id>", "Environment variable ID")
|
|
232
|
+
.option("--account <name>", "Account name", "default")
|
|
233
|
+
.action(async (opts) => {
|
|
234
|
+
await handleEnvDelete(resolve(), opts.account, opts.project, opts.envId);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { TokenStore } from "@digitalpresence/cliclaw-auth";
|
|
2
|
+
import { outputAuthRequired, outputError } from "../lib/output.js";
|
|
3
|
+
|
|
4
|
+
const VERCEL_API = "https://api.vercel.com";
|
|
5
|
+
|
|
6
|
+
export function getToken(tokenStore: TokenStore, account: string): string {
|
|
7
|
+
const tokenKey = `vercel:${account}`;
|
|
8
|
+
const creds = tokenStore.get(tokenKey);
|
|
9
|
+
if (!creds?.access_token) {
|
|
10
|
+
outputAuthRequired("vercel");
|
|
11
|
+
}
|
|
12
|
+
return creds.access_token as string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function vercelFetch(
|
|
16
|
+
token: string,
|
|
17
|
+
path: string,
|
|
18
|
+
options: RequestInit = {},
|
|
19
|
+
teamId?: string,
|
|
20
|
+
): Promise<unknown> {
|
|
21
|
+
let url = path.startsWith("http") ? path : `${VERCEL_API}${path}`;
|
|
22
|
+
if (teamId) {
|
|
23
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
24
|
+
url = `${url}${separator}teamId=${teamId}`;
|
|
25
|
+
}
|
|
26
|
+
const res = await fetch(url, {
|
|
27
|
+
...options,
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${token}`,
|
|
30
|
+
Accept: "application/json",
|
|
31
|
+
...options.headers,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const body = await res.text().catch(() => "");
|
|
37
|
+
outputError("vercel_api_error", `${res.status} ${res.statusText}: ${body}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return res.json();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function vercelFetchPaginated(
|
|
44
|
+
token: string,
|
|
45
|
+
path: string,
|
|
46
|
+
arrayKey: string,
|
|
47
|
+
maxResults: number,
|
|
48
|
+
teamId?: string,
|
|
49
|
+
): Promise<unknown[]> {
|
|
50
|
+
const results: unknown[] = [];
|
|
51
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
52
|
+
let url: string | null = `${path}${separator}limit=${Math.min(maxResults, 100)}`;
|
|
53
|
+
|
|
54
|
+
while (url && results.length < maxResults) {
|
|
55
|
+
let fullUrl = url.startsWith("http") ? url : `${VERCEL_API}${url}`;
|
|
56
|
+
if (teamId) {
|
|
57
|
+
const sep = fullUrl.includes("?") ? "&" : "?";
|
|
58
|
+
fullUrl = `${fullUrl}${sep}teamId=${teamId}`;
|
|
59
|
+
}
|
|
60
|
+
const res: Response = await fetch(fullUrl, {
|
|
61
|
+
headers: {
|
|
62
|
+
Authorization: `Bearer ${token}`,
|
|
63
|
+
Accept: "application/json",
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const body = await res.text().catch(() => "");
|
|
69
|
+
outputError("vercel_api_error", `${res.status} ${res.statusText}: ${body}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = (await res.json()) as Record<string, unknown>;
|
|
73
|
+
const items = data[arrayKey];
|
|
74
|
+
results.push(...(Array.isArray(items) ? items : []));
|
|
75
|
+
|
|
76
|
+
const pagination = data.pagination as { next?: number } | undefined;
|
|
77
|
+
if (pagination?.next) {
|
|
78
|
+
const basePath: string = url!.split("?")[0];
|
|
79
|
+
const existingParams: string = url!.includes("?") ? url!.split("?")[1] : "";
|
|
80
|
+
const params: URLSearchParams = new URLSearchParams(existingParams);
|
|
81
|
+
params.set("until", String(pagination.next));
|
|
82
|
+
url = `${basePath}?${params.toString()}`;
|
|
83
|
+
} else {
|
|
84
|
+
url = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return results.slice(0, maxResults);
|
|
89
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import type { TokenStore } from "@digitalpresence/cliclaw-auth";
|
|
2
|
+
import { getToken, vercelFetch, vercelFetchPaginated } from "./api.js";
|
|
3
|
+
import { outputJson, outputError } from "../lib/output.js";
|
|
4
|
+
|
|
5
|
+
// --- Account ---
|
|
6
|
+
|
|
7
|
+
export async function handleWhoami(tokenStore: TokenStore, account: string): Promise<void> {
|
|
8
|
+
const token = getToken(tokenStore, account);
|
|
9
|
+
try {
|
|
10
|
+
const user = await vercelFetch(token, "/v2/user");
|
|
11
|
+
outputJson(user);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
outputError("whoami_failed", err instanceof Error ? err.message : String(err));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// --- Projects ---
|
|
18
|
+
|
|
19
|
+
export async function handleProjects(
|
|
20
|
+
tokenStore: TokenStore,
|
|
21
|
+
account: string,
|
|
22
|
+
maxResults: number,
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
const token = getToken(tokenStore, account);
|
|
25
|
+
try {
|
|
26
|
+
const projects = await vercelFetchPaginated(token, "/v9/projects", "projects", maxResults);
|
|
27
|
+
outputJson(projects);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
outputError("projects_failed", err instanceof Error ? err.message : String(err));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function handleProject(
|
|
34
|
+
tokenStore: TokenStore,
|
|
35
|
+
account: string,
|
|
36
|
+
projectId: string,
|
|
37
|
+
): Promise<void> {
|
|
38
|
+
const token = getToken(tokenStore, account);
|
|
39
|
+
try {
|
|
40
|
+
const project = await vercelFetch(token, `/v9/projects/${projectId}`);
|
|
41
|
+
outputJson(project);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
outputError("project_get_failed", err instanceof Error ? err.message : String(err));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function handleProjectCreate(
|
|
48
|
+
tokenStore: TokenStore,
|
|
49
|
+
account: string,
|
|
50
|
+
name: string,
|
|
51
|
+
framework?: string,
|
|
52
|
+
gitRepo?: string,
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
const token = getToken(tokenStore, account);
|
|
55
|
+
try {
|
|
56
|
+
const payload: Record<string, unknown> = { name };
|
|
57
|
+
if (framework) payload.framework = framework;
|
|
58
|
+
if (gitRepo) payload.gitRepository = { type: "github", repo: gitRepo };
|
|
59
|
+
const project = await vercelFetch(token, "/v9/projects", {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: { "Content-Type": "application/json" },
|
|
62
|
+
body: JSON.stringify(payload),
|
|
63
|
+
});
|
|
64
|
+
outputJson(project);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
outputError("project_create_failed", err instanceof Error ? err.message : String(err));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function handleProjectDelete(
|
|
71
|
+
tokenStore: TokenStore,
|
|
72
|
+
account: string,
|
|
73
|
+
projectId: string,
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
const token = getToken(tokenStore, account);
|
|
76
|
+
try {
|
|
77
|
+
const result = await vercelFetch(token, `/v9/projects/${projectId}`, { method: "DELETE" });
|
|
78
|
+
outputJson(result);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
outputError("project_delete_failed", err instanceof Error ? err.message : String(err));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- Deployments ---
|
|
85
|
+
|
|
86
|
+
export async function handleDeployments(
|
|
87
|
+
tokenStore: TokenStore,
|
|
88
|
+
account: string,
|
|
89
|
+
projectId?: string,
|
|
90
|
+
maxResults?: number,
|
|
91
|
+
): Promise<void> {
|
|
92
|
+
const token = getToken(tokenStore, account);
|
|
93
|
+
try {
|
|
94
|
+
const path = projectId ? `/v6/deployments?projectId=${projectId}` : "/v6/deployments";
|
|
95
|
+
const deployments = await vercelFetchPaginated(
|
|
96
|
+
token,
|
|
97
|
+
path,
|
|
98
|
+
"deployments",
|
|
99
|
+
maxResults ?? 20,
|
|
100
|
+
);
|
|
101
|
+
outputJson(deployments);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
outputError("deployments_failed", err instanceof Error ? err.message : String(err));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function handleDeployment(
|
|
108
|
+
tokenStore: TokenStore,
|
|
109
|
+
account: string,
|
|
110
|
+
deploymentId: string,
|
|
111
|
+
): Promise<void> {
|
|
112
|
+
const token = getToken(tokenStore, account);
|
|
113
|
+
try {
|
|
114
|
+
const deployment = await vercelFetch(token, `/v13/deployments/${deploymentId}`);
|
|
115
|
+
outputJson(deployment);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
outputError("deployment_get_failed", err instanceof Error ? err.message : String(err));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function handleRedeploy(
|
|
122
|
+
tokenStore: TokenStore,
|
|
123
|
+
account: string,
|
|
124
|
+
deploymentId: string,
|
|
125
|
+
target?: string,
|
|
126
|
+
): Promise<void> {
|
|
127
|
+
const token = getToken(tokenStore, account);
|
|
128
|
+
try {
|
|
129
|
+
const payload: Record<string, unknown> = { deploymentId };
|
|
130
|
+
if (target) payload.target = target;
|
|
131
|
+
const result = await vercelFetch(token, "/v13/deployments", {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: { "Content-Type": "application/json" },
|
|
134
|
+
body: JSON.stringify(payload),
|
|
135
|
+
});
|
|
136
|
+
outputJson(result);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
outputError("redeploy_failed", err instanceof Error ? err.message : String(err));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function handlePromote(
|
|
143
|
+
tokenStore: TokenStore,
|
|
144
|
+
account: string,
|
|
145
|
+
projectId: string,
|
|
146
|
+
deploymentId: string,
|
|
147
|
+
): Promise<void> {
|
|
148
|
+
const token = getToken(tokenStore, account);
|
|
149
|
+
try {
|
|
150
|
+
const result = await vercelFetch(
|
|
151
|
+
token,
|
|
152
|
+
`/v10/projects/${projectId}/promote/${deploymentId}`,
|
|
153
|
+
{ method: "POST" },
|
|
154
|
+
);
|
|
155
|
+
outputJson(result);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
outputError("promote_failed", err instanceof Error ? err.message : String(err));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function handleDeploymentDelete(
|
|
162
|
+
tokenStore: TokenStore,
|
|
163
|
+
account: string,
|
|
164
|
+
deploymentId: string,
|
|
165
|
+
): Promise<void> {
|
|
166
|
+
const token = getToken(tokenStore, account);
|
|
167
|
+
try {
|
|
168
|
+
const result = await vercelFetch(token, `/v13/deployments/${deploymentId}`, {
|
|
169
|
+
method: "DELETE",
|
|
170
|
+
});
|
|
171
|
+
outputJson(result);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
outputError("deployment_delete_failed", err instanceof Error ? err.message : String(err));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function handleBuildLogs(
|
|
178
|
+
tokenStore: TokenStore,
|
|
179
|
+
account: string,
|
|
180
|
+
deploymentId: string,
|
|
181
|
+
): Promise<void> {
|
|
182
|
+
const token = getToken(tokenStore, account);
|
|
183
|
+
try {
|
|
184
|
+
const logs = await vercelFetch(token, `/v2/deployments/${deploymentId}/events`);
|
|
185
|
+
outputJson(logs);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
outputError("build_logs_failed", err instanceof Error ? err.message : String(err));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// --- Domains ---
|
|
192
|
+
|
|
193
|
+
export async function handleDomains(
|
|
194
|
+
tokenStore: TokenStore,
|
|
195
|
+
account: string,
|
|
196
|
+
maxResults?: number,
|
|
197
|
+
): Promise<void> {
|
|
198
|
+
const token = getToken(tokenStore, account);
|
|
199
|
+
try {
|
|
200
|
+
const domains = await vercelFetchPaginated(
|
|
201
|
+
token,
|
|
202
|
+
"/v5/domains",
|
|
203
|
+
"domains",
|
|
204
|
+
maxResults ?? 20,
|
|
205
|
+
);
|
|
206
|
+
outputJson(domains);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
outputError("domains_failed", err instanceof Error ? err.message : String(err));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function handleDomain(
|
|
213
|
+
tokenStore: TokenStore,
|
|
214
|
+
account: string,
|
|
215
|
+
domain: string,
|
|
216
|
+
): Promise<void> {
|
|
217
|
+
const token = getToken(tokenStore, account);
|
|
218
|
+
try {
|
|
219
|
+
const data = await vercelFetch(token, `/v5/domains/${domain}`);
|
|
220
|
+
outputJson(data);
|
|
221
|
+
} catch (err) {
|
|
222
|
+
outputError("domain_get_failed", err instanceof Error ? err.message : String(err));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function handleDomainAdd(
|
|
227
|
+
tokenStore: TokenStore,
|
|
228
|
+
account: string,
|
|
229
|
+
projectId: string,
|
|
230
|
+
domain: string,
|
|
231
|
+
): Promise<void> {
|
|
232
|
+
const token = getToken(tokenStore, account);
|
|
233
|
+
try {
|
|
234
|
+
const result = await vercelFetch(token, `/v9/projects/${projectId}/domains`, {
|
|
235
|
+
method: "POST",
|
|
236
|
+
headers: { "Content-Type": "application/json" },
|
|
237
|
+
body: JSON.stringify({ name: domain }),
|
|
238
|
+
});
|
|
239
|
+
outputJson(result);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
outputError("domain_add_failed", err instanceof Error ? err.message : String(err));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function handleDomainRemove(
|
|
246
|
+
tokenStore: TokenStore,
|
|
247
|
+
account: string,
|
|
248
|
+
projectId: string,
|
|
249
|
+
domain: string,
|
|
250
|
+
): Promise<void> {
|
|
251
|
+
const token = getToken(tokenStore, account);
|
|
252
|
+
try {
|
|
253
|
+
const result = await vercelFetch(token, `/v9/projects/${projectId}/domains/${domain}`, {
|
|
254
|
+
method: "DELETE",
|
|
255
|
+
});
|
|
256
|
+
outputJson(result);
|
|
257
|
+
} catch (err) {
|
|
258
|
+
outputError("domain_remove_failed", err instanceof Error ? err.message : String(err));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export async function handleDomainVerify(
|
|
263
|
+
tokenStore: TokenStore,
|
|
264
|
+
account: string,
|
|
265
|
+
projectId: string,
|
|
266
|
+
domain: string,
|
|
267
|
+
): Promise<void> {
|
|
268
|
+
const token = getToken(tokenStore, account);
|
|
269
|
+
try {
|
|
270
|
+
const result = await vercelFetch(
|
|
271
|
+
token,
|
|
272
|
+
`/v9/projects/${projectId}/domains/${domain}/verify`,
|
|
273
|
+
{ method: "POST" },
|
|
274
|
+
);
|
|
275
|
+
outputJson(result);
|
|
276
|
+
} catch (err) {
|
|
277
|
+
outputError("domain_verify_failed", err instanceof Error ? err.message : String(err));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// --- Environment Variables ---
|
|
282
|
+
|
|
283
|
+
export async function handleEnvs(
|
|
284
|
+
tokenStore: TokenStore,
|
|
285
|
+
account: string,
|
|
286
|
+
projectId: string,
|
|
287
|
+
): Promise<void> {
|
|
288
|
+
const token = getToken(tokenStore, account);
|
|
289
|
+
try {
|
|
290
|
+
const data = await vercelFetch(token, `/v10/projects/${projectId}/env`);
|
|
291
|
+
outputJson((data as Record<string, unknown>).envs);
|
|
292
|
+
} catch (err) {
|
|
293
|
+
outputError("envs_failed", err instanceof Error ? err.message : String(err));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function handleEnvGet(
|
|
298
|
+
tokenStore: TokenStore,
|
|
299
|
+
account: string,
|
|
300
|
+
projectId: string,
|
|
301
|
+
envId: string,
|
|
302
|
+
): Promise<void> {
|
|
303
|
+
const token = getToken(tokenStore, account);
|
|
304
|
+
try {
|
|
305
|
+
const env = await vercelFetch(token, `/v10/projects/${projectId}/env/${envId}`);
|
|
306
|
+
outputJson(env);
|
|
307
|
+
} catch (err) {
|
|
308
|
+
outputError("env_get_failed", err instanceof Error ? err.message : String(err));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export async function handleEnvCreate(
|
|
313
|
+
tokenStore: TokenStore,
|
|
314
|
+
account: string,
|
|
315
|
+
projectId: string,
|
|
316
|
+
key: string,
|
|
317
|
+
value: string,
|
|
318
|
+
target: string[],
|
|
319
|
+
type?: string,
|
|
320
|
+
): Promise<void> {
|
|
321
|
+
const token = getToken(tokenStore, account);
|
|
322
|
+
try {
|
|
323
|
+
const env = await vercelFetch(token, `/v10/projects/${projectId}/env`, {
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers: { "Content-Type": "application/json" },
|
|
326
|
+
body: JSON.stringify({ key, value, target, type: type || "encrypted" }),
|
|
327
|
+
});
|
|
328
|
+
outputJson(env);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
outputError("env_create_failed", err instanceof Error ? err.message : String(err));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export async function handleEnvUpdate(
|
|
335
|
+
tokenStore: TokenStore,
|
|
336
|
+
account: string,
|
|
337
|
+
projectId: string,
|
|
338
|
+
envId: string,
|
|
339
|
+
value: string,
|
|
340
|
+
): Promise<void> {
|
|
341
|
+
const token = getToken(tokenStore, account);
|
|
342
|
+
try {
|
|
343
|
+
const env = await vercelFetch(token, `/v10/projects/${projectId}/env/${envId}`, {
|
|
344
|
+
method: "PATCH",
|
|
345
|
+
headers: { "Content-Type": "application/json" },
|
|
346
|
+
body: JSON.stringify({ value }),
|
|
347
|
+
});
|
|
348
|
+
outputJson(env);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
outputError("env_update_failed", err instanceof Error ? err.message : String(err));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export async function handleEnvDelete(
|
|
355
|
+
tokenStore: TokenStore,
|
|
356
|
+
account: string,
|
|
357
|
+
projectId: string,
|
|
358
|
+
envId: string,
|
|
359
|
+
): Promise<void> {
|
|
360
|
+
const token = getToken(tokenStore, account);
|
|
361
|
+
try {
|
|
362
|
+
const result = await vercelFetch(token, `/v10/projects/${projectId}/env/${envId}`, {
|
|
363
|
+
method: "DELETE",
|
|
364
|
+
});
|
|
365
|
+
outputJson(result);
|
|
366
|
+
} catch (err) {
|
|
367
|
+
outputError("env_delete_failed", err instanceof Error ? err.message : String(err));
|
|
368
|
+
}
|
|
369
|
+
}
|