@prisma/cli 3.0.0-beta.3 → 3.0.0-beta.30

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 +6 -15
  2. package/dist/adapters/local-state.js +15 -4
  3. package/dist/adapters/mock-api.js +244 -0
  4. package/dist/adapters/token-storage.js +335 -34
  5. package/dist/cli.js +7 -7
  6. package/dist/cli2.js +24 -5
  7. package/dist/commands/agent/index.js +60 -0
  8. package/dist/commands/app/index.js +91 -61
  9. package/dist/commands/auth/index.js +55 -2
  10. package/dist/commands/branch/index.js +2 -27
  11. package/dist/commands/bucket/index.js +123 -0
  12. package/dist/commands/build/index.js +29 -0
  13. package/dist/commands/database/index.js +249 -0
  14. package/dist/commands/env.js +8 -4
  15. package/dist/commands/feedback/index.js +20 -0
  16. package/dist/commands/git/index.js +1 -1
  17. package/dist/commands/init/index.js +33 -0
  18. package/dist/commands/project/index.js +54 -5
  19. package/dist/controllers/agent-setup.js +52 -0
  20. package/dist/controllers/agent.js +228 -0
  21. package/dist/controllers/app-env-api.js +55 -0
  22. package/dist/controllers/app-env-file.js +181 -0
  23. package/dist/controllers/app-env.js +227 -104
  24. package/dist/controllers/app.js +746 -306
  25. package/dist/controllers/auth.js +247 -3
  26. package/dist/controllers/branch.js +78 -48
  27. package/dist/controllers/bucket.js +278 -0
  28. package/dist/controllers/build.js +88 -0
  29. package/dist/controllers/database.js +567 -0
  30. package/dist/controllers/feedback.js +86 -0
  31. package/dist/controllers/init.js +753 -0
  32. package/dist/controllers/project.js +377 -22
  33. package/dist/controllers/select-prompt-port.js +1 -0
  34. package/dist/lib/agent/cli-command.js +20 -0
  35. package/dist/lib/agent/constants.js +12 -0
  36. package/dist/lib/agent/package-manager.js +99 -0
  37. package/dist/lib/agent/setup-status.js +83 -0
  38. package/dist/lib/app/{preview-provider.js → app-provider.js} +137 -88
  39. package/dist/lib/app/branch-database-api.js +102 -0
  40. package/dist/lib/app/branch-database-deploy.js +326 -0
  41. package/dist/lib/app/branch-database.js +216 -0
  42. package/dist/lib/app/build-settings.js +93 -0
  43. package/dist/lib/app/build.js +83 -0
  44. package/dist/lib/app/bun-project.js +3 -4
  45. package/dist/lib/app/compute-config.js +145 -0
  46. package/dist/lib/app/deploy-plan.js +59 -0
  47. package/dist/lib/app/{preview-progress.js → deploy-progress.js} +12 -12
  48. package/dist/lib/app/env-config.js +1 -1
  49. package/dist/lib/app/env-file.js +82 -0
  50. package/dist/lib/app/env-vars.js +28 -2
  51. package/dist/lib/app/local-dev.js +3 -60
  52. package/dist/lib/app/production-deploy-gate.js +162 -0
  53. package/dist/lib/app/read-branch.js +30 -0
  54. package/dist/lib/auth/auth-ops.js +10 -4
  55. package/dist/lib/auth/guard.js +4 -1
  56. package/dist/lib/auth/login.js +33 -26
  57. package/dist/lib/auth/recipient.js +42 -0
  58. package/dist/lib/bucket/provider.js +139 -0
  59. package/dist/lib/database/provider.js +378 -0
  60. package/dist/lib/diagnostics.js +15 -0
  61. package/dist/lib/fs/home-path.js +24 -0
  62. package/dist/lib/git/local-branch.js +53 -0
  63. package/dist/lib/git/local-status.js +57 -0
  64. package/dist/lib/project/interactive-setup.js +5 -4
  65. package/dist/lib/project/local-pin.js +171 -41
  66. package/dist/lib/project/provider.js +92 -0
  67. package/dist/lib/project/resolution.js +199 -48
  68. package/dist/lib/project/setup.js +67 -20
  69. package/dist/output/patterns.js +1 -1
  70. package/dist/presenters/agent.js +74 -0
  71. package/dist/presenters/app-env.js +149 -14
  72. package/dist/presenters/app.js +208 -27
  73. package/dist/presenters/auth.js +99 -2
  74. package/dist/presenters/branch.js +37 -102
  75. package/dist/presenters/bucket.js +174 -0
  76. package/dist/presenters/database.js +448 -0
  77. package/dist/presenters/feedback.js +26 -0
  78. package/dist/presenters/init.js +30 -0
  79. package/dist/presenters/project.js +139 -27
  80. package/dist/presenters/verbose-context.js +64 -0
  81. package/dist/shell/cli-command.js +12 -0
  82. package/dist/shell/command-arguments.js +7 -1
  83. package/dist/shell/command-meta.js +458 -17
  84. package/dist/shell/command-runner.js +58 -18
  85. package/dist/shell/diagnostics-output.js +57 -0
  86. package/dist/shell/errors.js +56 -1
  87. package/dist/shell/help.js +31 -20
  88. package/dist/shell/output.js +72 -1
  89. package/dist/shell/prompt.js +12 -5
  90. package/dist/shell/runtime.js +8 -4
  91. package/dist/shell/ui.js +42 -3
  92. package/dist/shell/update-check.js +2 -2
  93. package/dist/use-cases/auth.js +68 -1
  94. package/dist/use-cases/branch.js +20 -68
  95. package/dist/use-cases/create-cli-gateways.js +2 -17
  96. package/dist/use-cases/project.js +2 -1
  97. package/package.json +21 -4
  98. package/dist/lib/app/preview-build.js +0 -312
  99. package/dist/lib/app/preview-interaction.js +0 -5
@@ -1,17 +1,25 @@
1
- import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
1
+ import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
2
+ import { formatCommandArgument } from "../shell/command-arguments.js";
3
+ import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceRequiredError } from "../shell/errors.js";
2
4
  import { renderSummaryLine } from "../shell/ui.js";
3
5
  import { canPrompt } from "../shell/runtime.js";
6
+ import { SERVICE_TOKEN_ENV_VAR } from "../lib/auth/client.js";
7
+ import { WorkspaceSelectionError } from "../adapters/token-storage.js";
8
+ import { createAppProvider } from "../lib/app/app-provider.js";
9
+ import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin, writeLocalResolutionPin } from "../lib/project/local-pin.js";
10
+ import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectResolutionErrorToCliError, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
11
+ import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
4
12
  import { requireComputeAuth } from "../lib/auth/guard.js";
5
- import { formatCommandArgument } from "../shell/command-arguments.js";
6
- import { readLocalResolutionPin } from "../lib/project/local-pin.js";
7
- import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
8
- import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
9
13
  import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
10
- import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
11
14
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
12
15
  import { requireAuthenticatedAuthState } from "./auth.js";
13
16
  import { parseGitHubRepositoryUrl, readGitOriginRemote } from "../adapters/git.js";
17
+ import { RecipientSessionInvalidError, resolveRecipientWorkspaceSession } from "../lib/auth/recipient.js";
18
+ import { createManagementProjectProvider, projectRemoveBlockedError, projectRenameFailedError, projectTransferRejectedError } from "../lib/project/provider.js";
14
19
  import { createProjectUseCases } from "../use-cases/project.js";
20
+ import path from "node:path";
21
+ import { unlink } from "node:fs/promises";
22
+ import { matchError } from "better-result";
15
23
  import open from "open";
16
24
  //#region src/controllers/project.ts
17
25
  const GITHUB_INSTALL_POLL_INTERVAL_MS = 2e3;
@@ -20,11 +28,24 @@ function isRealMode(context) {
20
28
  return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
21
29
  }
22
30
  async function readProjectListLocalBinding(cwd, workspace, projects, signal) {
23
- const pin = await readLocalResolutionPin(cwd, signal);
31
+ const pinResult = await readLocalResolutionPin(cwd, signal);
32
+ if (pinResult.isErr()) return localPinReadErrorToInvalidLocalBinding(pinResult.error);
33
+ const pin = pinResult.value;
24
34
  if (pin.kind === "present") return pin.pin.workspaceId === workspace.id && projects.some((project) => project.id === pin.pin.projectId) ? { status: "linked" } : { status: "invalid" };
25
- if (pin.kind === "invalid") return { status: "invalid" };
26
35
  return { status: "not-linked" };
27
36
  }
37
+ function localPinReadErrorToInvalidLocalBinding(error) {
38
+ return matchError(error, {
39
+ LocalResolutionPinInvalidJsonError: () => ({ status: "invalid" }),
40
+ LocalResolutionPinInvalidShapeError: () => ({ status: "invalid" }),
41
+ LocalResolutionPinReadAbortedError: (error) => {
42
+ throw error;
43
+ },
44
+ UnhandledException: (error) => {
45
+ throw error;
46
+ }
47
+ });
48
+ }
28
49
  async function runProjectList(context) {
29
50
  const authState = await requireAuthenticatedAuthState(context);
30
51
  const workspace = authState.workspace;
@@ -83,17 +104,18 @@ async function runProjectShow(context, explicitProject) {
83
104
  }) : []
84
105
  };
85
106
  }
86
- async function runProjectCreate(context, projectName) {
107
+ async function runProjectCreate(context, projectName, options) {
87
108
  const workspace = (await requireAuthenticatedAuthState(context)).workspace;
88
109
  if (!workspace) throw workspaceRequiredError();
89
110
  if (!isValidProjectSetupName(projectName)) throw projectSetupNameRequiredError("project create");
90
111
  if (!isRealMode(context)) throw featureUnavailableError("Project create is not available in fixture mode", "Creating Projects requires live platform integration.", "Rerun without fixture mode enabled to create a Project.", ["prisma-cli auth login"], "project");
91
112
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
92
113
  if (!client) throw authRequiredError();
93
- const provider = createPreviewAppProvider(client);
114
+ const provider = createAppProvider(client);
94
115
  const name = projectName.trim();
95
116
  const created = await provider.createProject({
96
117
  name,
118
+ region: options?.region,
97
119
  signal: context.runtime.signal
98
120
  }).catch((error) => {
99
121
  throw projectCreateFailedError(error, name, workspace, {
@@ -102,12 +124,15 @@ async function runProjectCreate(context, projectName) {
102
124
  fallbackFix: "Retry the command, or choose an existing Project with prisma-cli project link <id-or-name>."
103
125
  });
104
126
  });
127
+ const bindResult = await bindProjectToDirectory(context, workspace, {
128
+ id: created.id,
129
+ name: created.name,
130
+ ...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {}
131
+ }, "created");
132
+ if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
105
133
  return {
106
134
  command: "project.create",
107
- result: await bindProjectToDirectory(context, workspace, {
108
- id: created.id,
109
- name: created.name
110
- }, "created"),
135
+ result: bindResult.value,
111
136
  warnings: [],
112
137
  nextSteps: ["prisma-cli app deploy"]
113
138
  };
@@ -120,11 +145,11 @@ async function runProjectLink(context, projectRef) {
120
145
  if (isRealMode(context)) {
121
146
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
122
147
  if (!client) throw authRequiredError();
123
- provider = createPreviewAppProvider(client);
148
+ provider = createAppProvider(client);
124
149
  projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
125
150
  } else projects = listFixtureWorkspaceProjects(context, workspace);
126
151
  let result;
127
- if (projectRef?.trim()) result = await bindProjectToDirectory(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked");
152
+ if (projectRef?.trim()) result = await requireProjectDirectoryBinding(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked");
128
153
  else if (canPrompt(context) && !context.flags.yes) result = await resolveInteractiveProjectLinkSetup(context, workspace, projects, provider);
129
154
  else throw await projectLinkTargetRequiredError(context, projects);
130
155
  return {
@@ -148,7 +173,12 @@ async function resolveInteractiveProjectLinkSetup(context, workspace, projects,
148
173
  nextSteps: ["prisma-cli project link <id-or-name>", "prisma-cli project create <name>"]
149
174
  }
150
175
  });
151
- return bindProjectToDirectory(context, workspace, setup.project, setup.action);
176
+ return requireProjectDirectoryBinding(context, workspace, setup.project, setup.action);
177
+ }
178
+ async function requireProjectDirectoryBinding(context, workspace, project, action) {
179
+ const bindResult = await bindProjectToDirectory(context, workspace, project, action);
180
+ if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
181
+ return bindResult.value;
152
182
  }
153
183
  async function createProjectForLinkSetup(provider, projectName, workspace, signal) {
154
184
  const created = await provider.createProject({
@@ -196,6 +226,322 @@ async function projectLinkTargetRequiredError(context, projects) {
196
226
  })
197
227
  });
198
228
  }
229
+ async function runProjectRename(context, newName, options) {
230
+ const workspace = (await requireAuthenticatedAuthState(context)).workspace;
231
+ if (!workspace) throw workspaceRequiredError();
232
+ const name = newName.trim();
233
+ if (!isValidProjectSetupName(name)) throw projectSetupNameRequiredError("project rename");
234
+ const { provider, target } = await requireProjectCommandContext(context, workspace, options.project, "project rename");
235
+ const previousName = target.project.name;
236
+ return {
237
+ command: "project.rename",
238
+ result: {
239
+ workspace,
240
+ project: await provider.renameProject({
241
+ projectId: target.project.id,
242
+ name,
243
+ signal: context.runtime.signal
244
+ }),
245
+ previousName
246
+ },
247
+ warnings: [],
248
+ nextSteps: []
249
+ };
250
+ }
251
+ async function runProjectRemove(context, projectRef, options) {
252
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
253
+ const workspace = (await requireAuthenticatedAuthState(context)).workspace;
254
+ if (!workspace) throw workspaceRequiredError();
255
+ const { provider, projects } = await requireProjectMutationContext(context, workspace);
256
+ const project = toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace));
257
+ requireProjectExactConfirmation({
258
+ id: project.id,
259
+ confirm: options.confirm,
260
+ summary: "Confirm project removal",
261
+ why: "Removing a project is permanent, deletes its databases, and stops its apps, so it requires the exact project id.",
262
+ nextStep: formatCommand([
263
+ "project",
264
+ "remove",
265
+ project.id,
266
+ "--confirm",
267
+ project.id
268
+ ])
269
+ });
270
+ await provider.removeProject({
271
+ projectId: project.id,
272
+ signal: context.runtime.signal
273
+ });
274
+ const warnings = [];
275
+ return {
276
+ command: "project.remove",
277
+ result: {
278
+ workspace,
279
+ project,
280
+ localPin: { cleared: await cleanupLocalPinForProject(context, project.id, { onError: (message) => warnings.push(message) }) }
281
+ },
282
+ warnings,
283
+ nextSteps: []
284
+ };
285
+ }
286
+ async function runProjectTransfer(context, projectRef, options) {
287
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
288
+ const workspace = (await requireAuthenticatedAuthState(context)).workspace;
289
+ if (!workspace) throw workspaceRequiredError();
290
+ if (options.toWorkspace && options.recipientToken) throw usageError("Choose one transfer recipient source", "--to-workspace and --recipient-token are mutually exclusive.", "Pass either --to-workspace <id-or-name> or --recipient-token <token>.", [formatCommand([
291
+ "project",
292
+ "transfer",
293
+ "<project>",
294
+ "--to-workspace",
295
+ "<id-or-name>",
296
+ "--confirm",
297
+ "<project-id>"
298
+ ])], "project");
299
+ if (!options.toWorkspace?.trim() && !options.recipientToken?.trim()) throw transferRecipientRequiredError(formatCommand);
300
+ const { provider, projects } = await requireProjectMutationContext(context, workspace);
301
+ const project = toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace));
302
+ requireProjectExactConfirmation({
303
+ id: project.id,
304
+ confirm: options.confirm,
305
+ summary: "Confirm project transfer",
306
+ why: "Transferring moves the project to another workspace and this workspace loses access, so it requires the exact project id.",
307
+ nextStep: `${formatCommand([
308
+ "project",
309
+ "transfer",
310
+ project.id
311
+ ])} ${options.toWorkspace ? `--to-workspace ${formatCommandArgument(options.toWorkspace)}` : "--recipient-token <token>"} --confirm ${project.id}`
312
+ });
313
+ const recipient = await resolveTransferRecipient(context, options);
314
+ await provider.transferProject({
315
+ projectId: project.id,
316
+ recipientAccessToken: recipient.accessToken,
317
+ signal: context.runtime.signal
318
+ });
319
+ const warnings = [];
320
+ const pinAction = await rewriteOrClearLocalPinForProject(context, project.id, recipient.workspaceId, { onError: (message) => warnings.push(message) });
321
+ return {
322
+ command: "project.transfer",
323
+ result: {
324
+ workspace,
325
+ project,
326
+ recipient: {
327
+ workspaceId: recipient.workspaceId,
328
+ workspaceName: recipient.workspaceName,
329
+ source: recipient.source
330
+ },
331
+ localPin: { action: pinAction }
332
+ },
333
+ warnings,
334
+ nextSteps: options.toWorkspace ? [`${formatCommand([
335
+ "auth",
336
+ "workspace",
337
+ "use"
338
+ ])} ${formatCommandArgument(options.toWorkspace)}`] : []
339
+ };
340
+ }
341
+ async function resolveTransferRecipient(context, options) {
342
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
343
+ const recipientToken = options.recipientToken?.trim();
344
+ if (recipientToken) return {
345
+ accessToken: recipientToken,
346
+ workspaceId: isRealMode(context) ? null : recipientToken,
347
+ workspaceName: null,
348
+ source: "recipient-token"
349
+ };
350
+ const workspaceRef = options.toWorkspace?.trim();
351
+ if (!workspaceRef) throw transferRecipientRequiredError(formatCommand);
352
+ if (!isRealMode(context)) {
353
+ const matches = context.api.listWorkspaces().filter((candidate) => candidate.id === workspaceRef || candidate.name.toLowerCase() === workspaceRef.toLowerCase());
354
+ if (matches.length === 0) throw workspaceNotAuthenticatedError(workspaceRef);
355
+ if (matches.length > 1) throw workspaceAmbiguousError(workspaceRef, matches.map((match) => ({
356
+ id: match.id,
357
+ name: match.name,
358
+ credentialWorkspaceId: match.id
359
+ })));
360
+ return {
361
+ accessToken: matches[0].id,
362
+ workspaceId: matches[0].id,
363
+ workspaceName: matches[0].name,
364
+ source: "workspace-session"
365
+ };
366
+ }
367
+ if (context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw transferRecipientUnavailableError(formatCommand);
368
+ try {
369
+ const session = await resolveRecipientWorkspaceSession(workspaceRef, context.runtime.env, context.runtime.signal);
370
+ return {
371
+ accessToken: session.accessToken,
372
+ workspaceId: session.workspace.id,
373
+ workspaceName: session.workspace.name,
374
+ source: "workspace-session"
375
+ };
376
+ } catch (error) {
377
+ if (error instanceof WorkspaceSelectionError) {
378
+ if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
379
+ id: match.id,
380
+ name: match.name,
381
+ credentialWorkspaceId: match.credentialWorkspaceId
382
+ })));
383
+ throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
384
+ }
385
+ if (error instanceof RecipientSessionInvalidError) throw workspaceNotAuthenticatedError(error.workspaceRef);
386
+ throw error;
387
+ }
388
+ }
389
+ async function requireProjectMutationContext(context, workspace) {
390
+ if (isRealMode(context)) {
391
+ const client = await requireProjectClient(context);
392
+ return {
393
+ provider: createManagementProjectProvider(client),
394
+ projects: await listRealWorkspaceProjects(client, workspace, context.runtime.signal)
395
+ };
396
+ }
397
+ return {
398
+ provider: createFixtureProjectProvider(context),
399
+ projects: listFixtureWorkspaceProjects(context, workspace)
400
+ };
401
+ }
402
+ async function requireProjectCommandContext(context, workspace, explicitProject, commandName) {
403
+ const client = isRealMode(context) ? await requireProjectClient(context) : null;
404
+ const listProjects = async () => client ? listRealWorkspaceProjects(client, workspace, context.runtime.signal) : listFixtureWorkspaceProjects(context, workspace);
405
+ const targetResult = await resolveProjectTarget({
406
+ context,
407
+ workspace,
408
+ explicitProject,
409
+ listProjects,
410
+ commandName
411
+ });
412
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
413
+ return {
414
+ provider: client ? createManagementProjectProvider(client) : createFixtureProjectProvider(context),
415
+ target: targetResult.value
416
+ };
417
+ }
418
+ async function requireProjectClient(context) {
419
+ const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
420
+ if (!client) throw authRequiredError();
421
+ return client;
422
+ }
423
+ function createFixtureProjectProvider(context) {
424
+ const fixtureFormatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
425
+ return {
426
+ async renameProject(options) {
427
+ const renamed = context.api.renameProject(options.projectId, options.name);
428
+ if (!renamed) throw projectRenameFailedError(options.name, void 0);
429
+ return {
430
+ id: renamed.id,
431
+ name: renamed.name,
432
+ ...renamed.url ? { url: renamed.url } : {}
433
+ };
434
+ },
435
+ async removeProject(options) {
436
+ const removed = context.api.removeProject(options.projectId);
437
+ if (removed.outcome === "blocked") throw projectRemoveBlockedError(options.projectId, void 0);
438
+ if (removed.outcome === "not-found") throw new CliError({
439
+ code: "PROJECT_NOT_FOUND",
440
+ domain: "project",
441
+ summary: "Project not found",
442
+ why: `No project matched "${options.projectId}".`,
443
+ fix: `Pass a project id or name from ${fixtureFormatCommand(["project", "list"])}.`,
444
+ exitCode: 1,
445
+ nextSteps: [fixtureFormatCommand(["project", "list"])]
446
+ });
447
+ },
448
+ async transferProject(options) {
449
+ if (context.api.transferProject(options.projectId, options.recipientAccessToken).outcome !== "transferred") throw projectTransferRejectedError(options.projectId, void 0);
450
+ }
451
+ };
452
+ }
453
+ function requireProjectExactConfirmation(options) {
454
+ if (options.confirm === options.id) return;
455
+ throw new CliError({
456
+ code: "CONFIRMATION_REQUIRED",
457
+ domain: "project",
458
+ summary: options.summary,
459
+ why: options.why,
460
+ fix: `Rerun with --confirm ${options.id}.`,
461
+ exitCode: 2,
462
+ nextSteps: [options.nextStep],
463
+ meta: {
464
+ expectedConfirm: options.id,
465
+ receivedConfirm: options.confirm ?? null
466
+ }
467
+ });
468
+ }
469
+ function transferRecipientRequiredError(formatCommand) {
470
+ return new CliError({
471
+ code: "TRANSFER_RECIPIENT_REQUIRED",
472
+ domain: "project",
473
+ summary: "Transfer recipient required",
474
+ why: "Project transfer needs the receiving workspace.",
475
+ fix: "Pass --to-workspace <id-or-name> for a locally authenticated workspace, or --recipient-token <token> for a cross-account transfer.",
476
+ exitCode: 2,
477
+ nextSteps: [formatCommand([
478
+ "auth",
479
+ "workspace",
480
+ "list"
481
+ ]), formatCommand([
482
+ "project",
483
+ "transfer",
484
+ "<project>",
485
+ "--to-workspace",
486
+ "<id-or-name>",
487
+ "--confirm",
488
+ "<project-id>"
489
+ ])]
490
+ });
491
+ }
492
+ function transferRecipientUnavailableError(formatCommand) {
493
+ return new CliError({
494
+ code: "TRANSFER_RECIPIENT_UNAVAILABLE",
495
+ domain: "project",
496
+ summary: "Local workspace sessions are unavailable",
497
+ why: `--to-workspace resolves locally stored OAuth sessions, but ${SERVICE_TOKEN_ENV_VAR} is set and service-token mode does not read them.`,
498
+ fix: "Pass --recipient-token <token> with an access token for the receiving workspace, or unset the service token.",
499
+ exitCode: 1,
500
+ nextSteps: [formatCommand([
501
+ "project",
502
+ "transfer",
503
+ "<project>",
504
+ "--recipient-token",
505
+ "<token>",
506
+ "--confirm",
507
+ "<project-id>"
508
+ ])]
509
+ });
510
+ }
511
+ async function cleanupLocalPinForProject(context, projectId, hooks) {
512
+ const pinResult = await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
513
+ if (pinResult.isErr()) return false;
514
+ const pin = pinResult.value;
515
+ if (pin.kind !== "present" || pin.pin.projectId !== projectId) return false;
516
+ try {
517
+ await unlink(path.join(context.runtime.cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH));
518
+ return true;
519
+ } catch {
520
+ hooks.onError(`The local pin ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} points at the removed project but could not be deleted.`);
521
+ return false;
522
+ }
523
+ }
524
+ async function rewriteOrClearLocalPinForProject(context, projectId, recipientWorkspaceId, hooks) {
525
+ const pinResult = await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
526
+ if (pinResult.isErr()) return "none";
527
+ const pin = pinResult.value;
528
+ if (pin.kind !== "present" || pin.pin.projectId !== projectId) return "none";
529
+ if (recipientWorkspaceId) {
530
+ if ((await writeLocalResolutionPin(context.runtime.cwd, {
531
+ workspaceId: recipientWorkspaceId,
532
+ projectId
533
+ }, context.runtime.signal)).isOk()) return "rewritten";
534
+ hooks.onError(`The local pin ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} points at the transferred project but could not be rewritten.`);
535
+ return "none";
536
+ }
537
+ try {
538
+ await unlink(path.join(context.runtime.cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH));
539
+ return "cleared";
540
+ } catch {
541
+ hooks.onError(`The local pin ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} points at the transferred project but could not be cleared.`);
542
+ return "none";
543
+ }
544
+ }
199
545
  async function runGitConnect(context, gitUrl, options = {}) {
200
546
  const workspace = (await requireAuthenticatedAuthState(context)).workspace;
201
547
  if (!workspace) throw workspaceRequiredError();
@@ -309,42 +655,50 @@ async function runGitDisconnect(context, options = {}) {
309
655
  async function resolveProjectShowInRealMode(context, workspace, explicitProject) {
310
656
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
311
657
  if (!client) throw authRequiredError();
312
- return inspectProjectBinding({
658
+ const result = await inspectProjectBinding({
313
659
  context,
314
660
  workspace,
315
661
  explicitProject,
316
662
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
317
663
  commandName: "project show"
318
664
  });
665
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
666
+ return result.value;
319
667
  }
320
668
  async function resolveRequiredProjectInRealMode(context, workspace, explicitProject, commandName) {
321
669
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
322
670
  if (!client) throw authRequiredError();
323
- return resolveProjectTarget({
671
+ const result = await resolveProjectTarget({
324
672
  context,
325
673
  workspace,
326
674
  explicitProject,
327
675
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
328
676
  commandName
329
677
  });
678
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
679
+ return result.value;
330
680
  }
331
681
  async function resolveProjectShowInFixtureMode(context, workspace, explicitProject) {
332
- return inspectProjectBinding({
682
+ const result = await inspectProjectBinding({
333
683
  context,
334
684
  workspace,
335
685
  explicitProject,
336
686
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
337
687
  commandName: "project show"
338
688
  });
689
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
690
+ return result.value;
339
691
  }
340
692
  async function resolveRequiredProjectInFixtureMode(context, workspace, explicitProject, commandName) {
341
- return resolveProjectTarget({
693
+ const result = await resolveProjectTarget({
342
694
  context,
343
695
  workspace,
344
696
  explicitProject,
345
697
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
346
698
  commandName
347
699
  });
700
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
701
+ return result.value;
348
702
  }
349
703
  async function listRealWorkspaceProjects(client, workspace, signal) {
350
704
  const { data } = await client.GET("/v1/projects", { signal });
@@ -352,6 +706,7 @@ async function listRealWorkspaceProjects(client, workspace, signal) {
352
706
  id: project.id,
353
707
  name: project.name,
354
708
  ..."url" in project && typeof project.url === "string" ? { url: project.url } : {},
709
+ ..."defaultRegion" in project ? { defaultRegion: project.defaultRegion } : {},
355
710
  slug: "slug" in project && typeof project.slug === "string" ? project.slug : null,
356
711
  workspace: {
357
712
  id: project.workspace.id,
@@ -699,4 +1054,4 @@ function repoConnectionFixForStatus(status) {
699
1054
  return "Re-run with --trace for the underlying API response details.";
700
1055
  }
701
1056
  //#endregion
702
- export { listRealWorkspaceProjects, runGitConnect, runGitDisconnect, runProjectCreate, runProjectLink, runProjectList, runProjectShow };
1057
+ export { listFixtureWorkspaceProjects, listRealWorkspaceProjects, runGitConnect, runGitDisconnect, runProjectCreate, runProjectLink, runProjectList, runProjectRemove, runProjectRename, runProjectShow, runProjectTransfer };
@@ -4,6 +4,7 @@ function createSelectPromptPort(context) {
4
4
  return { select: ({ message, choices }) => selectPrompt({
5
5
  input: context.runtime.stdin,
6
6
  output: context.runtime.stderr,
7
+ signal: context.runtime.signal,
7
8
  message,
8
9
  choices
9
10
  }) };
@@ -0,0 +1,20 @@
1
+ import { formatPrismaCliCommand } from "../../shell/cli-command.js";
2
+ import { resolvePackageRunner, resolvePackageRunnerSync } from "./package-manager.js";
3
+ //#region src/lib/agent/cli-command.ts
4
+ async function resolvePrismaCliPackageCommandFormatter(options) {
5
+ return createPrismaCliPackageCommandFormatter(await resolvePackageRunner(options));
6
+ }
7
+ async function resolvePrismaCliPackageCommand(options) {
8
+ return (await resolvePrismaCliPackageCommandFormatter(options))(options.args);
9
+ }
10
+ function resolvePrismaCliPackageCommandFormatterSync(cwd) {
11
+ return createPrismaCliPackageCommandFormatter(resolvePackageRunnerSync(cwd));
12
+ }
13
+ function resolvePrismaCliPackageCommandSync(cwd, args) {
14
+ return resolvePrismaCliPackageCommandFormatterSync(cwd)(args);
15
+ }
16
+ function createPrismaCliPackageCommandFormatter(packageRunner) {
17
+ return (args) => formatPrismaCliCommand(args, { packageRunner });
18
+ }
19
+ //#endregion
20
+ export { resolvePrismaCliPackageCommand, resolvePrismaCliPackageCommandFormatterSync, resolvePrismaCliPackageCommandSync };
@@ -0,0 +1,12 @@
1
+ //#region src/lib/agent/constants.ts
2
+ const PRISMA_SKILLS_SOURCE = "prisma/skills";
3
+ const PRISMA_SKILLS_LOCK_FILENAME = "skills-lock.json";
4
+ const SKILLS_CLI_PACKAGE = "skills@latest";
5
+ const DEFAULT_PRISMA_AGENT_SKILLS = ["*"];
6
+ const PRISMA_COMPUTE_AGENT_SKILL = "prisma-compute";
7
+ const DEFAULT_PRISMA_AGENT_TARGETS = ["codex", "claude-code"];
8
+ const PRISMA_AGENT_INSTALL_ARGS = ["agent", "install"];
9
+ const PRISMA_AGENT_UPDATE_ARGS = ["agent", "update"];
10
+ const PRISMA_AGENT_STATUS_ARGS = ["agent", "status"];
11
+ //#endregion
12
+ export { DEFAULT_PRISMA_AGENT_SKILLS, DEFAULT_PRISMA_AGENT_TARGETS, PRISMA_AGENT_INSTALL_ARGS, PRISMA_AGENT_STATUS_ARGS, PRISMA_AGENT_UPDATE_ARGS, PRISMA_COMPUTE_AGENT_SKILL, PRISMA_SKILLS_LOCK_FILENAME, PRISMA_SKILLS_SOURCE, SKILLS_CLI_PACKAGE };
@@ -0,0 +1,99 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ //#region src/lib/agent/package-manager.ts
4
+ const LOCKFILE_PACKAGE_MANAGERS = [
5
+ {
6
+ packageManager: "bun",
7
+ fileNames: ["bun.lock", "bun.lockb"]
8
+ },
9
+ {
10
+ packageManager: "pnpm",
11
+ fileNames: ["pnpm-lock.yaml", "pnpm-workspace.yaml"]
12
+ },
13
+ {
14
+ packageManager: "yarn",
15
+ fileNames: ["yarn.lock"]
16
+ },
17
+ {
18
+ packageManager: "npm",
19
+ fileNames: ["package-lock.json", "npm-shrinkwrap.json"]
20
+ }
21
+ ];
22
+ async function resolveSkillsPackageRunner(options) {
23
+ return resolvePackageRunner(options);
24
+ }
25
+ async function resolvePackageRunner(options) {
26
+ options.signal.throwIfAborted();
27
+ const packageManager = detectPackageManagerSync(options.cwd, options.signal) ?? "npm";
28
+ options.signal.throwIfAborted();
29
+ return packageRunnerForPackageManager(packageManager);
30
+ }
31
+ function resolvePackageRunnerSync(cwd) {
32
+ return packageRunnerForPackageManager(detectPackageManagerSync(cwd) ?? "npm");
33
+ }
34
+ function detectPackageManagerSync(cwd, signal) {
35
+ let directory = path.resolve(cwd);
36
+ while (true) {
37
+ signal?.throwIfAborted();
38
+ const packageJsonManager = readPackageJsonPackageManager(directory);
39
+ if (packageJsonManager) return packageJsonManager;
40
+ const lockfileManager = readLockfilePackageManager(directory, signal);
41
+ if (lockfileManager) return lockfileManager;
42
+ const parent = path.dirname(directory);
43
+ if (parent === directory) return null;
44
+ directory = parent;
45
+ }
46
+ }
47
+ function readPackageJsonPackageManager(directory) {
48
+ const packageJsonPath = path.join(directory, "package.json");
49
+ let content;
50
+ try {
51
+ content = readFileSync(packageJsonPath, "utf8");
52
+ } catch (error) {
53
+ if (isMissingFileError(error)) return null;
54
+ throw error;
55
+ }
56
+ try {
57
+ return parsePackageManager(JSON.parse(content).packageManager);
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+ function readLockfilePackageManager(directory, signal) {
63
+ for (const candidate of LOCKFILE_PACKAGE_MANAGERS) for (const fileName of candidate.fileNames) {
64
+ signal?.throwIfAborted();
65
+ if (fileExists(path.join(directory, fileName))) return candidate.packageManager;
66
+ }
67
+ return null;
68
+ }
69
+ function fileExists(filePath) {
70
+ try {
71
+ return statSync(filePath).isFile();
72
+ } catch (error) {
73
+ if (isMissingFileError(error)) return false;
74
+ throw error;
75
+ }
76
+ }
77
+ function parsePackageManager(value) {
78
+ if (typeof value !== "string") return null;
79
+ const normalized = value.trim().toLowerCase();
80
+ if (normalized === "bun" || normalized.startsWith("bun@")) return "bun";
81
+ if (normalized === "pnpm" || normalized.startsWith("pnpm@")) return "pnpm";
82
+ if (normalized === "yarn" || normalized.startsWith("yarn@")) return "yarn";
83
+ if (normalized === "npm" || normalized.startsWith("npm@")) return "npm";
84
+ return null;
85
+ }
86
+ function packageRunnerForPackageManager(packageManager) {
87
+ switch (packageManager) {
88
+ case "bun": return ["bunx"];
89
+ case "pnpm": return ["pnpm", "dlx"];
90
+ case "yarn": return ["yarn", "dlx"];
91
+ case "npm": return ["npx", "-y"];
92
+ }
93
+ }
94
+ function isMissingFileError(error) {
95
+ const code = error.code;
96
+ return code === "ENOENT" || code === "ENOTDIR";
97
+ }
98
+ //#endregion
99
+ export { detectPackageManagerSync, resolvePackageRunner, resolvePackageRunnerSync, resolveSkillsPackageRunner };