@prisma/cli 3.0.0-beta.2 → 3.0.0-beta.20

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 (87) hide show
  1. package/README.md +5 -16
  2. package/dist/adapters/git.js +8 -3
  3. package/dist/adapters/local-state.js +26 -7
  4. package/dist/adapters/mock-api.js +81 -2
  5. package/dist/adapters/token-storage.js +344 -25
  6. package/dist/cli.js +18 -3
  7. package/dist/cli2.js +12 -4
  8. package/dist/commands/agent/index.js +60 -0
  9. package/dist/commands/app/index.js +90 -61
  10. package/dist/commands/auth/index.js +55 -2
  11. package/dist/commands/branch/index.js +2 -27
  12. package/dist/commands/build/index.js +29 -0
  13. package/dist/commands/database/index.js +159 -0
  14. package/dist/commands/env.js +8 -4
  15. package/dist/commands/git/index.js +1 -1
  16. package/dist/commands/project/index.js +1 -1
  17. package/dist/controllers/agent.js +228 -0
  18. package/dist/controllers/app-env-api.js +54 -0
  19. package/dist/controllers/app-env-file.js +181 -0
  20. package/dist/controllers/app-env.js +286 -131
  21. package/dist/controllers/app.js +849 -309
  22. package/dist/controllers/auth.js +255 -11
  23. package/dist/controllers/branch.js +78 -48
  24. package/dist/controllers/build.js +88 -0
  25. package/dist/controllers/database.js +318 -0
  26. package/dist/controllers/project.js +156 -87
  27. package/dist/controllers/select-prompt-port.js +1 -0
  28. package/dist/lib/agent/cli-command.js +17 -0
  29. package/dist/lib/agent/constants.js +12 -0
  30. package/dist/lib/agent/package-manager.js +99 -0
  31. package/dist/lib/agent/setup-status.js +83 -0
  32. package/dist/lib/app/app-interaction.js +5 -0
  33. package/dist/lib/app/{preview-provider.js → app-provider.js} +220 -104
  34. package/dist/lib/app/branch-database-api.js +102 -0
  35. package/dist/lib/app/branch-database-deploy.js +326 -0
  36. package/dist/lib/app/branch-database.js +216 -0
  37. package/dist/lib/app/build-settings.js +93 -0
  38. package/dist/lib/app/build.js +82 -0
  39. package/dist/lib/app/bun-project.js +15 -9
  40. package/dist/lib/app/compute-config.js +145 -0
  41. package/dist/lib/app/deploy-plan.js +59 -0
  42. package/dist/lib/app/{preview-progress.js → deploy-progress.js} +12 -12
  43. package/dist/lib/app/env-config.js +1 -1
  44. package/dist/lib/app/env-file.js +82 -0
  45. package/dist/lib/app/env-vars.js +28 -2
  46. package/dist/lib/app/local-dev.js +8 -49
  47. package/dist/lib/app/production-deploy-gate.js +162 -0
  48. package/dist/lib/app/read-branch.js +30 -0
  49. package/dist/lib/auth/auth-ops.js +38 -23
  50. package/dist/lib/auth/guard.js +6 -2
  51. package/dist/lib/auth/login.js +117 -15
  52. package/dist/lib/database/provider.js +167 -0
  53. package/dist/lib/diagnostics.js +15 -0
  54. package/dist/lib/fs/home-path.js +24 -0
  55. package/dist/lib/git/local-branch.js +53 -0
  56. package/dist/lib/git/local-status.js +57 -0
  57. package/dist/lib/project/interactive-setup.js +2 -1
  58. package/dist/lib/project/local-pin.js +183 -34
  59. package/dist/lib/project/resolution.js +206 -50
  60. package/dist/lib/project/setup.js +64 -18
  61. package/dist/output/patterns.js +1 -1
  62. package/dist/presenters/agent.js +74 -0
  63. package/dist/presenters/app-env.js +149 -14
  64. package/dist/presenters/app.js +187 -26
  65. package/dist/presenters/auth.js +99 -2
  66. package/dist/presenters/branch.js +37 -102
  67. package/dist/presenters/database.js +274 -0
  68. package/dist/presenters/project.js +44 -26
  69. package/dist/presenters/verbose-context.js +64 -0
  70. package/dist/shell/cli-command.js +12 -0
  71. package/dist/shell/command-arguments.js +7 -1
  72. package/dist/shell/command-meta.js +243 -15
  73. package/dist/shell/command-runner.js +60 -21
  74. package/dist/shell/diagnostics-output.js +57 -0
  75. package/dist/shell/errors.js +61 -1
  76. package/dist/shell/help.js +31 -20
  77. package/dist/shell/output.js +10 -1
  78. package/dist/shell/prompt.js +7 -3
  79. package/dist/shell/runtime.js +10 -6
  80. package/dist/shell/ui.js +42 -3
  81. package/dist/shell/update-check.js +247 -0
  82. package/dist/use-cases/auth.js +68 -1
  83. package/dist/use-cases/branch.js +20 -68
  84. package/dist/use-cases/create-cli-gateways.js +2 -17
  85. package/package.json +27 -10
  86. package/dist/lib/app/preview-build.js +0 -284
  87. package/dist/lib/app/preview-interaction.js +0 -5
@@ -1,5 +1,11 @@
1
+ import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
2
+ import { PRISMA_AGENT_INSTALL_ARGS, PRISMA_AGENT_STATUS_ARGS, PRISMA_AGENT_UPDATE_ARGS, PRISMA_COMPUTE_AGENT_SKILL } from "../lib/agent/constants.js";
1
3
  //#region src/shell/command-meta.ts
2
4
  const COMMAND_DESCRIPTOR_ID = Symbol("prisma.commandDescriptorId");
5
+ function agentCommandExamples(runtime, commands) {
6
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(runtime.cwd);
7
+ return commands.map((command) => formatCommand(command));
8
+ }
3
9
  const DESCRIPTORS = [
4
10
  {
5
11
  id: "root",
@@ -14,6 +20,71 @@ const DESCRIPTORS = [
14
20
  description: "Show CLI build and environment",
15
21
  examples: ["prisma-cli version", "prisma-cli version --json"]
16
22
  },
23
+ {
24
+ id: "agent",
25
+ path: ["prisma", "agent"],
26
+ description: "Install Prisma context for AI coding agents",
27
+ examples: (runtime) => agentCommandExamples(runtime, [
28
+ PRISMA_AGENT_INSTALL_ARGS,
29
+ PRISMA_AGENT_UPDATE_ARGS,
30
+ PRISMA_AGENT_STATUS_ARGS
31
+ ])
32
+ },
33
+ {
34
+ id: "agent.install",
35
+ path: [
36
+ "prisma",
37
+ "agent",
38
+ "install"
39
+ ],
40
+ description: "Install Prisma skills for AI coding agents",
41
+ examples: (runtime) => agentCommandExamples(runtime, [
42
+ PRISMA_AGENT_INSTALL_ARGS,
43
+ [
44
+ ...PRISMA_AGENT_INSTALL_ARGS,
45
+ "--agent",
46
+ "codex"
47
+ ],
48
+ [...PRISMA_AGENT_INSTALL_ARGS, "--all-agents"],
49
+ [
50
+ ...PRISMA_AGENT_INSTALL_ARGS,
51
+ "--skill",
52
+ PRISMA_COMPUTE_AGENT_SKILL
53
+ ]
54
+ ])
55
+ },
56
+ {
57
+ id: "agent.update",
58
+ path: [
59
+ "prisma",
60
+ "agent",
61
+ "update"
62
+ ],
63
+ description: "Refresh Prisma skills for AI coding agents",
64
+ examples: (runtime) => agentCommandExamples(runtime, [
65
+ PRISMA_AGENT_UPDATE_ARGS,
66
+ [
67
+ ...PRISMA_AGENT_UPDATE_ARGS,
68
+ "--agent",
69
+ "codex"
70
+ ],
71
+ [...PRISMA_AGENT_UPDATE_ARGS, "--all-agents"]
72
+ ])
73
+ },
74
+ {
75
+ id: "agent.status",
76
+ path: [
77
+ "prisma",
78
+ "agent",
79
+ "status"
80
+ ],
81
+ description: "Show installed Prisma skills",
82
+ examples: (runtime) => agentCommandExamples(runtime, [
83
+ PRISMA_AGENT_STATUS_ARGS,
84
+ [...PRISMA_AGENT_STATUS_ARGS, "--json"],
85
+ [...PRISMA_AGENT_STATUS_ARGS, "--global"]
86
+ ])
87
+ },
17
88
  {
18
89
  id: "auth",
19
90
  path: ["prisma", "auth"],
@@ -50,6 +121,58 @@ const DESCRIPTORS = [
50
121
  description: "Show the authenticated user and accessible workspace",
51
122
  examples: ["prisma-cli auth whoami", "prisma-cli auth whoami --json"]
52
123
  },
124
+ {
125
+ id: "auth.workspace",
126
+ path: [
127
+ "prisma",
128
+ "auth",
129
+ "workspace"
130
+ ],
131
+ description: "Manage local authenticated workspaces",
132
+ examples: [
133
+ "prisma-cli auth workspace list",
134
+ "prisma-cli auth workspace use",
135
+ "prisma-cli auth workspace use wksp_123",
136
+ "prisma-cli auth workspace logout wksp_123"
137
+ ]
138
+ },
139
+ {
140
+ id: "auth.workspace.list",
141
+ path: [
142
+ "prisma",
143
+ "auth",
144
+ "workspace",
145
+ "list"
146
+ ],
147
+ description: "List locally authenticated workspaces",
148
+ examples: ["prisma-cli auth workspace list", "prisma-cli auth workspace list --json"]
149
+ },
150
+ {
151
+ id: "auth.workspace.use",
152
+ path: [
153
+ "prisma",
154
+ "auth",
155
+ "workspace",
156
+ "use"
157
+ ],
158
+ description: "Switch the local CLI workspace",
159
+ examples: [
160
+ "prisma-cli auth workspace use",
161
+ "prisma-cli auth workspace use wksp_123",
162
+ "prisma-cli auth workspace use \"Acme Inc\""
163
+ ]
164
+ },
165
+ {
166
+ id: "auth.workspace.logout",
167
+ path: [
168
+ "prisma",
169
+ "auth",
170
+ "workspace",
171
+ "logout"
172
+ ],
173
+ description: "Remove one local OAuth workspace session",
174
+ examples: ["prisma-cli auth workspace logout wksp_123", "prisma-cli auth workspace logout \"Acme Inc\""]
175
+ },
53
176
  {
54
177
  id: "project",
55
178
  path: ["prisma", "project"],
@@ -69,8 +192,34 @@ const DESCRIPTORS = [
69
192
  {
70
193
  id: "branch",
71
194
  path: ["prisma", "branch"],
72
- description: "View your active Platform branches",
73
- examples: ["prisma-cli branch list", "prisma-cli branch show"]
195
+ description: "View your Platform branches",
196
+ examples: ["prisma-cli branch list"]
197
+ },
198
+ {
199
+ id: "build",
200
+ path: ["prisma", "build"],
201
+ description: "Inspect builds",
202
+ examples: ["prisma-cli build logs <build_id>"]
203
+ },
204
+ {
205
+ id: "build.logs",
206
+ path: [
207
+ "prisma",
208
+ "build",
209
+ "logs"
210
+ ],
211
+ description: "Stream the logs for a build",
212
+ examples: ["prisma-cli build logs <build_id>", "prisma-cli build logs <build_id> --follow"]
213
+ },
214
+ {
215
+ id: "database",
216
+ path: ["prisma", "database"],
217
+ description: "Manage Prisma Postgres databases for a project",
218
+ examples: [
219
+ "prisma-cli database list",
220
+ "prisma-cli database create my-db",
221
+ "prisma-cli database connection create db_123"
222
+ ]
74
223
  },
75
224
  {
76
225
  id: "git",
@@ -153,28 +302,99 @@ const DESCRIPTORS = [
153
302
  "branch",
154
303
  "list"
155
304
  ],
156
- description: "List active Platform branches for the resolved project",
305
+ description: "List Platform branches for the resolved project",
157
306
  examples: ["prisma-cli branch list", "prisma-cli branch list --json"]
158
307
  },
159
308
  {
160
- id: "branch.show",
309
+ id: "database.list",
161
310
  path: [
162
311
  "prisma",
163
- "branch",
312
+ "database",
313
+ "list"
314
+ ],
315
+ description: "List Prisma Postgres databases for the resolved project",
316
+ examples: [
317
+ "prisma-cli database list",
318
+ "prisma-cli database list --branch feature/foo",
319
+ "prisma-cli database list --json"
320
+ ]
321
+ },
322
+ {
323
+ id: "database.show",
324
+ path: [
325
+ "prisma",
326
+ "database",
164
327
  "show"
165
328
  ],
166
- description: "Show the Platform branch matching your current Git branch",
167
- examples: ["prisma-cli branch show", "prisma-cli branch show --json"]
329
+ description: "Show database metadata without secret values",
330
+ examples: ["prisma-cli database show db_123", "prisma-cli database show acme-preview --branch preview --json"]
168
331
  },
169
332
  {
170
- id: "branch.use",
333
+ id: "database.create",
171
334
  path: [
172
335
  "prisma",
173
- "branch",
174
- "use"
336
+ "database",
337
+ "create"
338
+ ],
339
+ description: "Create a Prisma Postgres database and print its one-time connection URL",
340
+ examples: ["prisma-cli database create my-db", "prisma-cli database create my-db --branch feature/foo --region eu-central-1"]
341
+ },
342
+ {
343
+ id: "database.remove",
344
+ path: [
345
+ "prisma",
346
+ "database",
347
+ "remove"
175
348
  ],
176
- description: "Change the local default branch context.",
177
- examples: ["prisma-cli branch use", "prisma-cli branch use production"]
349
+ description: "Remove a database after exact id confirmation",
350
+ examples: ["prisma-cli database remove db_123 --confirm db_123"]
351
+ },
352
+ {
353
+ id: "database.connection",
354
+ path: [
355
+ "prisma",
356
+ "database",
357
+ "connection"
358
+ ],
359
+ description: "Manage one-time-view database connection strings",
360
+ examples: [
361
+ "prisma-cli database connection list db_123",
362
+ "prisma-cli database connection create db_123",
363
+ "prisma-cli database connection remove conn_123 --confirm conn_123"
364
+ ]
365
+ },
366
+ {
367
+ id: "database.connection.list",
368
+ path: [
369
+ "prisma",
370
+ "database",
371
+ "connection",
372
+ "list"
373
+ ],
374
+ description: "List database connection metadata without secret values",
375
+ examples: ["prisma-cli database connection list db_123", "prisma-cli database connection list acme-preview --branch preview --json"]
376
+ },
377
+ {
378
+ id: "database.connection.create",
379
+ path: [
380
+ "prisma",
381
+ "database",
382
+ "connection",
383
+ "create"
384
+ ],
385
+ description: "Create a database connection and print its one-time connection URL",
386
+ examples: ["prisma-cli database connection create db_123", "prisma-cli database connection create db_123 --name readonly"]
387
+ },
388
+ {
389
+ id: "database.connection.remove",
390
+ path: [
391
+ "prisma",
392
+ "database",
393
+ "connection",
394
+ "remove"
395
+ ],
396
+ description: "Remove a database connection after exact id confirmation",
397
+ examples: ["prisma-cli database connection remove conn_123 --confirm conn_123"]
178
398
  },
179
399
  {
180
400
  id: "app.build",
@@ -208,15 +428,18 @@ const DESCRIPTORS = [
208
428
  "deploy"
209
429
  ],
210
430
  description: "Creates a new deployment for the app",
211
- longDescription: "Agent skills for guided Next.js deploys are available from the Prisma CLI skill cluster.",
212
431
  examples: [
213
432
  "prisma-cli app deploy",
214
433
  "prisma-cli app deploy --project proj_123",
215
434
  "prisma-cli app deploy --create-project my-app --yes",
216
435
  "prisma-cli app deploy --app my-app --env DATABASE_URL=postgresql://example",
436
+ "prisma-cli app deploy --env .env",
437
+ "prisma-cli app deploy --db",
438
+ "prisma-cli app deploy --db --yes",
217
439
  "prisma-cli app deploy --app my-app --framework nextjs --http-port 3000",
440
+ "prisma-cli app deploy --app my-app --region us-west-1",
218
441
  "prisma-cli app deploy --branch feat-login --framework hono",
219
- "pnpm dlx skills@latest add prisma/prisma-cli/skills#cli-v<cli-version> --all",
442
+ "prisma-cli app deploy --prod --yes",
220
443
  "prisma-cli app deploy --framework bun --entry src/server.ts"
221
444
  ]
222
445
  },
@@ -378,8 +601,9 @@ const DESCRIPTORS = [
378
601
  ],
379
602
  description: "Manage environment variables for the active project",
380
603
  examples: [
381
- "prisma-cli project env list --role production",
604
+ "prisma-cli project env list",
382
605
  "prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
606
+ "prisma-cli project env add --file .env --role preview",
383
607
  "prisma-cli project env add DATABASE_URL=postgresql://branch --branch feature/foo",
384
608
  "prisma-cli project env remove STRIPE_KEY --role preview"
385
609
  ]
@@ -396,7 +620,9 @@ const DESCRIPTORS = [
396
620
  examples: [
397
621
  "prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
398
622
  "prisma-cli project env add STRIPE_KEY=sk_test_xxx --role preview",
623
+ "prisma-cli project env add --file .env --role preview",
399
624
  "prisma-cli project env add DATABASE_URL=postgresql://branch --branch feature/foo",
625
+ "prisma-cli project env add --file .env.local --branch feature/foo",
400
626
  "API_URL=https://api.example prisma-cli project env add API_URL --project proj_123 --role preview"
401
627
  ]
402
628
  },
@@ -412,6 +638,7 @@ const DESCRIPTORS = [
412
638
  examples: [
413
639
  "prisma-cli project env update STRIPE_KEY=sk_new_xxx --role production",
414
640
  "prisma-cli project env update STRIPE_KEY=sk_new_xxx --role preview",
641
+ "prisma-cli project env update --file .env --role production",
415
642
  "prisma-cli project env update DATABASE_URL=postgresql://branch --branch feature/foo"
416
643
  ]
417
644
  },
@@ -425,6 +652,7 @@ const DESCRIPTORS = [
425
652
  ],
426
653
  description: "List environment variable metadata for a scope (no values).",
427
654
  examples: [
655
+ "prisma-cli project env list",
428
656
  "prisma-cli project env list --role production",
429
657
  "prisma-cli project env list --role preview",
430
658
  "prisma-cli project env list --branch feature/foo"
@@ -1,47 +1,86 @@
1
- import { CliError, authRequiredError } from "./errors.js";
2
- import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess } from "./output.js";
1
+ import { CliError, authConfigInvalidError, authRequiredError, commandCanceledError } from "./errors.js";
3
2
  import { getCommandDescriptor } from "./command-meta.js";
4
3
  import { resolveGlobalFlags } from "./global-flags.js";
5
4
  import { createCommandContext } from "./runtime.js";
5
+ import { collectCommandDiagnostics } from "../lib/diagnostics.js";
6
+ import { renderCommandDiagnostics } from "./diagnostics-output.js";
7
+ import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess } from "./output.js";
6
8
  import { AuthError } from "@prisma/management-api-sdk";
7
9
  //#region src/shell/command-runner.ts
8
- function toCliError(error) {
10
+ function toCliError(error, runtime) {
11
+ if (isCancellationError(error) || runtime.signal.aborted) return commandCanceledError();
9
12
  if (error instanceof CliError) return error;
10
13
  if (error instanceof AuthError) return authRequiredError(["prisma-cli auth login"], { debug: error.message });
14
+ if (error instanceof Error && error.message.startsWith("PRISMA_SERVICE_TOKEN is set but empty")) return authConfigInvalidError(error.message);
11
15
  return null;
12
16
  }
17
+ function isCancellationError(error) {
18
+ if (!(error instanceof Error)) return false;
19
+ return error.name === "AbortError" || error.name === "CancelledError";
20
+ }
13
21
  async function runCommand(runtime, commandName, options, handler, presenter) {
14
- const flags = resolveGlobalFlags(runtime.argv, options);
15
- const context = await createCommandContext(runtime, flags);
22
+ const context = await createCommandContext(runtime, resolveGlobalFlags(runtime.argv, options));
16
23
  const descriptor = getCommandDescriptor(commandName);
24
+ const startedAt = Date.now();
17
25
  try {
18
- const success = await handler(context);
19
- if (flags.json) {
20
- writeJsonSuccess(context.output, {
21
- ...success,
22
- result: presenter.renderJson ? presenter.renderJson(success.result) : success.result
23
- });
24
- return;
25
- }
26
- if (flags.quiet) return;
27
- writeHumanLines(context.output, presenter.renderHuman(context, descriptor, success.result));
26
+ await writeCommandSuccess(context, descriptor, await handler(context), presenter, Date.now() - startedAt);
28
27
  } catch (error) {
29
- const cliError = toCliError(error);
28
+ const cliError = toCliError(error, runtime);
30
29
  if (cliError) {
31
- if (flags.json) writeJsonError(context.output, commandName, cliError);
32
- else writeHumanError(context.output, context.ui, cliError, { trace: flags.trace });
30
+ writeCommandError(context, commandName, cliError);
33
31
  process.exitCode = cliError.exitCode;
34
32
  return;
35
33
  }
36
34
  throw error;
37
35
  }
38
36
  }
39
- async function runStreamingCommand(runtime, commandName, options, handler) {
37
+ async function writeCommandSuccess(context, descriptor, success, presenter, durationMs) {
38
+ if (context.flags.json) {
39
+ writeJsonSuccess(context.output, {
40
+ ...success,
41
+ result: presenter.renderJson ? presenter.renderJson(success.result) : success.result
42
+ });
43
+ return;
44
+ }
45
+ const stdout = presenter.renderStdout?.(context, descriptor, success.result) ?? [];
46
+ if (context.flags.quiet) {
47
+ writeStdoutLines(context, stdout);
48
+ return;
49
+ }
50
+ const rendered = presenter.renderHuman(context, descriptor, success.result);
51
+ const diagnostics = await renderBestEffortCommandDiagnostics(context, {
52
+ enabled: context.flags.verbose && rendered.length > 0,
53
+ durationMs
54
+ });
55
+ const humanLines = [...rendered, ...diagnostics];
56
+ if (stdout.length > 0 && humanLines.length > 0) humanLines.push("");
57
+ writeHumanLines(context.output, humanLines);
58
+ writeStdoutLines(context, stdout);
59
+ }
60
+ function writeCommandError(context, commandName, cliError) {
61
+ if (context.flags.json) {
62
+ writeJsonError(context.output, commandName, cliError);
63
+ return;
64
+ }
65
+ writeHumanError(context.output, context.ui, cliError, { trace: context.flags.trace });
66
+ }
67
+ function writeStdoutLines(context, lines) {
68
+ if (lines.length > 0) context.output.stdout.write(`${lines.join("\n")}\n`);
69
+ }
70
+ async function renderBestEffortCommandDiagnostics(context, options) {
71
+ if (!options.enabled) return [];
72
+ try {
73
+ return renderCommandDiagnostics(context, await collectCommandDiagnostics(context, { durationMs: options.durationMs }));
74
+ } catch {
75
+ return [];
76
+ }
77
+ }
78
+ async function runStreamingCommand(runtime, commandName, options, handler, streamOptions) {
40
79
  const flags = resolveGlobalFlags(runtime.argv, options);
41
80
  const context = await createCommandContext(runtime, flags);
42
81
  try {
43
82
  await handler(context);
44
- if (flags.json) writeJsonEvent(context.output, {
83
+ if (flags.json && (streamOptions?.emitJsonSuccessEvent ?? true)) writeJsonEvent(context.output, {
45
84
  type: "success",
46
85
  command: commandName,
47
86
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -51,7 +90,7 @@ async function runStreamingCommand(runtime, commandName, options, handler) {
51
90
  nextActions: []
52
91
  });
53
92
  } catch (error) {
54
- const cliError = toCliError(error);
93
+ const cliError = toCliError(error, runtime);
55
94
  if (cliError) {
56
95
  if (flags.json) writeJsonEvent(context.output, {
57
96
  type: "error",
@@ -0,0 +1,57 @@
1
+ import { renderVerboseBlock } from "./ui.js";
2
+ import { shortenHomePath } from "../lib/fs/home-path.js";
3
+ //#region src/shell/diagnostics-output.ts
4
+ function renderCommandDiagnostics(context, diagnostics, rows = [], options = {}) {
5
+ if (!diagnostics) return [];
6
+ const { env } = context.runtime;
7
+ const git = diagnostics.git;
8
+ return renderVerboseBlock(context.ui, [
9
+ ...rows,
10
+ ...diagnostics.durationMs === void 0 ? [] : [{
11
+ key: "duration",
12
+ value: formatDuration(diagnostics.durationMs)
13
+ }],
14
+ {
15
+ key: "cwd",
16
+ value: formatLocalPath(diagnostics.cwd, env)
17
+ },
18
+ {
19
+ key: "state file",
20
+ value: formatLocalPath(diagnostics.stateFilePath, env)
21
+ },
22
+ ...git ? [
23
+ {
24
+ key: "git ref",
25
+ value: git.ref ?? "detached",
26
+ tone: git.ref ? "default" : "dim"
27
+ },
28
+ {
29
+ key: "git sha",
30
+ value: git.sha ?? "unknown",
31
+ tone: git.sha ? "default" : "dim"
32
+ },
33
+ {
34
+ key: "git dirty",
35
+ value: formatDirtyState(git.dirty),
36
+ tone: git.dirty ? "warning" : "dim"
37
+ }
38
+ ] : [{
39
+ key: "git",
40
+ value: "not detected",
41
+ tone: "dim"
42
+ }]
43
+ ], { title: options.title ?? "Local context" });
44
+ }
45
+ function formatLocalPath(value, env) {
46
+ return shortenHomePath(value, env);
47
+ }
48
+ function formatDirtyState(dirty) {
49
+ if (dirty === null) return "unknown";
50
+ return dirty ? "yes" : "no";
51
+ }
52
+ function formatDuration(durationMs) {
53
+ if (durationMs < 1e3) return `${durationMs}ms`;
54
+ return `${(durationMs / 1e3).toFixed(1)}s`;
55
+ }
56
+ //#endregion
57
+ export { renderCommandDiagnostics };
@@ -56,9 +56,69 @@ function authRequiredError(nextSteps = ["prisma-cli auth login"], options = {})
56
56
  nextSteps
57
57
  });
58
58
  }
59
+ function authConfigInvalidError(message) {
60
+ return new CliError({
61
+ code: "AUTH_CONFIG_INVALID",
62
+ domain: "auth",
63
+ summary: "Authentication configuration is invalid",
64
+ why: message,
65
+ fix: "Provide a valid PRISMA_SERVICE_TOKEN value, or unset the variable to use local OAuth login.",
66
+ exitCode: 1,
67
+ nextSteps: ["prisma-cli auth login"]
68
+ });
69
+ }
70
+ function commandCanceledError() {
71
+ return new CliError({
72
+ code: "COMMAND_CANCELED",
73
+ domain: "cli",
74
+ summary: "Command canceled",
75
+ why: null,
76
+ fix: null,
77
+ exitCode: 130,
78
+ humanLines: ["Command canceled [COMMAND_CANCELED]"]
79
+ });
80
+ }
59
81
  function workspaceRequiredError() {
60
82
  return usageError("Workspace required", "This command needs an active workspace, but the authenticated session does not have one.", "Run prisma-cli auth login and choose a workspace.", ["prisma-cli auth login"], "auth");
61
83
  }
84
+ function workspaceSwitchUnavailableError() {
85
+ return new CliError({
86
+ code: "WORKSPACE_SWITCH_UNAVAILABLE",
87
+ domain: "auth",
88
+ summary: "Workspace switching is unavailable",
89
+ why: "PRISMA_SERVICE_TOKEN is set, so authenticated commands use that token instead of local OAuth workspaces.",
90
+ fix: "Unset PRISMA_SERVICE_TOKEN to switch between local OAuth workspaces, or use a token for the workspace you want.",
91
+ exitCode: 1,
92
+ nextSteps: ["unset PRISMA_SERVICE_TOKEN", "prisma-cli auth workspace list"]
93
+ });
94
+ }
95
+ function workspaceNotAuthenticatedError(workspaceRef) {
96
+ return new CliError({
97
+ code: "WORKSPACE_NOT_AUTHENTICATED",
98
+ domain: "auth",
99
+ summary: "Workspace is not authenticated",
100
+ why: `No stored OAuth session matched "${workspaceRef}".`,
101
+ fix: "Run prisma-cli auth login and authorize that workspace, then switch to it.",
102
+ meta: { workspaceRef },
103
+ exitCode: 1,
104
+ nextSteps: ["prisma-cli auth workspace list", "prisma-cli auth login"]
105
+ });
106
+ }
107
+ function workspaceAmbiguousError(workspaceRef, matches) {
108
+ return new CliError({
109
+ code: "WORKSPACE_AMBIGUOUS",
110
+ domain: "auth",
111
+ summary: "Workspace name is ambiguous",
112
+ why: `Multiple authenticated workspaces matched "${workspaceRef}".`,
113
+ fix: "Run prisma-cli auth workspace list and switch by workspace id.",
114
+ meta: {
115
+ workspaceRef,
116
+ matches
117
+ },
118
+ exitCode: 2,
119
+ nextSteps: ["prisma-cli auth workspace list"]
120
+ });
121
+ }
62
122
  function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cli") {
63
123
  return new CliError({
64
124
  code: "FEATURE_UNAVAILABLE",
@@ -71,4 +131,4 @@ function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cl
71
131
  });
72
132
  }
73
133
  //#endregion
74
- export { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError };
134
+ export { CliError, authConfigInvalidError, authRequiredError, commandCanceledError, featureUnavailableError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceRequiredError, workspaceSwitchUnavailableError };
@@ -1,5 +1,5 @@
1
- import { createShellUi, padDisplay, wrapText } from "./ui.js";
2
1
  import { formatDescriptorLabel, getDescriptorForCommand } from "./command-meta.js";
2
+ import { createShellUi, padDisplay, wrapText } from "./ui.js";
3
3
  import { COMPACT_GLOBAL_OPTION_FLAGS, resolveGlobalFlags } from "./global-flags.js";
4
4
  //#region src/shell/help.ts
5
5
  function renderHelp(command, runtime) {
@@ -10,28 +10,39 @@ function renderHelp(command, runtime) {
10
10
  const visibleCommands = command.commands.filter((candidate) => candidate.name() !== "help" && !candidate.hidden);
11
11
  const visibleOptions = command.options.filter((candidate) => !candidate.hidden);
12
12
  if (visibleCommands.length > 0) lines.push(...renderCommandRows(rail, ui, visibleCommands));
13
- if (descriptor.longDescription) {
14
- lines.push(`${rail}`);
15
- const wrapped = wrapText(descriptor.longDescription, Math.max(ui.width - 3, 40));
16
- for (const line of wrapped) lines.push(`${rail} ${line}`);
17
- }
18
- if (visibleOptions.length > 0) {
19
- if (visibleCommands.length > 0) lines.push(`${rail}`);
20
- if (visibleCommands.length > 0 && visibleOptions.every((option) => COMPACT_GLOBAL_OPTION_FLAGS.includes(option.flags))) lines.push(`${rail} Global options:`);
21
- lines.push(...renderOptionRows(rail, ui, visibleOptions));
22
- }
23
- if (descriptor.examples && descriptor.examples.length > 0) {
24
- lines.push(`${rail}`);
25
- lines.push(`${rail} Examples:`);
26
- for (const example of descriptor.examples) lines.push(`${rail} $ ${example}`);
27
- }
28
- if (descriptor.docsPath) {
29
- lines.push(`${rail}`);
30
- lines.push(`${rail} ${ui.accent(padDisplay("Read more", 16))} ${ui.link(descriptor.docsPath)}`);
31
- }
13
+ lines.push(...renderLongDescription(rail, ui, descriptor.longDescription));
14
+ lines.push(...renderVisibleOptions(rail, ui, visibleCommands, visibleOptions));
15
+ lines.push(...renderExamples(rail, runtime, descriptor.examples));
16
+ lines.push(...renderDocsPath(rail, ui, descriptor.docsPath));
32
17
  lines.push("");
33
18
  return `${lines.join("\n")}`;
34
19
  }
20
+ function renderLongDescription(rail, ui, longDescription) {
21
+ if (!longDescription) return [];
22
+ return [`${rail}`, ...wrapText(longDescription, Math.max(ui.width - 3, 40)).map((line) => `${rail} ${line}`)];
23
+ }
24
+ function renderVisibleOptions(rail, ui, visibleCommands, visibleOptions) {
25
+ if (visibleOptions.length === 0) return [];
26
+ const lines = visibleCommands.length > 0 ? [`${rail}`] : [];
27
+ if (shouldLabelGlobalOptions(visibleCommands, visibleOptions)) lines.push(`${rail} Global options:`);
28
+ return [...lines, ...renderOptionRows(rail, ui, visibleOptions)];
29
+ }
30
+ function shouldLabelGlobalOptions(visibleCommands, visibleOptions) {
31
+ return visibleCommands.length > 0 && visibleOptions.every((option) => COMPACT_GLOBAL_OPTION_FLAGS.includes(option.flags));
32
+ }
33
+ function renderExamples(rail, runtime, examples) {
34
+ const resolvedExamples = typeof examples === "function" ? examples(runtime) : examples;
35
+ if (!resolvedExamples || resolvedExamples.length === 0) return [];
36
+ return [
37
+ `${rail}`,
38
+ `${rail} Examples:`,
39
+ ...resolvedExamples.map((example) => `${rail} $ ${example}`)
40
+ ];
41
+ }
42
+ function renderDocsPath(rail, ui, docsPath) {
43
+ if (!docsPath) return [];
44
+ return [`${rail}`, `${rail} ${ui.accent(padDisplay("Read more", 16))} ${ui.link(docsPath)}`];
45
+ }
35
46
  function renderCommandRows(rail, ui, commands) {
36
47
  return renderAlignedRows(rail, ui, commands.map((command) => {
37
48
  const descriptor = getDescriptorForCommand(command);
@@ -23,6 +23,15 @@ function cliErrorToJson(error) {
23
23
  docsUrl: error.docsUrl
24
24
  };
25
25
  }
26
+ function formatUnexpectedError(error, trace) {
27
+ const debug = error instanceof Error ? error.stack ?? error.message : String(error);
28
+ if (trace) return `${debug}\n`;
29
+ return [
30
+ `Unexpected CLI error: ${error instanceof Error && error.message ? error.message : String(error)}`,
31
+ "More: Re-run with --trace for deeper diagnostics",
32
+ ""
33
+ ].join("\n");
34
+ }
26
35
  function writeJsonError(output, command, error) {
27
36
  output.stdout.write(`${JSON.stringify({
28
37
  ok: false,
@@ -70,4 +79,4 @@ function writeHumanError(output, ui, error, options) {
70
79
  writeHumanLines(output, lines);
71
80
  }
72
81
  //#endregion
73
- export { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess };
82
+ export { cliErrorToJson, formatUnexpectedError, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess };