@prisma/cli 3.0.0-dev.129.1 → 3.0.0-dev.131.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/README.md CHANGED
@@ -76,6 +76,8 @@ The beta package exposes `prisma-cli` so it can coexist with the existing
76
76
  | `project` | List projects, show the resolved project, and manage project environment variables. |
77
77
  | `git` | Connect or disconnect a project from a GitHub repository. |
78
78
  | `branch` | List Prisma branches for the resolved project. |
79
+ | `database` | Create, inspect, and remove Prisma Postgres databases and their connection strings. |
80
+ | `bucket` | Create, list, and delete Tigris object-store buckets and their access keys. |
79
81
  | `app` | Build, run, deploy, inspect, open, stream logs, promote, roll back, and remove apps. |
80
82
 
81
83
  Common examples:
@@ -227,6 +227,79 @@ var MockApi = class MockApi {
227
227
  connectionString
228
228
  };
229
229
  }
230
+ listBucketsForProject(projectId, branchName) {
231
+ return (this.data.buckets ?? []).filter((bucket) => bucket.projectId === projectId && (!branchName || this.data.branches.find((branch) => branch.id === bucket.branchId && branch.name === branchName)));
232
+ }
233
+ getBucket(bucketId) {
234
+ return (this.data.buckets ?? []).find((bucket) => bucket.id === bucketId);
235
+ }
236
+ createBucket(input) {
237
+ this.data.buckets ??= [];
238
+ let branchId = null;
239
+ if (input.branchGitName) {
240
+ const branch = this.data.branches.find((b) => b.projectId === input.projectId && b.name === input.branchGitName);
241
+ if (!branch) return;
242
+ branchId = branch.id;
243
+ }
244
+ const bucket = {
245
+ id: `bkt_${this.data.buckets.length + 1e3}`,
246
+ projectId: input.projectId,
247
+ branchId,
248
+ name: input.name ?? `bucket-${this.data.buckets.length + 1e3}`,
249
+ status: "ready",
250
+ createdAt: "2026-06-09T00:00:00.000Z"
251
+ };
252
+ this.data.buckets.push(bucket);
253
+ return bucket;
254
+ }
255
+ deleteBucket(bucketId) {
256
+ this.data.buckets ??= [];
257
+ this.data.bucketKeys ??= [];
258
+ const bucket = this.getBucket(bucketId);
259
+ if (!bucket) return;
260
+ this.data.buckets = this.data.buckets.filter((candidate) => candidate.id !== bucketId);
261
+ this.data.bucketKeys = this.data.bucketKeys.filter((key) => key.bucketId !== bucketId);
262
+ return bucket;
263
+ }
264
+ listBucketKeys(bucketId) {
265
+ return (this.data.bucketKeys ?? []).filter((key) => key.bucketId === bucketId);
266
+ }
267
+ createBucketKey(input) {
268
+ const bucket = this.getBucket(input.bucketId);
269
+ if (!bucket) return;
270
+ this.data.bucketKeys ??= [];
271
+ const secretAccessKey = `secret-${input.bucketId}-${this.data.bucketKeys.length + 1}`;
272
+ const accessKeyId = `AKIA${input.bucketId.toUpperCase().replace(/_/g, "")}${this.data.bucketKeys.length + 1}`;
273
+ const endpoint = `https://fly.storage.tigris.dev`;
274
+ const bucketName = bucket.name;
275
+ const key = {
276
+ id: `bkey_${this.data.bucketKeys.length + 1e3}`,
277
+ bucketId: input.bucketId,
278
+ name: input.name ?? `key-${this.data.bucketKeys.length + 1e3}`,
279
+ role: input.role,
280
+ valueHint: `${accessKeyId.slice(0, 8)}...`,
281
+ createdAt: "2026-06-09T00:00:00.000Z",
282
+ secretAccessKey,
283
+ accessKeyId,
284
+ endpoint,
285
+ bucketName
286
+ };
287
+ this.data.bucketKeys.push(key);
288
+ return {
289
+ key,
290
+ secretAccessKey,
291
+ accessKeyId,
292
+ endpoint,
293
+ bucketName
294
+ };
295
+ }
296
+ deleteBucketKey(bucketId, keyId) {
297
+ this.data.bucketKeys ??= [];
298
+ const key = (this.data.bucketKeys ?? []).find((candidate) => candidate.id === keyId && candidate.bucketId === bucketId);
299
+ if (!key) return;
300
+ this.data.bucketKeys = this.data.bucketKeys.filter((candidate) => candidate.id !== keyId);
301
+ return key;
302
+ }
230
303
  };
231
304
  //#endregion
232
305
  export { MockApi };
package/dist/cli2.js CHANGED
@@ -9,6 +9,7 @@ import "./shell/prompt.js";
9
9
  import { createAppCommand } from "./commands/app/index.js";
10
10
  import { createAuthCommand } from "./commands/auth/index.js";
11
11
  import { createBranchCommand } from "./commands/branch/index.js";
12
+ import { createBucketCommand } from "./commands/bucket/index.js";
12
13
  import { createBuildCommand } from "./commands/build/index.js";
13
14
  import { createDatabaseCommand } from "./commands/database/index.js";
14
15
  import { getCliName, getCliVersion } from "./lib/version.js";
@@ -67,6 +68,7 @@ function createProgram(runtime) {
67
68
  program.addCommand(createBranchCommand(runtime));
68
69
  program.addCommand(createBuildCommand(runtime));
69
70
  program.addCommand(createDatabaseCommand(runtime));
71
+ program.addCommand(createBucketCommand(runtime));
70
72
  program.addCommand(createAppCommand(runtime));
71
73
  return program;
72
74
  }
@@ -0,0 +1,121 @@
1
+ import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
+ import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
+ import { configureRuntimeCommand } from "../../shell/runtime.js";
4
+ import { runCommand } from "../../shell/command-runner.js";
5
+ import { runBucketCreate, runBucketDelete, runBucketKeyCreate, runBucketKeyDelete, runBucketKeyList, runBucketList } from "../../controllers/bucket.js";
6
+ import { renderBucketCreate, renderBucketDelete, renderBucketKeyCreate, renderBucketKeyCreateStdout, renderBucketKeyDelete, renderBucketKeyList, renderBucketList, serializeBucketCreate, serializeBucketDelete, serializeBucketKeyCreate, serializeBucketKeyDelete, serializeBucketKeyList, serializeBucketList } from "../../presenters/bucket.js";
7
+ import { Command, Option } from "commander";
8
+ //#region src/commands/bucket/index.ts
9
+ function createBucketCommand(runtime) {
10
+ const bucket = attachCommandDescriptor(configureRuntimeCommand(new Command("bucket"), runtime), "bucket");
11
+ addCompactGlobalFlags(bucket);
12
+ bucket.addCommand(createBucketListCommand(runtime));
13
+ bucket.addCommand(createBucketCreateCommand(runtime));
14
+ bucket.addCommand(createBucketDeleteCommand(runtime));
15
+ bucket.addCommand(createBucketKeyCommand(runtime));
16
+ return bucket;
17
+ }
18
+ function addProjectAndBranchOptions(command) {
19
+ return command.addOption(new Option("--project <id-or-name>", "Project id or name")).addOption(new Option("--branch <git-name>", "Branch git name"));
20
+ }
21
+ function createBucketListCommand(runtime) {
22
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "bucket.list");
23
+ addProjectAndBranchOptions(command);
24
+ addGlobalFlags(command);
25
+ command.action(async (options) => {
26
+ const projectRef = options.project;
27
+ const branchName = options.branch;
28
+ await runCommand(runtime, "bucket.list", options, (context) => runBucketList(context, {
29
+ projectRef,
30
+ branchName
31
+ }), {
32
+ renderHuman: (context, descriptor, result) => renderBucketList(context, descriptor, result),
33
+ renderJson: (result) => serializeBucketList(result)
34
+ });
35
+ });
36
+ return command;
37
+ }
38
+ function createBucketCreateCommand(runtime) {
39
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "bucket.create");
40
+ command.addOption(new Option("--name <name>", "Bucket display name (auto-generated if omitted)"));
41
+ addProjectAndBranchOptions(command);
42
+ addGlobalFlags(command);
43
+ command.action(async (options) => {
44
+ const projectRef = options.project;
45
+ const branchName = options.branch;
46
+ const name = options.name;
47
+ await runCommand(runtime, "bucket.create", options, (context) => runBucketCreate(context, {
48
+ projectRef,
49
+ branchName,
50
+ name
51
+ }), {
52
+ renderHuman: (context, descriptor, result) => renderBucketCreate(context, descriptor, result),
53
+ renderJson: (result) => serializeBucketCreate(result)
54
+ });
55
+ });
56
+ return command;
57
+ }
58
+ function createBucketDeleteCommand(runtime) {
59
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("delete"), runtime), "bucket.delete");
60
+ command.argument("<bucketId>", "Bucket id");
61
+ addGlobalFlags(command);
62
+ command.action(async (bucketId, options) => {
63
+ await runCommand(runtime, "bucket.delete", options, (context) => runBucketDelete(context, bucketId), {
64
+ renderHuman: (context, descriptor, result) => renderBucketDelete(context, descriptor, result),
65
+ renderJson: (result) => serializeBucketDelete(result)
66
+ });
67
+ });
68
+ return command;
69
+ }
70
+ function createBucketKeyCommand(runtime) {
71
+ const key = attachCommandDescriptor(configureRuntimeCommand(new Command("key"), runtime), "bucket.key");
72
+ addCompactGlobalFlags(key);
73
+ key.addCommand(createBucketKeyListCommand(runtime));
74
+ key.addCommand(createBucketKeyCreateCommand(runtime));
75
+ key.addCommand(createBucketKeyDeleteCommand(runtime));
76
+ return key;
77
+ }
78
+ function createBucketKeyListCommand(runtime) {
79
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "bucket.key.list");
80
+ command.argument("<bucketId>", "Bucket id");
81
+ addGlobalFlags(command);
82
+ command.action(async (bucketId, options) => {
83
+ await runCommand(runtime, "bucket.key.list", options, (context) => runBucketKeyList(context, bucketId), {
84
+ renderHuman: (context, descriptor, result) => renderBucketKeyList(context, descriptor, result),
85
+ renderJson: (result) => serializeBucketKeyList(result)
86
+ });
87
+ });
88
+ return command;
89
+ }
90
+ function createBucketKeyCreateCommand(runtime) {
91
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "bucket.key.create");
92
+ command.argument("<bucketId>", "Bucket id").addOption(new Option("--role <role>", "Access role (default: read_write)").choices(["read", "read_write"])).addOption(new Option("--name <name>", "Key display name (auto-generated if omitted)"));
93
+ addGlobalFlags(command);
94
+ command.action(async (bucketId, options) => {
95
+ const role = options.role;
96
+ const name = options.name;
97
+ await runCommand(runtime, "bucket.key.create", options, (context) => runBucketKeyCreate(context, bucketId, {
98
+ role,
99
+ name
100
+ }), {
101
+ renderStdout: (context, descriptor, result) => renderBucketKeyCreateStdout(context, descriptor, result),
102
+ renderHuman: (context, descriptor, result) => renderBucketKeyCreate(context, descriptor, result),
103
+ renderJson: (result) => serializeBucketKeyCreate(result)
104
+ });
105
+ });
106
+ return command;
107
+ }
108
+ function createBucketKeyDeleteCommand(runtime) {
109
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("delete"), runtime), "bucket.key.delete");
110
+ command.argument("<bucketId>", "Bucket id").argument("<keyId>", "Key id");
111
+ addGlobalFlags(command);
112
+ command.action(async (bucketId, keyId, options) => {
113
+ await runCommand(runtime, "bucket.key.delete", options, (context) => runBucketKeyDelete(context, bucketId, keyId), {
114
+ renderHuman: (context, descriptor, result) => renderBucketKeyDelete(context, descriptor, result),
115
+ renderJson: (result) => serializeBucketKeyDelete(result)
116
+ });
117
+ });
118
+ return command;
119
+ }
120
+ //#endregion
121
+ export { createBucketCommand };
@@ -0,0 +1,265 @@
1
+ import { CliError, authRequiredError, workspaceRequiredError } from "../shell/errors.js";
2
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
3
+ import { requireComputeAuth } from "../lib/auth/guard.js";
4
+ import { requireAuthenticatedAuthState } from "./auth.js";
5
+ import { listFixtureWorkspaceProjects, listRealWorkspaceProjects } from "./project.js";
6
+ import { createManagementBucketProvider, normalizeBucket, normalizeKey } from "../lib/bucket/provider.js";
7
+ //#region src/controllers/bucket.ts
8
+ function isRealMode(context) {
9
+ return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
10
+ }
11
+ async function runBucketList(context, flags) {
12
+ const { provider, target } = await requireBucketContext(context, flags, "bucket list");
13
+ const buckets = await provider.listBuckets({
14
+ projectId: target.project.id,
15
+ branchName: flags.branchName,
16
+ signal: context.runtime.signal
17
+ });
18
+ return {
19
+ command: "bucket.list",
20
+ result: {
21
+ projectId: target.project.id,
22
+ projectName: target.project.name,
23
+ branchName: flags.branchName ?? null,
24
+ verboseContext: target,
25
+ buckets
26
+ },
27
+ warnings: [],
28
+ nextSteps: []
29
+ };
30
+ }
31
+ async function runBucketCreate(context, flags) {
32
+ const { provider, target } = await requireBucketContext(context, flags, "bucket create");
33
+ const bucket = await provider.createBucket({
34
+ projectId: target.project.id,
35
+ name: flags.name?.trim() || void 0,
36
+ branchGitName: flags.branchName,
37
+ signal: context.runtime.signal
38
+ });
39
+ return {
40
+ command: "bucket.create",
41
+ result: {
42
+ projectId: target.project.id,
43
+ projectName: target.project.name,
44
+ verboseContext: target,
45
+ bucket
46
+ },
47
+ warnings: [],
48
+ nextSteps: []
49
+ };
50
+ }
51
+ async function runBucketDelete(context, bucketId) {
52
+ const id = bucketId.trim();
53
+ if (!id) throw new CliError({
54
+ code: "USAGE_ERROR",
55
+ domain: "bucket",
56
+ summary: "Bucket id required",
57
+ why: "Bucket deletion needs a bucket id.",
58
+ fix: "Pass the bucket id to delete.",
59
+ exitCode: 2,
60
+ nextSteps: ["prisma-cli bucket list"]
61
+ });
62
+ await (await requireBucketProviderOnly(context)).deleteBucket(id, { signal: context.runtime.signal });
63
+ return {
64
+ command: "bucket.delete",
65
+ result: { bucket: { id } },
66
+ warnings: [],
67
+ nextSteps: []
68
+ };
69
+ }
70
+ async function runBucketKeyList(context, bucketId) {
71
+ const id = bucketId.trim();
72
+ if (!id) throw new CliError({
73
+ code: "USAGE_ERROR",
74
+ domain: "bucket",
75
+ summary: "Bucket id required",
76
+ why: "Bucket key listing needs a bucket id.",
77
+ fix: "Pass the bucket id.",
78
+ exitCode: 2,
79
+ nextSteps: ["prisma-cli bucket list"]
80
+ });
81
+ return {
82
+ command: "bucket.key.list",
83
+ result: {
84
+ bucketId: id,
85
+ keys: await (await requireBucketProviderOnly(context)).listKeys(id, { signal: context.runtime.signal })
86
+ },
87
+ warnings: [],
88
+ nextSteps: []
89
+ };
90
+ }
91
+ async function runBucketKeyCreate(context, bucketId, flags) {
92
+ const id = bucketId.trim();
93
+ if (!id) throw new CliError({
94
+ code: "USAGE_ERROR",
95
+ domain: "bucket",
96
+ summary: "Bucket id required",
97
+ why: "Bucket key creation needs a bucket id.",
98
+ fix: "Pass the bucket id.",
99
+ exitCode: 2,
100
+ nextSteps: ["prisma-cli bucket list"]
101
+ });
102
+ const role = resolveKeyRole(flags.role);
103
+ const created = await (await requireBucketProviderOnly(context)).createKey({
104
+ bucketId: id,
105
+ name: flags.name?.trim() || void 0,
106
+ role,
107
+ signal: context.runtime.signal
108
+ });
109
+ return {
110
+ command: "bucket.key.create",
111
+ result: {
112
+ bucketId: id,
113
+ key: created.key,
114
+ secretAccessKey: created.secretAccessKey,
115
+ accessKeyId: created.accessKeyId,
116
+ endpoint: created.endpoint,
117
+ bucketName: created.bucketName
118
+ },
119
+ warnings: [],
120
+ nextSteps: []
121
+ };
122
+ }
123
+ async function runBucketKeyDelete(context, bucketId, keyId) {
124
+ const bktId = bucketId.trim();
125
+ const kId = keyId.trim();
126
+ if (!bktId || !kId) throw new CliError({
127
+ code: "USAGE_ERROR",
128
+ domain: "bucket",
129
+ summary: "Bucket id and key id required",
130
+ why: "Bucket key deletion needs both a bucket id and a key id.",
131
+ fix: "Pass the bucket id and key id.",
132
+ exitCode: 2,
133
+ nextSteps: ["prisma-cli bucket key list <bucketId>"]
134
+ });
135
+ await (await requireBucketProviderOnly(context)).deleteKey(bktId, kId, { signal: context.runtime.signal });
136
+ return {
137
+ command: "bucket.key.delete",
138
+ result: { key: { id: kId } },
139
+ warnings: [],
140
+ nextSteps: []
141
+ };
142
+ }
143
+ async function resolveBucketProvider(context) {
144
+ if (isRealMode(context)) {
145
+ const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
146
+ if (!client) throw authRequiredError();
147
+ return createManagementBucketProvider(client);
148
+ }
149
+ return createFixtureBucketProvider(context);
150
+ }
151
+ async function requireBucketContext(context, flags, commandName) {
152
+ const workspace = (await requireAuthenticatedAuthState(context)).workspace;
153
+ if (!workspace) throw workspaceRequiredError();
154
+ if (isRealMode(context)) {
155
+ const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
156
+ if (!client) throw authRequiredError();
157
+ const targetResult = await resolveProjectTarget({
158
+ context,
159
+ workspace,
160
+ explicitProject: flags.projectRef,
161
+ listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
162
+ commandName
163
+ });
164
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
165
+ return {
166
+ provider: createManagementBucketProvider(client),
167
+ target: targetResult.value
168
+ };
169
+ }
170
+ const targetResult = await resolveProjectTarget({
171
+ context,
172
+ workspace,
173
+ explicitProject: flags.projectRef,
174
+ listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
175
+ commandName
176
+ });
177
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
178
+ return {
179
+ provider: createFixtureBucketProvider(context),
180
+ target: targetResult.value
181
+ };
182
+ }
183
+ async function requireBucketProviderOnly(context) {
184
+ await requireAuthenticatedAuthState(context);
185
+ return resolveBucketProvider(context);
186
+ }
187
+ function createFixtureBucketProvider(context) {
188
+ return {
189
+ async listBuckets(options) {
190
+ return context.api.listBucketsForProject(options.projectId, options.branchName).map((bucket) => normalizeBucket(bucket));
191
+ },
192
+ async createBucket(options) {
193
+ const created = context.api.createBucket({
194
+ projectId: options.projectId,
195
+ name: options.name,
196
+ branchGitName: options.branchGitName
197
+ });
198
+ if (!created) throw branchNotFoundError(options.branchGitName ?? "");
199
+ return normalizeBucket(created);
200
+ },
201
+ async deleteBucket(bucketId) {
202
+ if (!context.api.deleteBucket(bucketId)) throw bucketNotFoundError(bucketId);
203
+ },
204
+ async listKeys(bucketId) {
205
+ if (!context.api.getBucket(bucketId)) throw bucketNotFoundError(bucketId);
206
+ return context.api.listBucketKeys(bucketId).map((key) => normalizeKey(key));
207
+ },
208
+ async createKey(options) {
209
+ const created = context.api.createBucketKey({
210
+ bucketId: options.bucketId,
211
+ name: options.name,
212
+ role: options.role
213
+ });
214
+ if (!created) throw bucketNotFoundError(options.bucketId);
215
+ return {
216
+ key: normalizeKey(created.key),
217
+ secretAccessKey: created.secretAccessKey,
218
+ accessKeyId: created.accessKeyId,
219
+ endpoint: created.endpoint,
220
+ bucketName: created.bucketName
221
+ };
222
+ },
223
+ async deleteKey(bucketId, keyId) {
224
+ if (!context.api.deleteBucketKey(bucketId, keyId)) throw keyNotFoundError(keyId, bucketId);
225
+ }
226
+ };
227
+ }
228
+ function resolveKeyRole(role) {
229
+ return role === "read" ? "read" : "read_write";
230
+ }
231
+ function branchNotFoundError(branchGitName) {
232
+ return new CliError({
233
+ code: "BRANCH_NOT_FOUND",
234
+ domain: "bucket",
235
+ summary: "Branch not found",
236
+ why: `No branch matched "${branchGitName}" in the resolved project.`,
237
+ fix: "Pass a branch git name from prisma-cli branch list.",
238
+ exitCode: 1,
239
+ nextSteps: ["prisma-cli branch list"]
240
+ });
241
+ }
242
+ function bucketNotFoundError(bucketId) {
243
+ return new CliError({
244
+ code: "BUCKET_NOT_FOUND",
245
+ domain: "bucket",
246
+ summary: "Bucket not found",
247
+ why: `No bucket matched "${bucketId}".`,
248
+ fix: "Pass a bucket id from prisma-cli bucket list.",
249
+ exitCode: 1,
250
+ nextSteps: ["prisma-cli bucket list"]
251
+ });
252
+ }
253
+ function keyNotFoundError(keyId, bucketId) {
254
+ return new CliError({
255
+ code: "BUCKET_KEY_NOT_FOUND",
256
+ domain: "bucket",
257
+ summary: "Bucket key not found",
258
+ why: `No key matched "${keyId}" for bucket "${bucketId}".`,
259
+ fix: "Pass a key id from prisma-cli bucket key list <bucketId>.",
260
+ exitCode: 1,
261
+ nextSteps: [`prisma-cli bucket key list ${bucketId}`]
262
+ });
263
+ }
264
+ //#endregion
265
+ export { runBucketCreate, runBucketDelete, runBucketKeyCreate, runBucketKeyDelete, runBucketKeyList, runBucketList };
@@ -0,0 +1,139 @@
1
+ import { CliError } from "../../shell/errors.js";
2
+ //#region src/lib/bucket/provider.ts
3
+ function createManagementBucketProvider(client) {
4
+ return {
5
+ async listBuckets(options) {
6
+ const buckets = [];
7
+ let cursor;
8
+ while (true) {
9
+ const result = await client.GET("/v1/buckets", {
10
+ params: { query: {
11
+ projectId: options.projectId,
12
+ branchGitName: options.branchName,
13
+ cursor
14
+ } },
15
+ signal: options.signal
16
+ });
17
+ if (result.error || !result.data) throw bucketApiError("Failed to list buckets", result.response, result.error);
18
+ const data = result.data;
19
+ buckets.push(...data.data);
20
+ if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
21
+ cursor = data.pagination.nextCursor;
22
+ }
23
+ return buckets.map(normalizeBucket);
24
+ },
25
+ async createBucket(options) {
26
+ const result = await client.POST("/v1/buckets", {
27
+ body: {
28
+ projectId: options.projectId,
29
+ ...options.name ? { name: options.name } : {},
30
+ ...options.branchGitName ? { branchGitName: options.branchGitName } : {}
31
+ },
32
+ signal: options.signal
33
+ });
34
+ if (result.error || !result.data) throw bucketApiError("Failed to create bucket", result.response, result.error);
35
+ const data = result.data;
36
+ return normalizeBucket(data.data);
37
+ },
38
+ async deleteBucket(bucketId, options) {
39
+ const result = await client.DELETE("/v1/buckets/{bucketId}", {
40
+ params: { path: { bucketId } },
41
+ signal: options?.signal
42
+ });
43
+ if (result.error) throw bucketApiError("Failed to delete bucket", result.response, result.error);
44
+ },
45
+ async listKeys(bucketId, options) {
46
+ const keys = [];
47
+ let cursor;
48
+ while (true) {
49
+ const result = await client.GET("/v1/buckets/{bucketId}/keys", {
50
+ params: {
51
+ path: { bucketId },
52
+ query: { cursor }
53
+ },
54
+ signal: options?.signal
55
+ });
56
+ if (result.error || !result.data) throw bucketApiError("Failed to list bucket keys", result.response, result.error);
57
+ const data = result.data;
58
+ keys.push(...data.data);
59
+ if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
60
+ cursor = data.pagination.nextCursor;
61
+ }
62
+ return keys.map(normalizeKey);
63
+ },
64
+ async createKey(options) {
65
+ const result = await client.POST("/v1/buckets/{bucketId}/keys", {
66
+ params: { path: { bucketId: options.bucketId } },
67
+ body: {
68
+ role: options.role,
69
+ ...options.name ? { name: options.name } : {}
70
+ },
71
+ signal: options.signal
72
+ });
73
+ if (result.error || !result.data) throw bucketApiError("Failed to create bucket key", result.response, result.error);
74
+ const raw = result.data.data;
75
+ const secretAccessKey = raw.secretAccessKey;
76
+ const accessKeyId = raw.accessKeyId;
77
+ const endpoint = raw.endpoint;
78
+ const bucketName = raw.bucketName;
79
+ if (!secretAccessKey || !accessKeyId || !endpoint || !bucketName) throw new CliError({
80
+ code: "BUCKET_KEY_SECRET_MISSING",
81
+ domain: "bucket",
82
+ summary: "Created bucket key did not return credentials",
83
+ why: "Bucket key credentials are one-time-view secrets, but the Management API did not include them in this create response.",
84
+ fix: "Create another bucket key and store the returned credentials immediately.",
85
+ exitCode: 1,
86
+ nextSteps: [`prisma-cli bucket key create ${options.bucketId}`]
87
+ });
88
+ return {
89
+ key: normalizeKey(raw),
90
+ secretAccessKey,
91
+ accessKeyId,
92
+ endpoint,
93
+ bucketName
94
+ };
95
+ },
96
+ async deleteKey(bucketId, keyId, options) {
97
+ const result = await client.DELETE("/v1/buckets/{bucketId}/keys/{keyId}", {
98
+ params: { path: {
99
+ bucketId,
100
+ keyId
101
+ } },
102
+ signal: options?.signal
103
+ });
104
+ if (result.error) throw bucketApiError("Failed to delete bucket key", result.response, result.error);
105
+ }
106
+ };
107
+ }
108
+ function normalizeBucket(raw) {
109
+ return {
110
+ id: raw.id,
111
+ name: raw.name,
112
+ status: raw.status,
113
+ branchId: raw.branchId,
114
+ createdAt: raw.createdAt
115
+ };
116
+ }
117
+ function normalizeKey(raw) {
118
+ return {
119
+ id: raw.id,
120
+ name: raw.name,
121
+ role: raw.role,
122
+ valueHint: raw.valueHint,
123
+ createdAt: raw.createdAt
124
+ };
125
+ }
126
+ function bucketApiError(summary, response, error) {
127
+ const status = response?.status ?? 0;
128
+ return new CliError({
129
+ code: error?.error?.code ?? "BUCKET_API_ERROR",
130
+ domain: "bucket",
131
+ summary,
132
+ why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
133
+ fix: error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
134
+ exitCode: 1,
135
+ nextSteps: []
136
+ });
137
+ }
138
+ //#endregion
139
+ export { createManagementBucketProvider, normalizeBucket, normalizeKey };
@@ -0,0 +1,174 @@
1
+ import { formatDescriptorLabel } from "../shell/command-meta.js";
2
+ import { formatColumns, renderSummaryLine } from "../shell/ui.js";
3
+ import { renderMutate, serializeList } from "../output/patterns.js";
4
+ import { renderResolvedProjectContextBlock, stripVerboseContext } from "./verbose-context.js";
5
+ //#region src/presenters/bucket.ts
6
+ function renderBucketList(context, descriptor, result) {
7
+ const ui = context.ui;
8
+ const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing object-store buckets for the resolved project.")}`, ""];
9
+ const rail = ui.dim("│");
10
+ lines.push(`${rail} ${ui.accent("project:")} ${result.projectName}`);
11
+ if (result.branchName) lines.push(`${rail} ${ui.accent("branch:")} ${result.branchName}`);
12
+ lines.push(rail);
13
+ if (result.buckets.length === 0) {
14
+ lines.push(`${rail} ${ui.dim("No buckets found.")}`);
15
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
16
+ return lines;
17
+ }
18
+ const rows = result.buckets.map((bucket) => [
19
+ bucket.name,
20
+ bucket.id,
21
+ bucket.status,
22
+ bucket.branchId ?? "unscoped",
23
+ bucket.createdAt
24
+ ]);
25
+ const widths = [
26
+ Math.max(4, ...rows.map((row) => row[0].length)),
27
+ Math.max(2, ...rows.map((row) => row[1].length)),
28
+ Math.max(6, ...rows.map((row) => row[2].length)),
29
+ Math.max(6, ...rows.map((row) => row[3].length)),
30
+ Math.max(7, ...rows.map((row) => row[4].length))
31
+ ];
32
+ lines.push(`${rail} ${ui.accent(formatColumns([
33
+ "Name",
34
+ "Id",
35
+ "Status",
36
+ "Branch",
37
+ "Created"
38
+ ], widths))}`);
39
+ for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
40
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
41
+ return lines;
42
+ }
43
+ function serializeBucketList(result) {
44
+ return {
45
+ context: {
46
+ project: result.projectName,
47
+ ...result.branchName ? { branch: result.branchName } : {}
48
+ },
49
+ items: result.buckets.map((bucket) => ({
50
+ name: bucket.name,
51
+ id: bucket.id,
52
+ status: bucket.status
53
+ })),
54
+ count: result.buckets.length,
55
+ projectId: result.projectId,
56
+ branchName: result.branchName,
57
+ buckets: result.buckets
58
+ };
59
+ }
60
+ function renderBucketCreate(context, _descriptor, result) {
61
+ const ui = context.ui;
62
+ return ["Creating bucket...", renderSummaryLine(ui, "success", `Created bucket "${result.bucket.name}" in ${formatBucketTarget(result.projectName, result.bucket.branchId)}.`)];
63
+ }
64
+ function serializeBucketCreate(result) {
65
+ return stripVerboseContext(result);
66
+ }
67
+ function renderBucketDelete(context, descriptor, result) {
68
+ return renderMutate({
69
+ title: "Deleting object-store bucket.",
70
+ descriptor,
71
+ context: [{
72
+ key: "bucket",
73
+ value: result.bucket.id,
74
+ tone: "dim"
75
+ }],
76
+ operationDescription: "Deleting bucket",
77
+ operationCount: 1,
78
+ details: ["Bucket and all its access keys were removed."]
79
+ }, context.ui);
80
+ }
81
+ function serializeBucketDelete(result) {
82
+ return { bucket: result.bucket };
83
+ }
84
+ function renderBucketKeyList(context, descriptor, result) {
85
+ const ui = context.ui;
86
+ const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing access keys for bucket.")}`, ""];
87
+ const rail = ui.dim("│");
88
+ lines.push(`${rail} ${ui.accent("bucket:")} ${result.bucketId}`);
89
+ lines.push(rail);
90
+ if (result.keys.length === 0) {
91
+ lines.push(`${rail} ${ui.dim("No keys found.")}`);
92
+ return lines;
93
+ }
94
+ const rows = result.keys.map((key) => [
95
+ key.name,
96
+ key.id,
97
+ key.role,
98
+ key.valueHint,
99
+ key.createdAt
100
+ ]);
101
+ const widths = [
102
+ Math.max(4, ...rows.map((row) => row[0].length)),
103
+ Math.max(2, ...rows.map((row) => row[1].length)),
104
+ Math.max(4, ...rows.map((row) => row[2].length)),
105
+ Math.max(4, ...rows.map((row) => row[3].length)),
106
+ Math.max(7, ...rows.map((row) => row[4].length))
107
+ ];
108
+ lines.push(`${rail} ${ui.accent(formatColumns([
109
+ "Name",
110
+ "Id",
111
+ "Role",
112
+ "Hint",
113
+ "Created"
114
+ ], widths))}`);
115
+ for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
116
+ return lines;
117
+ }
118
+ function serializeBucketKeyList(result) {
119
+ return {
120
+ ...serializeList({
121
+ context: { bucket: result.bucketId },
122
+ items: result.keys.map((key) => ({
123
+ noun: "key",
124
+ label: key.name,
125
+ id: key.id,
126
+ status: null
127
+ }))
128
+ }),
129
+ bucketId: result.bucketId,
130
+ keys: result.keys
131
+ };
132
+ }
133
+ function renderBucketKeyCreateStdout(_context, _descriptor, result) {
134
+ return [
135
+ `S3_ENDPOINT=${result.endpoint}`,
136
+ `S3_ACCESS_KEY_ID=${result.accessKeyId}`,
137
+ `S3_SECRET_ACCESS_KEY=${result.secretAccessKey}`,
138
+ `S3_BUCKET=${result.bucketName}`
139
+ ];
140
+ }
141
+ function renderBucketKeyCreate(context, _descriptor, result) {
142
+ const ui = context.ui;
143
+ return [
144
+ "Creating bucket key...",
145
+ renderSummaryLine(ui, "success", `Created key "${result.key.name}" for bucket "${result.bucketName}".`),
146
+ " The credentials below are shown once — copy them now.",
147
+ " Set these environment variables to use this bucket:"
148
+ ];
149
+ }
150
+ function serializeBucketKeyCreate(result) {
151
+ return result;
152
+ }
153
+ function renderBucketKeyDelete(context, descriptor, result) {
154
+ return renderMutate({
155
+ title: "Deleting bucket access key.",
156
+ descriptor,
157
+ context: [{
158
+ key: "key",
159
+ value: result.key.id,
160
+ tone: "dim"
161
+ }],
162
+ operationDescription: "Deleting bucket key",
163
+ operationCount: 1,
164
+ details: ["The access key was revoked and removed."]
165
+ }, context.ui);
166
+ }
167
+ function serializeBucketKeyDelete(result) {
168
+ return { key: result.key };
169
+ }
170
+ function formatBucketTarget(projectName, branchId) {
171
+ return branchId ? `${projectName} / ${branchId}` : projectName;
172
+ }
173
+ //#endregion
174
+ export { renderBucketCreate, renderBucketDelete, renderBucketKeyCreate, renderBucketKeyCreateStdout, renderBucketKeyDelete, renderBucketKeyList, renderBucketList, serializeBucketCreate, serializeBucketDelete, serializeBucketKeyCreate, serializeBucketKeyDelete, serializeBucketKeyList, serializeBucketList };
@@ -249,6 +249,105 @@ const DESCRIPTORS = [
249
249
  "prisma-cli database connection create db_123"
250
250
  ]
251
251
  },
252
+ {
253
+ id: "bucket",
254
+ path: ["prisma", "bucket"],
255
+ description: "Manage object-store buckets for a project",
256
+ examples: [
257
+ "prisma-cli bucket list",
258
+ "prisma-cli bucket create",
259
+ "prisma-cli bucket key create bkt_123"
260
+ ]
261
+ },
262
+ {
263
+ id: "bucket.list",
264
+ path: [
265
+ "prisma",
266
+ "bucket",
267
+ "list"
268
+ ],
269
+ description: "List object-store buckets for the resolved project",
270
+ examples: [
271
+ "prisma-cli bucket list",
272
+ "prisma-cli bucket list --branch preview",
273
+ "prisma-cli bucket list --json"
274
+ ]
275
+ },
276
+ {
277
+ id: "bucket.create",
278
+ path: [
279
+ "prisma",
280
+ "bucket",
281
+ "create"
282
+ ],
283
+ description: "Create an object-store bucket",
284
+ examples: [
285
+ "prisma-cli bucket create",
286
+ "prisma-cli bucket create --name my-store",
287
+ "prisma-cli bucket create --branch preview --json"
288
+ ]
289
+ },
290
+ {
291
+ id: "bucket.delete",
292
+ path: [
293
+ "prisma",
294
+ "bucket",
295
+ "delete"
296
+ ],
297
+ description: "Delete a bucket and all its access keys",
298
+ examples: ["prisma-cli bucket delete bkt_123"]
299
+ },
300
+ {
301
+ id: "bucket.key",
302
+ path: [
303
+ "prisma",
304
+ "bucket",
305
+ "key"
306
+ ],
307
+ description: "Manage access keys for an object-store bucket",
308
+ examples: [
309
+ "prisma-cli bucket key list bkt_123",
310
+ "prisma-cli bucket key create bkt_123",
311
+ "prisma-cli bucket key delete bkt_123 bkey_456"
312
+ ]
313
+ },
314
+ {
315
+ id: "bucket.key.list",
316
+ path: [
317
+ "prisma",
318
+ "bucket",
319
+ "key",
320
+ "list"
321
+ ],
322
+ description: "List access keys for a bucket",
323
+ examples: ["prisma-cli bucket key list bkt_123", "prisma-cli bucket key list bkt_123 --json"]
324
+ },
325
+ {
326
+ id: "bucket.key.create",
327
+ path: [
328
+ "prisma",
329
+ "bucket",
330
+ "key",
331
+ "create"
332
+ ],
333
+ description: "Create a bucket access key and print its one-time credentials",
334
+ examples: [
335
+ "prisma-cli bucket key create bkt_123",
336
+ "prisma-cli bucket key create bkt_123 --role read",
337
+ "prisma-cli bucket key create bkt_123 --name ci-key --role read_write"
338
+ ]
339
+ },
340
+ {
341
+ id: "bucket.key.delete",
342
+ path: [
343
+ "prisma",
344
+ "bucket",
345
+ "key",
346
+ "delete"
347
+ ],
348
+ description: "Revoke and delete a bucket access key",
349
+ examples: ["prisma-cli bucket key delete bkt_123 bkey_456"]
350
+ },
252
351
  {
253
352
  id: "git",
254
353
  path: ["prisma", "git"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-dev.129.1",
3
+ "version": "3.0.0-dev.131.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -46,7 +46,7 @@
46
46
  "@clack/prompts": "^1.5.0",
47
47
  "@prisma/compute-sdk": "0.34.0",
48
48
  "@prisma/credentials-store": "^7.8.0",
49
- "@prisma/management-api-sdk": "^1.46.0",
49
+ "@prisma/management-api-sdk": "^1.50.0",
50
50
  "better-result": "^2.9.2",
51
51
  "colorette": "^2.0.20",
52
52
  "commander": "^14.0.3",