@prisma/cli 3.0.0-dev.97.1 → 8.0.0-rc.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.
Files changed (99) hide show
  1. package/README.md +3 -14
  2. package/dist/adapters/local-state.js +14 -3
  3. package/dist/{lib/auth → auth}/client.js +11 -3
  4. package/dist/auth/errors.js +50 -0
  5. package/dist/{lib/auth → auth}/guard.js +7 -6
  6. package/dist/{lib/auth → auth}/login.js +147 -6
  7. package/dist/{lib/auth/auth-ops.js → auth/operations.js} +63 -13
  8. package/dist/auth/recipient.js +42 -0
  9. package/dist/{adapters → auth}/token-storage.js +28 -7
  10. package/dist/auth/workspaces.js +171 -0
  11. package/dist/cli-command.js +14 -0
  12. package/dist/cli-name.js +12 -0
  13. package/dist/cli.js +1 -1
  14. package/dist/cli2.js +25 -8
  15. package/dist/command-arguments.js +12 -0
  16. package/dist/commands/agent/index.js +60 -0
  17. package/dist/commands/app/index.js +11 -6
  18. package/dist/commands/auth/index.js +2 -3
  19. package/dist/commands/bucket/index.js +123 -0
  20. package/dist/commands/build/index.js +29 -0
  21. package/dist/commands/database/index.js +92 -2
  22. package/dist/commands/feedback/index.js +20 -0
  23. package/dist/commands/git/index.js +1 -1
  24. package/dist/commands/init/index.js +33 -0
  25. package/dist/commands/project/index.js +54 -5
  26. package/dist/controllers/agent-setup.js +52 -0
  27. package/dist/controllers/agent.js +228 -0
  28. package/dist/controllers/app-env-api.js +3 -2
  29. package/dist/controllers/app-env-file.js +1 -1
  30. package/dist/controllers/app-env.js +5 -5
  31. package/dist/controllers/app.js +423 -358
  32. package/dist/controllers/auth.js +62 -254
  33. package/dist/controllers/branch.js +5 -16
  34. package/dist/controllers/bucket.js +184 -0
  35. package/dist/controllers/build.js +88 -0
  36. package/dist/controllers/database.js +230 -85
  37. package/dist/controllers/feedback.js +90 -0
  38. package/dist/controllers/init.js +814 -0
  39. package/dist/controllers/project.js +345 -184
  40. package/dist/controllers/select-prompt-port.js +1 -0
  41. package/dist/{shell/errors.js → errors.js} +8 -40
  42. package/dist/lib/agent/cli-command.js +20 -0
  43. package/dist/lib/agent/constants.js +12 -0
  44. package/dist/lib/agent/package-manager.js +99 -0
  45. package/dist/lib/agent/setup-status.js +83 -0
  46. package/dist/lib/app/app-provider.js +168 -95
  47. package/dist/lib/app/branch-database-deploy.js +70 -42
  48. package/dist/lib/app/branch-database.js +39 -20
  49. package/dist/lib/app/build-settings.js +8 -3
  50. package/dist/lib/app/build.js +16 -14
  51. package/dist/lib/app/bun-project.js +1 -1
  52. package/dist/lib/app/compute-config.js +29 -31
  53. package/dist/lib/app/deploy-plan.js +1 -0
  54. package/dist/lib/app/deploy-progress.js +7 -7
  55. package/dist/lib/app/env-config.js +1 -1
  56. package/dist/lib/app/env-file.js +2 -2
  57. package/dist/lib/app/env-vars.js +1 -1
  58. package/dist/lib/app/local-dev.js +1 -1
  59. package/dist/lib/app/production-deploy-gate.js +3 -2
  60. package/dist/lib/bucket/provider.js +139 -0
  61. package/dist/lib/database/provider.js +235 -17
  62. package/dist/lib/diagnostics.js +2 -1
  63. package/dist/lib/feedback.js +15 -0
  64. package/dist/lib/git/local-branch.js +1 -1
  65. package/dist/lib/project/interactive-setup.js +6 -5
  66. package/dist/lib/project/local-pin.js +1 -1
  67. package/dist/lib/project/provider.js +93 -0
  68. package/dist/lib/project/resolution.js +11 -8
  69. package/dist/lib/project/setup.js +9 -6
  70. package/dist/lib/version.js +3 -2
  71. package/dist/lib/workspace-id.js +18 -0
  72. package/dist/payload-B88Qvzxk-CrtTXSOe.js +34 -0
  73. package/dist/presenters/agent.js +74 -0
  74. package/dist/presenters/app.js +33 -7
  75. package/dist/presenters/auth.js +3 -2
  76. package/dist/presenters/bucket.js +174 -0
  77. package/dist/presenters/database.js +193 -5
  78. package/dist/presenters/feedback.js +26 -0
  79. package/dist/presenters/init.js +30 -0
  80. package/dist/presenters/project.js +98 -4
  81. package/dist/shell/cli-command.js +2 -0
  82. package/dist/shell/command-meta.js +303 -4
  83. package/dist/shell/command-runner.js +13 -6
  84. package/dist/shell/help.js +6 -5
  85. package/dist/shell/output.js +65 -3
  86. package/dist/shell/prompt.js +12 -5
  87. package/dist/shell/runtime.js +4 -21
  88. package/dist/state-dir.js +12 -0
  89. package/dist/{shell/update-check.js → update-check.js} +6 -5
  90. package/dist/v8/cli.js +14699 -0
  91. package/dist/v8/sender.js +192 -0
  92. package/package.json +21 -13
  93. package/dist/adapters/mock-api.js +0 -136
  94. package/dist/lib/app/app-interaction.js +0 -5
  95. package/dist/shell/command-arguments.js +0 -6
  96. package/dist/use-cases/auth.js +0 -145
  97. package/dist/use-cases/branch.js +0 -47
  98. package/dist/use-cases/create-cli-gateways.js +0 -68
  99. package/dist/use-cases/project.js +0 -30
@@ -1,40 +1,45 @@
1
- import { CLIENT_ID, SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
2
- import { FileTokenStorage, WorkspaceSelectionError } from "../adapters/token-storage.js";
3
- import { authRequiredError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceSwitchUnavailableError } from "../shell/errors.js";
1
+ import { resolvePrismaCliPackageCommand } from "../lib/agent/cli-command.js";
2
+ import { PRISMA_AGENT_INSTALL_ARGS } from "../lib/agent/constants.js";
3
+ import { isLikelyProjectDirectory, readPrismaAgentSetupStatus, resolvePrismaAgentSetupCwd, shouldOfferPrismaAgentSetup } from "../lib/agent/setup-status.js";
4
+ import { authRequiredError, usageError } from "../errors.js";
5
+ import { performLogin, performLogout, readAuthState, storeLegacyCredential } from "../auth/operations.js";
4
6
  import { canPrompt } from "../shell/runtime.js";
5
- import { performLogin, performLogout, readAuthState } from "../lib/auth/auth-ops.js";
6
- import { createAuthUseCases } from "../use-cases/auth.js";
7
- import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
7
+ import { workspaceSwitchUnavailableError } from "../auth/errors.js";
8
+ import { listAuthWorkspaces, logoutAuthWorkspace, switchAuthWorkspace } from "../auth/workspaces.js";
8
9
  import { createSelectPromptPort } from "./select-prompt-port.js";
9
- import { createManagementApiSdk } from "@prisma/management-api-sdk";
10
+ import { SERVICE_TOKEN_ENV_VAR } from "@prisma/cli-engine";
10
11
  //#region src/controllers/auth.ts
11
- function isRealMode(context) {
12
- return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
12
+ function workspaceOperationContext(context) {
13
+ return {
14
+ env: context.runtime.env,
15
+ signal: context.runtime.signal
16
+ };
13
17
  }
14
- async function runAuthLogin(context, options) {
15
- let result;
16
- if (isRealMode(context)) {
17
- await performLogin(context.runtime.env, context.runtime.signal);
18
- result = await readAuthState(context.runtime.env, context.runtime.signal);
19
- } else result = await loginWithSelectionFlow(context, createAuthUseCases(createCliUseCaseGateways(context)), options);
20
- return createAuthSuccess("auth.login", result, ["prisma-cli auth whoami", "prisma-cli project list"]);
18
+ async function runAuthLogin(context) {
19
+ const credential = await performLogin(context.runtime.env, context.runtime.signal);
20
+ await storeLegacyCredential(context.runtime.env, credential, context.runtime.signal);
21
+ let result = await readAuthState(context.runtime.env, context.runtime.signal);
22
+ const agentSetupTipCommand = await resolveAgentSetupTipCommand(context);
23
+ if (agentSetupTipCommand) result = {
24
+ ...result,
25
+ agentSetupTip: { command: agentSetupTipCommand }
26
+ };
27
+ return createAuthSuccess("auth.login", result, [
28
+ "prisma-cli auth whoami",
29
+ "prisma-cli project list",
30
+ ...result.agentSetupTip ? [result.agentSetupTip.command] : []
31
+ ]);
21
32
  }
22
33
  async function runAuthLogout(context) {
23
- let result;
24
- if (isRealMode(context)) {
25
- await performLogout(context.runtime.env, context.runtime.signal);
26
- result = await readAuthState(context.runtime.env, context.runtime.signal);
27
- } else result = await createAuthUseCases(createCliUseCaseGateways(context)).logout();
28
- return createAuthSuccess("auth.logout", result, ["prisma-cli auth login"]);
34
+ await performLogout(context.runtime.env, context.runtime.signal);
35
+ return createAuthSuccess("auth.logout", await readAuthState(context.runtime.env, context.runtime.signal), ["prisma-cli auth login"]);
29
36
  }
30
37
  async function runAuthWhoAmI(context) {
31
- let result;
32
- if (isRealMode(context)) result = await readAuthState(context.runtime.env, context.runtime.signal);
33
- else result = await createAuthUseCases(createCliUseCaseGateways(context)).whoami();
38
+ const result = await readAuthState(context.runtime.env, context.runtime.signal);
34
39
  return createAuthSuccess("auth.whoami", result, result.authenticated ? [] : ["prisma-cli auth login"]);
35
40
  }
36
41
  async function runAuthWorkspaceList(context) {
37
- const result = isRealMode(context) ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces();
42
+ const result = await listAuthWorkspaces(workspaceOperationContext(context));
38
43
  return {
39
44
  command: "auth.workspace.list",
40
45
  result,
@@ -47,14 +52,14 @@ async function runAuthWorkspaceUse(context, workspaceRef) {
47
52
  const selectedWorkspaceRef = trimmedWorkspaceRef ? trimmedWorkspaceRef : await selectWorkspaceSession(context);
48
53
  return {
49
54
  command: "auth.workspace.use",
50
- result: isRealMode(context) ? await useRealAuthWorkspace(context, selectedWorkspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).useWorkspace(selectedWorkspaceRef),
55
+ result: await switchAuthWorkspace(workspaceOperationContext(context), selectedWorkspaceRef),
51
56
  warnings: [],
52
57
  nextSteps: ["prisma-cli auth whoami", "prisma-cli project list"]
53
58
  };
54
59
  }
55
60
  async function runAuthWorkspaceLogout(context, workspaceRef) {
56
61
  if (!workspaceRef?.trim()) throw usageError("Workspace required", "auth workspace logout needs a workspace id or cached workspace name.", "Pass a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
57
- const result = isRealMode(context) ? await logoutRealAuthWorkspace(context, workspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).logoutWorkspace(workspaceRef);
62
+ const result = await logoutAuthWorkspace(workspaceOperationContext(context), workspaceRef);
58
63
  return {
59
64
  command: "auth.workspace.logout",
60
65
  result,
@@ -63,107 +68,16 @@ async function runAuthWorkspaceLogout(context, workspaceRef) {
63
68
  };
64
69
  }
65
70
  async function requireAuthenticatedAuthState(context) {
66
- if (isRealMode(context)) {
67
- const current = await readAuthState(context.runtime.env, context.runtime.signal);
68
- if (current.authenticated) return current;
69
- if (!canPrompt(context)) throw authRequiredError();
70
- await performLogin(context.runtime.env, context.runtime.signal);
71
- return readAuthState(context.runtime.env, context.runtime.signal);
72
- }
73
- const useCases = createAuthUseCases(createCliUseCaseGateways(context));
74
- const current = await useCases.whoami();
71
+ const current = await readAuthState(context.runtime.env, context.runtime.signal);
75
72
  if (current.authenticated) return current;
76
73
  if (!canPrompt(context)) throw authRequiredError();
77
- return loginWithSelectionFlow(context, useCases, {});
78
- }
79
- async function listRealAuthWorkspaces(context) {
80
- const rawServiceToken = context.runtime.env[SERVICE_TOKEN_ENV_VAR];
81
- const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
82
- const localWorkspaces = await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
83
- if (rawServiceToken !== void 0) {
84
- const authState = await readAuthState(context.runtime.env, context.runtime.signal);
85
- return {
86
- authSource: authState.authenticated ? "service_token" : "none",
87
- activeWorkspace: authState.workspace,
88
- workspaces: [...authState.workspace ? [{
89
- ...authState.workspace,
90
- credentialWorkspaceId: null,
91
- active: true,
92
- source: "service_token",
93
- switchable: false,
94
- lastSeenAt: null
95
- }] : [], ...localWorkspaces.map((workspace) => ({
96
- ...toAuthWorkspace(workspace),
97
- credentialWorkspaceId: workspace.credentialWorkspaceId,
98
- active: false,
99
- source: "oauth",
100
- switchable: false,
101
- lastSeenAt: workspace.lastSeenAt
102
- }))]
103
- };
104
- }
105
- const active = localWorkspaces.find((workspace) => workspace.active) ?? null;
106
- return {
107
- authSource: localWorkspaces.length > 0 ? "oauth" : "none",
108
- activeWorkspace: active ? toAuthWorkspace(active) : null,
109
- workspaces: localWorkspaces.map((workspace) => ({
110
- ...toAuthWorkspace(workspace),
111
- credentialWorkspaceId: workspace.credentialWorkspaceId,
112
- active: workspace.active,
113
- source: "oauth",
114
- switchable: true,
115
- lastSeenAt: workspace.lastSeenAt
116
- }))
117
- };
118
- }
119
- async function useRealAuthWorkspace(context, workspaceRef) {
120
- if (context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
121
- const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
122
- await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
123
- try {
124
- const result = await storage.useWorkspace(workspaceRef);
125
- return {
126
- previousWorkspace: result.previous ? toAuthWorkspace(result.previous) : null,
127
- workspace: toAuthWorkspace(result.selected)
128
- };
129
- } catch (error) {
130
- if (error instanceof WorkspaceSelectionError) {
131
- if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
132
- id: match.id,
133
- name: match.name,
134
- credentialWorkspaceId: match.credentialWorkspaceId
135
- })));
136
- throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
137
- }
138
- throw error;
139
- }
140
- }
141
- async function logoutRealAuthWorkspace(context, workspaceRef) {
142
- const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
143
- await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
144
- try {
145
- const result = await storage.logoutWorkspace(workspaceRef);
146
- return {
147
- workspace: toAuthWorkspace(result.workspace),
148
- wasActive: result.wasActive,
149
- activeWorkspace: result.activeWorkspace ? toAuthWorkspace(result.activeWorkspace) : null
150
- };
151
- } catch (error) {
152
- if (error instanceof WorkspaceSelectionError) {
153
- if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
154
- id: match.id,
155
- name: match.name,
156
- credentialWorkspaceId: match.credentialWorkspaceId
157
- })));
158
- throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
159
- }
160
- throw error;
161
- }
74
+ const credential = await performLogin(context.runtime.env, context.runtime.signal);
75
+ await storeLegacyCredential(context.runtime.env, credential, context.runtime.signal);
76
+ return readAuthState(context.runtime.env, context.runtime.signal);
162
77
  }
163
78
  async function selectWorkspaceSession(context) {
164
- const realMode = isRealMode(context);
165
- if (realMode && context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
166
- const workspaces = (realMode ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces()).workspaces.filter((workspace) => workspace.switchable);
79
+ if (context.runtime.env[SERVICE_TOKEN_ENV_VAR] !== void 0) throw workspaceSwitchUnavailableError();
80
+ const workspaces = (await listAuthWorkspaces(workspaceOperationContext(context))).workspaces.filter((workspace) => workspace.switchable);
167
81
  if (workspaces.length === 0) throw usageError("No authenticated workspaces", "There are no local OAuth workspace sessions to select.", "Run prisma-cli auth login and authorize a workspace.", ["prisma-cli auth login"], "auth");
168
82
  if (workspaces.length === 1) return workspaces[0].id;
169
83
  if (!canPrompt(context)) throw usageError("Interactive workspace selection unavailable", "auth workspace use needs an interactive terminal when no workspace is provided and more than one workspace is available.", "Run prisma-cli auth workspace use <id-or-name> with a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
@@ -175,135 +89,6 @@ async function selectWorkspaceSession(context) {
175
89
  }))
176
90
  })).id;
177
91
  }
178
- async function hydrateLocalAuthWorkspaces(context, storage, workspaces) {
179
- const candidates = workspaces.filter(needsWorkspaceMetadataHydration);
180
- if (candidates.length === 0) return workspaces;
181
- const tokensByCredentialWorkspaceId = new Map((await storage.listWorkspaceTokens()).map((tokens) => [tokens.workspaceId, tokens]));
182
- let nextWorkspaces = workspaces;
183
- for (const workspace of candidates) {
184
- const tokens = tokensByCredentialWorkspaceId.get(workspace.credentialWorkspaceId);
185
- if (!tokens) continue;
186
- const resolved = await resolveOAuthWorkspaceMetadata(context, tokens);
187
- if (!resolved) continue;
188
- await rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved);
189
- nextWorkspaces = nextWorkspaces.map((candidate) => candidate.credentialWorkspaceId === workspace.credentialWorkspaceId ? {
190
- ...candidate,
191
- id: resolved.id,
192
- name: resolved.name,
193
- lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
194
- } : candidate);
195
- }
196
- return nextWorkspaces;
197
- }
198
- async function rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved) {
199
- try {
200
- await storage.rememberWorkspace(tokens.workspaceId, resolved);
201
- } catch {
202
- context.runtime.signal?.throwIfAborted();
203
- }
204
- }
205
- function needsWorkspaceMetadataHydration(workspace) {
206
- return workspace.id === workspace.credentialWorkspaceId || workspace.name === "Unknown workspace" || workspace.name === workspace.credentialWorkspaceId;
207
- }
208
- async function resolveOAuthWorkspaceMetadata(context, tokens) {
209
- const sdk = createManagementApiSdk({
210
- clientId: CLIENT_ID,
211
- redirectUri: "http://localhost:0/auth/callback",
212
- tokenStorage: createSingleWorkspaceTokenStorage(new FileTokenStorage(context.runtime.env, context.runtime.signal, { activateOnSetTokens: false }), tokens),
213
- apiBaseUrl: getApiBaseUrl(context.runtime.env)
214
- });
215
- try {
216
- const { data } = await sdk.client.GET("/v1/workspaces/{id}", {
217
- params: { path: { id: tokens.workspaceId } },
218
- signal: context.runtime.signal
219
- });
220
- const id = stringOrNull(data?.data?.id) ?? tokens.workspaceId;
221
- const name = stringOrNull(data?.data?.name) ?? id;
222
- if (id === tokens.workspaceId && name === tokens.workspaceId) return null;
223
- return {
224
- id,
225
- name
226
- };
227
- } catch {
228
- context.runtime.signal?.throwIfAborted();
229
- return null;
230
- }
231
- }
232
- function createSingleWorkspaceTokenStorage(storage, initialTokens) {
233
- let currentTokens = initialTokens;
234
- return {
235
- getTokens: async () => currentTokens,
236
- setTokens: async (tokens) => {
237
- currentTokens = tokens;
238
- await storage.setTokens(tokens);
239
- },
240
- clearTokens: async () => {
241
- const tokens = currentTokens;
242
- currentTokens = null;
243
- if (tokens) await storage.clearTokensIfCurrent(tokens);
244
- }
245
- };
246
- }
247
- function stringOrNull(value) {
248
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
249
- }
250
- function toAuthWorkspace(workspace) {
251
- return {
252
- id: workspace.id,
253
- name: workspace.name
254
- };
255
- }
256
- async function loginWithSelectionFlow(context, useCases, options) {
257
- const selection = await resolveLoginSelection(useCases, canPrompt(context) ? createSelectPromptPort(context) : null, options);
258
- return useCases.login(selection);
259
- }
260
- async function resolveLoginSelection(useCases, prompt, options) {
261
- const provider = options.provider ? (await useCases.resolveProvider(options.provider)).id : (await selectProvider(useCases, prompt)).id;
262
- const user = options.user ? await useCases.resolveUserForProvider(provider, options.user) : await selectUser(useCases, prompt, provider);
263
- const workspace = options.workspace ? await useCases.resolveWorkspaceForUser(user.id, options.workspace) : await selectWorkspace(useCases, prompt, user.id);
264
- return {
265
- provider,
266
- userId: user.id,
267
- workspaceId: workspace.id
268
- };
269
- }
270
- async function selectProvider(useCases, prompt) {
271
- if (!prompt) throw nonInteractiveLoginError("Re-run prisma-cli auth login in a TTY, or provide --provider and --user, and --workspace when required.");
272
- const providers = await useCases.listProviders();
273
- return prompt.select({
274
- message: "Select a provider",
275
- choices: providers.map((provider) => ({
276
- label: provider.name,
277
- value: provider
278
- }))
279
- });
280
- }
281
- async function selectUser(useCases, prompt, provider) {
282
- const users = await useCases.listUsersForProvider(provider);
283
- if (!prompt) throw nonInteractiveLoginError("Re-run prisma-cli auth login in a TTY, or provide --provider and --user, and --workspace when required.");
284
- return prompt.select({
285
- message: "Select a user",
286
- choices: users.map((user) => ({
287
- label: `${user.name} <${user.email}>`,
288
- value: user
289
- }))
290
- });
291
- }
292
- async function selectWorkspace(useCases, prompt, userId) {
293
- const workspaces = await useCases.listWorkspacesForUser(userId);
294
- if (workspaces.length === 1) return workspaces[0];
295
- if (!prompt) throw usageError("Login requires explicit selectors in non-interactive mode", "The selected mock user belongs to more than one workspace and the shell cannot prompt in the current mode.", "Re-run prisma-cli auth login in a TTY, or provide --workspace.", ["prisma-cli auth login"], "auth");
296
- return prompt.select({
297
- message: "Select a workspace",
298
- choices: workspaces.map((workspace) => ({
299
- label: `${workspace.name} (${workspace.id})`,
300
- value: workspace
301
- }))
302
- });
303
- }
304
- function nonInteractiveLoginError(fix) {
305
- return usageError("Login requires explicit selectors in non-interactive mode", "The fixture mode cannot prompt in the current mode.", fix, ["prisma-cli auth login"], "auth");
306
- }
307
92
  function createAuthSuccess(command, result, nextSteps) {
308
93
  return {
309
94
  command,
@@ -312,5 +97,28 @@ function createAuthSuccess(command, result, nextSteps) {
312
97
  nextSteps
313
98
  };
314
99
  }
100
+ async function resolveAgentSetupTipCommand(context) {
101
+ if (context.flags.json || context.flags.quiet) return null;
102
+ if (context.runtime.env.CI && context.flags.interactive !== true) return null;
103
+ if (!context.runtime.stderr.isTTY && context.flags.interactive !== true) return null;
104
+ const setupCwd = await resolvePrismaAgentSetupCwd({
105
+ cwd: context.runtime.cwd,
106
+ signal: context.runtime.signal
107
+ });
108
+ if (!await isLikelyProjectDirectory({
109
+ cwd: setupCwd,
110
+ signal: context.runtime.signal
111
+ })) return null;
112
+ if (!shouldOfferPrismaAgentSetup(await readPrismaAgentSetupStatus({
113
+ cwd: setupCwd,
114
+ stateStore: context.stateStore,
115
+ signal: context.runtime.signal
116
+ }))) return null;
117
+ return await resolvePrismaCliPackageCommand({
118
+ cwd: setupCwd,
119
+ signal: context.runtime.signal,
120
+ args: PRISMA_AGENT_INSTALL_ARGS
121
+ });
122
+ }
315
123
  //#endregion
316
124
  export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceUse };
@@ -1,38 +1,27 @@
1
- import { CliError, authRequiredError, workspaceRequiredError } from "../shell/errors.js";
1
+ import { CliError, authRequiredError, workspaceRequiredError } from "../errors.js";
2
+ import { authenticatedManagementApiClient } from "../auth/guard.js";
2
3
  import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
3
- import { requireComputeAuth } from "../lib/auth/guard.js";
4
- import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
5
4
  import { requireAuthenticatedAuthState } from "./auth.js";
6
5
  import { listRealWorkspaceProjects } from "./project.js";
7
- import { createBranchUseCases } from "../use-cases/branch.js";
8
6
  //#region src/controllers/branch.ts
9
- function isRealMode(context) {
10
- return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
11
- }
12
7
  async function runBranchList(context) {
13
- if (isRealMode(context)) return {
14
- command: "branch.list",
15
- result: await listRealBranches(context),
16
- warnings: [],
17
- nextSteps: []
18
- };
19
8
  return {
20
9
  command: "branch.list",
21
- result: await createBranchUseCases(createCliUseCaseGateways(context)).list(),
10
+ result: await listRealBranches(context),
22
11
  warnings: [],
23
12
  nextSteps: []
24
13
  };
25
14
  }
26
15
  async function listRealBranches(context) {
27
16
  const authState = await requireAuthenticatedAuthState(context);
28
- const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
17
+ const client = await authenticatedManagementApiClient(context.runtime.env, context.runtime.signal);
29
18
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
30
19
  const workspace = authState.workspace;
31
20
  if (!workspace) throw workspaceRequiredError();
32
21
  const targetResult = await resolveProjectTarget({
33
22
  context,
34
23
  workspace,
35
- listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal)
24
+ listProjects: () => listRealWorkspaceProjects(client, context.runtime.signal)
36
25
  });
37
26
  if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
38
27
  const target = targetResult.value;
@@ -0,0 +1,184 @@
1
+ import { CliError, authRequiredError, workspaceRequiredError } from "../errors.js";
2
+ import { authenticatedManagementApiClient } from "../auth/guard.js";
3
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
4
+ import { requireAuthenticatedAuthState } from "./auth.js";
5
+ import { listRealWorkspaceProjects } from "./project.js";
6
+ import { createManagementBucketProvider } from "../lib/bucket/provider.js";
7
+ //#region src/controllers/bucket.ts
8
+ async function runBucketList(context, flags) {
9
+ const { provider, target } = await requireBucketContext(context, flags, "bucket list");
10
+ const buckets = await provider.listBuckets({
11
+ projectId: target.project.id,
12
+ branchName: flags.branchName,
13
+ signal: context.runtime.signal
14
+ });
15
+ return {
16
+ command: "bucket.list",
17
+ result: {
18
+ projectId: target.project.id,
19
+ projectName: target.project.name,
20
+ branchName: flags.branchName ?? null,
21
+ verboseContext: target,
22
+ buckets
23
+ },
24
+ warnings: [],
25
+ nextSteps: []
26
+ };
27
+ }
28
+ async function runBucketCreate(context, flags) {
29
+ const { provider, target } = await requireBucketContext(context, flags, "bucket create");
30
+ const bucket = await provider.createBucket({
31
+ projectId: target.project.id,
32
+ name: flags.name?.trim() || void 0,
33
+ branchGitName: flags.branchName,
34
+ signal: context.runtime.signal
35
+ });
36
+ return {
37
+ command: "bucket.create",
38
+ result: {
39
+ projectId: target.project.id,
40
+ projectName: target.project.name,
41
+ verboseContext: target,
42
+ bucket
43
+ },
44
+ warnings: [],
45
+ nextSteps: []
46
+ };
47
+ }
48
+ async function runBucketDelete(context, bucketId, flags) {
49
+ const id = bucketId.trim();
50
+ if (!id) throw new CliError({
51
+ code: "USAGE_ERROR",
52
+ domain: "bucket",
53
+ summary: "Bucket id required",
54
+ why: "Bucket deletion needs a bucket id.",
55
+ fix: "Pass the bucket id to delete.",
56
+ exitCode: 2,
57
+ nextSteps: ["prisma-cli bucket list"]
58
+ });
59
+ if (flags.confirm !== id) throw new CliError({
60
+ code: "CONFIRMATION_REQUIRED",
61
+ domain: "bucket",
62
+ summary: "Confirm bucket deletion",
63
+ why: "Deleting this bucket permanently removes all objects and access keys.",
64
+ fix: `Rerun with --confirm ${id}.`,
65
+ exitCode: 2,
66
+ nextSteps: [`prisma-cli bucket delete ${id} --confirm ${id}`],
67
+ meta: {
68
+ expectedConfirm: id,
69
+ receivedConfirm: flags.confirm ?? null
70
+ }
71
+ });
72
+ await (await requireBucketProviderOnly(context)).deleteBucket(id, { signal: context.runtime.signal });
73
+ return {
74
+ command: "bucket.delete",
75
+ result: { bucket: { id } },
76
+ warnings: [],
77
+ nextSteps: []
78
+ };
79
+ }
80
+ async function runBucketKeyList(context, bucketId) {
81
+ const id = bucketId.trim();
82
+ if (!id) throw new CliError({
83
+ code: "USAGE_ERROR",
84
+ domain: "bucket",
85
+ summary: "Bucket id required",
86
+ why: "Bucket key listing needs a bucket id.",
87
+ fix: "Pass the bucket id.",
88
+ exitCode: 2,
89
+ nextSteps: ["prisma-cli bucket list"]
90
+ });
91
+ return {
92
+ command: "bucket.key.list",
93
+ result: {
94
+ bucketId: id,
95
+ keys: await (await requireBucketProviderOnly(context)).listKeys(id, { signal: context.runtime.signal })
96
+ },
97
+ warnings: [],
98
+ nextSteps: []
99
+ };
100
+ }
101
+ async function runBucketKeyCreate(context, bucketId, flags) {
102
+ const id = bucketId.trim();
103
+ if (!id) throw new CliError({
104
+ code: "USAGE_ERROR",
105
+ domain: "bucket",
106
+ summary: "Bucket id required",
107
+ why: "Bucket key creation needs a bucket id.",
108
+ fix: "Pass the bucket id.",
109
+ exitCode: 2,
110
+ nextSteps: ["prisma-cli bucket list"]
111
+ });
112
+ const role = resolveKeyRole(flags.role);
113
+ const created = await (await requireBucketProviderOnly(context)).createKey({
114
+ bucketId: id,
115
+ name: flags.name?.trim() || void 0,
116
+ role,
117
+ signal: context.runtime.signal
118
+ });
119
+ return {
120
+ command: "bucket.key.create",
121
+ result: {
122
+ bucketId: id,
123
+ key: created.key,
124
+ secretAccessKey: created.secretAccessKey,
125
+ accessKeyId: created.accessKeyId,
126
+ endpoint: created.endpoint,
127
+ bucketName: created.bucketName
128
+ },
129
+ warnings: [],
130
+ nextSteps: []
131
+ };
132
+ }
133
+ async function runBucketKeyDelete(context, bucketId, keyId) {
134
+ const bktId = bucketId.trim();
135
+ const kId = keyId.trim();
136
+ if (!bktId || !kId) throw new CliError({
137
+ code: "USAGE_ERROR",
138
+ domain: "bucket",
139
+ summary: "Bucket id and key id required",
140
+ why: "Bucket key deletion needs both a bucket id and a key id.",
141
+ fix: "Pass the bucket id and key id.",
142
+ exitCode: 2,
143
+ nextSteps: ["prisma-cli bucket key list <bucketId>"]
144
+ });
145
+ await (await requireBucketProviderOnly(context)).deleteKey(bktId, kId, { signal: context.runtime.signal });
146
+ return {
147
+ command: "bucket.key.delete",
148
+ result: { key: { id: kId } },
149
+ warnings: [],
150
+ nextSteps: []
151
+ };
152
+ }
153
+ async function resolveBucketProvider(context) {
154
+ const client = await authenticatedManagementApiClient(context.runtime.env, context.runtime.signal);
155
+ if (!client) throw authRequiredError();
156
+ return createManagementBucketProvider(client);
157
+ }
158
+ async function requireBucketContext(context, flags, commandName) {
159
+ const workspace = (await requireAuthenticatedAuthState(context)).workspace;
160
+ if (!workspace) throw workspaceRequiredError();
161
+ const client = await authenticatedManagementApiClient(context.runtime.env, context.runtime.signal);
162
+ if (!client) throw authRequiredError();
163
+ const targetResult = await resolveProjectTarget({
164
+ context,
165
+ workspace,
166
+ explicitProject: flags.projectRef,
167
+ listProjects: () => listRealWorkspaceProjects(client, context.runtime.signal),
168
+ commandName
169
+ });
170
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
171
+ return {
172
+ provider: createManagementBucketProvider(client),
173
+ target: targetResult.value
174
+ };
175
+ }
176
+ async function requireBucketProviderOnly(context) {
177
+ await requireAuthenticatedAuthState(context);
178
+ return resolveBucketProvider(context);
179
+ }
180
+ function resolveKeyRole(role) {
181
+ return role === "read" ? "read" : "read_write";
182
+ }
183
+ //#endregion
184
+ export { runBucketCreate, runBucketDelete, runBucketKeyCreate, runBucketKeyDelete, runBucketKeyList, runBucketList };
@@ -0,0 +1,88 @@
1
+ import { CliError, authRequiredError } from "../errors.js";
2
+ import { authenticatedManagementApiClient } from "../auth/guard.js";
3
+ import { writeJsonEvent } from "../shell/output.js";
4
+ //#region src/controllers/build.ts
5
+ async function runBuildLogs(context, buildId, options = {}) {
6
+ const client = await authenticatedManagementApiClient(context.runtime.env, context.runtime.signal);
7
+ if (!client) throw authRequiredError(["prisma-cli auth login"]);
8
+ if (!context.flags.json && !context.flags.quiet) context.output.stderr.write(`build logs → Streaming logs for build ${buildId}\n\n`);
9
+ const { data, response } = await client.GET("/v1/builds/{buildId}/logs", {
10
+ params: {
11
+ path: { buildId },
12
+ query: {
13
+ ...options.follow ? { follow: "true" } : {},
14
+ ...options.cursor ? { cursor: options.cursor } : {}
15
+ }
16
+ },
17
+ parseAs: "stream",
18
+ signal: context.runtime.signal
19
+ });
20
+ const body = data;
21
+ if (!response.ok || !body) throw buildLogsRequestError(buildId, response.status);
22
+ let sawError = false;
23
+ await forEachNdjsonRecord(body, (record) => {
24
+ if (record.type === "terminal" && record.kind === "error") sawError = true;
25
+ writeBuildLogRecord(context, record);
26
+ });
27
+ if (sawError) process.exitCode = 1;
28
+ }
29
+ function writeBuildLogRecord(context, record) {
30
+ if (context.flags.json) {
31
+ writeJsonEvent(context.output, {
32
+ type: record.type,
33
+ command: "build.logs",
34
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35
+ data: record
36
+ });
37
+ return;
38
+ }
39
+ if (record.type === "log") {
40
+ const stream = record.source === "stderr" || record.level === "error" ? context.output.stderr : context.output.stdout;
41
+ stream.write(record.text);
42
+ if (!record.text.endsWith("\n")) stream.write("\n");
43
+ return;
44
+ }
45
+ if (record.code !== "end") context.output.stderr.write(`${record.message}\n`);
46
+ }
47
+ /** Reads a newline-delimited JSON body line by line, parsing each into a record. */
48
+ async function forEachNdjsonRecord(body, onRecord) {
49
+ const reader = body.getReader();
50
+ const decoder = new TextDecoder();
51
+ let buffer = "";
52
+ for (;;) {
53
+ const { done, value } = await reader.read();
54
+ if (value) buffer += decoder.decode(value, { stream: true });
55
+ let newlineIndex = buffer.indexOf("\n");
56
+ while (newlineIndex !== -1) {
57
+ const line = buffer.slice(0, newlineIndex).trim();
58
+ buffer = buffer.slice(newlineIndex + 1);
59
+ if (line) onRecord(JSON.parse(line));
60
+ newlineIndex = buffer.indexOf("\n");
61
+ }
62
+ if (done) {
63
+ const tail = buffer.trim();
64
+ if (tail) onRecord(JSON.parse(tail));
65
+ return;
66
+ }
67
+ }
68
+ }
69
+ function buildLogsRequestError(buildId, status) {
70
+ if (status === 404) return new CliError({
71
+ code: "BUILD_NOT_FOUND",
72
+ domain: "app",
73
+ summary: `Build ${buildId} was not found`,
74
+ why: "The build does not exist, or your workspace does not have access to it.",
75
+ fix: "Check the build id, or run prisma-cli auth login to switch to the workspace that owns it.",
76
+ exitCode: 1
77
+ });
78
+ return new CliError({
79
+ code: "BUILD_LOGS_FAILED",
80
+ domain: "app",
81
+ summary: `Failed to read logs for build ${buildId}`,
82
+ why: `The Management API returned HTTP ${status}.`,
83
+ fix: "Retry the command, or rerun with --trace for more detail.",
84
+ exitCode: 1
85
+ });
86
+ }
87
+ //#endregion
88
+ export { runBuildLogs };