@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
@@ -0,0 +1,74 @@
1
+ import { formatShellCommand } from "../command-arguments.js";
2
+ import { formatDescriptorLabel } from "../shell/command-meta.js";
3
+ import { renderSummaryLine } from "../shell/ui.js";
4
+ //#region src/presenters/agent.ts
5
+ function renderAgentInstall(context, descriptor, result) {
6
+ const ui = context.ui;
7
+ const rail = ui.dim("│");
8
+ return [
9
+ renderSummaryLine(ui, "success", `${formatDescriptorLabel(descriptor)} → ${operationSummary(result)} Prisma skills.`),
10
+ "",
11
+ `${rail} ${ui.accent("skills:")} ${result.skills.status.replace("-", " ")}`,
12
+ `${rail} ${ui.dim(formatShellCommand(result.skills.command))}`
13
+ ];
14
+ }
15
+ function renderAgentStatus(context, descriptor, result) {
16
+ const ui = context.ui;
17
+ const rail = ui.dim("│");
18
+ return [
19
+ `${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim(`Checking ${result.statusScope} Prisma skills.`)}`,
20
+ "",
21
+ `${rail} ${ui.accent("skills:")} ${result.skillsInstalled ? "installed" : "not found"}`,
22
+ ...renderInstalledSkills(context, result.skills),
23
+ `${rail}`,
24
+ `${rail} ${ui.accent("source:")} ${formatStatusSource(result)}`,
25
+ `${rail} ${ui.dim(formatShellCommand(result.skillsListCommand))}`,
26
+ ...renderProjectStatusDetails(context, result)
27
+ ];
28
+ }
29
+ function serializeAgentInstall(result) {
30
+ return result;
31
+ }
32
+ function serializeAgentStatus(result) {
33
+ return result;
34
+ }
35
+ function formatSetupPromptStatus(result) {
36
+ if (result.skillsInstalled) return "not needed";
37
+ if (result.promptDismissedAt) return `dismissed ${result.promptDismissedAt}`;
38
+ return "active";
39
+ }
40
+ function formatStatusSource(result) {
41
+ if (result.statusSource === "skills-cli") return result.statusScope === "global" ? "skills list -g --json" : "skills list --json";
42
+ if (result.statusSource === "skills-lock") return result.skillsLockPath;
43
+ return "unavailable";
44
+ }
45
+ function renderProjectStatusDetails(context, result) {
46
+ if (result.statusScope !== "project") return [];
47
+ const ui = context.ui;
48
+ const rail = ui.dim("│");
49
+ return [
50
+ `${rail}`,
51
+ `${rail} ${ui.accent("skills lock:")} ${result.skillsLockInstalled ? "installed" : "not found"}`,
52
+ `${rail} ${ui.dim(result.skillsLockPath)}`,
53
+ `${rail}`,
54
+ `${rail} ${ui.accent("setup prompt:")} ${formatSetupPromptStatus(result)}`
55
+ ];
56
+ }
57
+ function renderInstalledSkills(context, skills) {
58
+ const ui = context.ui;
59
+ const rail = ui.dim("│");
60
+ if (skills.length === 0) return [`${rail} ${ui.dim("No Prisma skills reported.")}`];
61
+ return skills.map((skill) => {
62
+ const agents = skill.agents.length > 0 ? skill.agents.join(", ") : "no agents reported";
63
+ return `${rail} ${skill.name} ${ui.dim(`${skill.scope}; ${agents}`)}`;
64
+ });
65
+ }
66
+ function operationSummary(result) {
67
+ if (result.skills.status === "would-install") return "Would install";
68
+ return operationLabel(result.operation);
69
+ }
70
+ function operationLabel(operation) {
71
+ return operation === "update" ? "Updated" : "Installed";
72
+ }
73
+ //#endregion
74
+ export { renderAgentInstall, renderAgentStatus, serializeAgentInstall, serializeAgentStatus };
@@ -1,3 +1,4 @@
1
+ import { resolvePrismaCliPackageCommandSync } from "../lib/agent/cli-command.js";
1
2
  import { renderVerboseBlock } from "../shell/ui.js";
2
3
  import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
3
4
  import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
@@ -31,18 +32,43 @@ function serializeAppBuild(result) {
31
32
  function renderAppDeploy(context, descriptor, result, options) {
32
33
  const logsCommand = options?.logsTarget ? `prisma-cli app logs ${options.logsTarget}` : "prisma-cli app logs";
33
34
  return [
34
- `Live in ${formatDuration(result.durationMs)}`,
35
- ...result.deployment.url ? [context.ui.link(result.deployment.url)] : [],
35
+ ...result.promoted ? [`Live in ${formatDuration(result.durationMs)}`, ...result.deployment.url ? [context.ui.link(result.deployment.url)] : []] : [
36
+ `Built ${result.deployment.id} in ${formatDuration(result.durationMs)} (not promoted)`,
37
+ ...result.deployment.url ? [context.ui.link(result.deployment.url)] : [],
38
+ context.ui.dim("The live deployment is unchanged.")
39
+ ],
36
40
  ...renderBranchDatabaseDeploySummary(context, result),
37
41
  "",
38
- ...renderDeployOutputRows(context.ui, [{
39
- label: "Logs",
40
- value: logsCommand
41
- }]),
42
+ ...renderDeployOutputRows(context.ui, [
43
+ ...result.promoted ? [] : [{
44
+ label: "Promote",
45
+ value: `prisma-cli app promote ${result.deployment.id}`
46
+ }],
47
+ {
48
+ label: "Logs",
49
+ value: logsCommand
50
+ },
51
+ ...result.deploySettings.region && result.deploySettings.regionSource === null ? [{
52
+ label: "Region",
53
+ value: result.deploySettings.region,
54
+ origin: result.project.defaultRegion != null ? "project default" : "platform default — pass --region to choose"
55
+ }] : [],
56
+ ...deployUsedComputeConfig(result) ? [] : [{
57
+ label: "Config",
58
+ value: resolvePrismaCliPackageCommandSync(context.runtime.cwd, ["init"])
59
+ }]
60
+ ]),
42
61
  ...renderDeployResolvedContextBlock(context, result),
43
62
  ...renderDeploySettingsBlock(context, result)
44
63
  ];
45
64
  }
65
+ function deployUsedComputeConfig(result) {
66
+ return result.deploySettings.config.path !== null || [
67
+ result.deploySettings.framework.source,
68
+ result.deploySettings.buildCommand.source,
69
+ result.deploySettings.outputDirectory.source
70
+ ].some((source) => source?.includes("prisma.compute"));
71
+ }
46
72
  function isAppDeployAllResult(result) {
47
73
  return "deployments" in result;
48
74
  }
@@ -82,7 +108,7 @@ function serializeAppDeploy(result) {
82
108
  };
83
109
  }
84
110
  function renderBranchDatabaseDeploySummary(context, result) {
85
- if (!result.branchDatabase || result.branchDatabase.status !== "created") return [];
111
+ if (result.branchDatabase?.status !== "created") return [];
86
112
  return ["", ...renderDeployOutputRows(context.ui, [{
87
113
  label: "Database",
88
114
  value: result.branchDatabase.database?.name ?? "created"
@@ -2,6 +2,7 @@ import { formatDescriptorLabel } from "../shell/command-meta.js";
2
2
  import { padDisplay } from "../shell/ui.js";
3
3
  import { renderMutate, renderShow } from "../output/patterns.js";
4
4
  import stringWidth from "string-width";
5
+ import { SERVICE_TOKEN_ENV_VAR } from "@prisma/cli-engine";
5
6
  //#region src/presenters/auth.ts
6
7
  function renderAuthSuccess(context, descriptor, command, result) {
7
8
  if (command === "auth.login") {
@@ -25,7 +26,7 @@ function renderAuthSuccess(context, descriptor, command, result) {
25
26
  context: rows,
26
27
  operationDescription: "Applying authentication session changes",
27
28
  operationCount: 1,
28
- details: ["Session stored in local CLI state."]
29
+ details: ["Session stored in local CLI state.", ...result.agentSetupTip ? [`Install Prisma skills for this project with ${result.agentSetupTip.command}.`] : []]
29
30
  }, context.ui);
30
31
  }
31
32
  if (command === "auth.logout") return renderMutate({
@@ -158,7 +159,7 @@ function providerLabel(provider) {
158
159
  }
159
160
  function authSourceLabel(source) {
160
161
  if (source === "oauth") return "local OAuth";
161
- if (source === "service_token") return "PRISMA_SERVICE_TOKEN";
162
+ if (source === "service_token") return SERVICE_TOKEN_ENV_VAR;
162
163
  return "none";
163
164
  }
164
165
  function workspaceSourceLabel(source) {
@@ -0,0 +1,174 @@
1
+ import { formatDescriptorLabel } from "../shell/command-meta.js";
2
+ import { formatColumns, renderSummaryLine } from "../shell/ui.js";
3
+ import { renderMutate, serializeList } from "../output/patterns.js";
4
+ import { renderResolvedProjectContextBlock, stripVerboseContext } from "./verbose-context.js";
5
+ //#region src/presenters/bucket.ts
6
+ function renderBucketList(context, descriptor, result) {
7
+ const ui = context.ui;
8
+ const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing object-store buckets for the resolved project.")}`, ""];
9
+ const rail = ui.dim("│");
10
+ lines.push(`${rail} ${ui.accent("project:")} ${result.projectName}`);
11
+ if (result.branchName) lines.push(`${rail} ${ui.accent("branch:")} ${result.branchName}`);
12
+ lines.push(rail);
13
+ if (result.buckets.length === 0) {
14
+ lines.push(`${rail} ${ui.dim("No buckets found.")}`);
15
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
16
+ return lines;
17
+ }
18
+ const rows = result.buckets.map((bucket) => [
19
+ bucket.name,
20
+ bucket.id,
21
+ bucket.status,
22
+ bucket.branchId ?? "unscoped",
23
+ bucket.createdAt
24
+ ]);
25
+ const widths = [
26
+ Math.max(4, ...rows.map((row) => row[0].length)),
27
+ Math.max(2, ...rows.map((row) => row[1].length)),
28
+ Math.max(6, ...rows.map((row) => row[2].length)),
29
+ Math.max(6, ...rows.map((row) => row[3].length)),
30
+ Math.max(7, ...rows.map((row) => row[4].length))
31
+ ];
32
+ lines.push(`${rail} ${ui.accent(formatColumns([
33
+ "Name",
34
+ "Id",
35
+ "Status",
36
+ "Branch",
37
+ "Created"
38
+ ], widths))}`);
39
+ for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
40
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
41
+ return lines;
42
+ }
43
+ function serializeBucketList(result) {
44
+ return {
45
+ context: {
46
+ project: result.projectName,
47
+ ...result.branchName ? { branch: result.branchName } : {}
48
+ },
49
+ items: result.buckets.map((bucket) => ({
50
+ name: bucket.name,
51
+ id: bucket.id,
52
+ status: bucket.status
53
+ })),
54
+ count: result.buckets.length,
55
+ projectId: result.projectId,
56
+ branchName: result.branchName,
57
+ buckets: result.buckets
58
+ };
59
+ }
60
+ function renderBucketCreate(context, _descriptor, result) {
61
+ const ui = context.ui;
62
+ return ["Creating bucket...", renderSummaryLine(ui, "success", `Created bucket "${result.bucket.name}" in ${formatBucketTarget(result.projectName, result.bucket.branchId)}.`)];
63
+ }
64
+ function serializeBucketCreate(result) {
65
+ return stripVerboseContext(result);
66
+ }
67
+ function renderBucketDelete(context, descriptor, result) {
68
+ return renderMutate({
69
+ title: "Deleting object-store bucket.",
70
+ descriptor,
71
+ context: [{
72
+ key: "bucket",
73
+ value: result.bucket.id,
74
+ tone: "dim"
75
+ }],
76
+ operationDescription: "Deleting bucket",
77
+ operationCount: 1,
78
+ details: ["Bucket and all its access keys were removed."]
79
+ }, context.ui);
80
+ }
81
+ function serializeBucketDelete(result) {
82
+ return { bucket: result.bucket };
83
+ }
84
+ function renderBucketKeyList(context, descriptor, result) {
85
+ const ui = context.ui;
86
+ const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing access keys for bucket.")}`, ""];
87
+ const rail = ui.dim("│");
88
+ lines.push(`${rail} ${ui.accent("bucket:")} ${result.bucketId}`);
89
+ lines.push(rail);
90
+ if (result.keys.length === 0) {
91
+ lines.push(`${rail} ${ui.dim("No keys found.")}`);
92
+ return lines;
93
+ }
94
+ const rows = result.keys.map((key) => [
95
+ key.name,
96
+ key.id,
97
+ key.role,
98
+ key.valueHint,
99
+ key.createdAt
100
+ ]);
101
+ const widths = [
102
+ Math.max(4, ...rows.map((row) => row[0].length)),
103
+ Math.max(2, ...rows.map((row) => row[1].length)),
104
+ Math.max(4, ...rows.map((row) => row[2].length)),
105
+ Math.max(4, ...rows.map((row) => row[3].length)),
106
+ Math.max(7, ...rows.map((row) => row[4].length))
107
+ ];
108
+ lines.push(`${rail} ${ui.accent(formatColumns([
109
+ "Name",
110
+ "Id",
111
+ "Role",
112
+ "Hint",
113
+ "Created"
114
+ ], widths))}`);
115
+ for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
116
+ return lines;
117
+ }
118
+ function serializeBucketKeyList(result) {
119
+ return {
120
+ ...serializeList({
121
+ context: { bucket: result.bucketId },
122
+ items: result.keys.map((key) => ({
123
+ noun: "key",
124
+ label: key.name,
125
+ id: key.id,
126
+ status: null
127
+ }))
128
+ }),
129
+ bucketId: result.bucketId,
130
+ keys: result.keys
131
+ };
132
+ }
133
+ function renderBucketKeyCreateStdout(_context, _descriptor, result) {
134
+ return [
135
+ `S3_ENDPOINT=${result.endpoint}`,
136
+ `S3_ACCESS_KEY_ID=${result.accessKeyId}`,
137
+ `S3_SECRET_ACCESS_KEY=${result.secretAccessKey}`,
138
+ `S3_BUCKET=${result.bucketName}`
139
+ ];
140
+ }
141
+ function renderBucketKeyCreate(context, _descriptor, result) {
142
+ const ui = context.ui;
143
+ return [
144
+ "Creating bucket key...",
145
+ renderSummaryLine(ui, "success", `Created key "${result.key.name}" for bucket "${result.bucketName}".`),
146
+ " The credentials below are shown once — copy them now.",
147
+ " Set these environment variables to use this bucket:"
148
+ ];
149
+ }
150
+ function serializeBucketKeyCreate(result) {
151
+ return result;
152
+ }
153
+ function renderBucketKeyDelete(context, descriptor, result) {
154
+ return renderMutate({
155
+ title: "Deleting bucket access key.",
156
+ descriptor,
157
+ context: [{
158
+ key: "key",
159
+ value: result.key.id,
160
+ tone: "dim"
161
+ }],
162
+ operationDescription: "Deleting bucket key",
163
+ operationCount: 1,
164
+ details: ["The access key was revoked and removed."]
165
+ }, context.ui);
166
+ }
167
+ function serializeBucketKeyDelete(result) {
168
+ return { key: result.key };
169
+ }
170
+ function formatBucketTarget(projectName, branchId) {
171
+ return branchId ? `${projectName} / ${branchId}` : projectName;
172
+ }
173
+ //#endregion
174
+ export { renderBucketCreate, renderBucketDelete, renderBucketKeyCreate, renderBucketKeyCreateStdout, renderBucketKeyDelete, renderBucketKeyList, renderBucketList, serializeBucketCreate, serializeBucketDelete, serializeBucketKeyCreate, serializeBucketKeyDelete, serializeBucketKeyList, serializeBucketList };
@@ -17,7 +17,7 @@ function renderDatabaseList(context, descriptor, result) {
17
17
  }
18
18
  const rows = result.databases.map((database) => [
19
19
  database.name,
20
- database.branchName ?? "unscoped",
20
+ branchLabel(database),
21
21
  database.region ?? "unknown",
22
22
  formatStatus(database),
23
23
  database.id
@@ -79,8 +79,8 @@ function renderDatabaseShow(context, descriptor, result) {
79
79
  },
80
80
  {
81
81
  key: "branch",
82
- value: result.database.branchName ?? "unscoped",
83
- tone: result.database.branchName ? "default" : "dim"
82
+ value: branchLabel(result.database),
83
+ tone: result.database.branchId ? "default" : "dim"
84
84
  },
85
85
  {
86
86
  key: "region",
@@ -236,8 +236,196 @@ function renderDatabaseConnectionRemove(context, descriptor, result) {
236
236
  function serializeDatabaseConnectionRemove(result) {
237
237
  return { connection: result.connection };
238
238
  }
239
+ function renderDatabaseUsage(context, descriptor, result) {
240
+ const lines = renderShow({
241
+ title: "Showing database usage metrics.",
242
+ descriptor,
243
+ fields: [
244
+ {
245
+ key: "project",
246
+ value: result.projectName
247
+ },
248
+ {
249
+ key: "database",
250
+ value: result.database.name
251
+ },
252
+ {
253
+ key: "id",
254
+ value: result.database.id,
255
+ tone: "dim"
256
+ },
257
+ {
258
+ key: "period",
259
+ value: `${formatUsageTimestamp(result.period.start)} to ${formatUsageTimestamp(result.period.end)}`
260
+ },
261
+ {
262
+ key: "operations",
263
+ value: formatUsageMetric(result.metrics.operations)
264
+ },
265
+ {
266
+ key: "storage",
267
+ value: formatUsageMetric(result.metrics.storage)
268
+ },
269
+ {
270
+ key: "generated",
271
+ value: formatUsageTimestamp(result.generatedAt),
272
+ tone: "dim"
273
+ }
274
+ ]
275
+ }, context.ui);
276
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
277
+ return lines;
278
+ }
279
+ function serializeDatabaseUsage(result) {
280
+ return stripVerboseContext(result);
281
+ }
282
+ function renderDatabaseBackupList(context, descriptor, result) {
283
+ const ui = context.ui;
284
+ const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing platform-created database backups.")}`, ""];
285
+ const rail = ui.dim("│");
286
+ lines.push(`${rail} ${ui.accent("database:")} ${result.database.name}`);
287
+ if (result.retentionDays !== null) lines.push(`${rail} ${ui.accent("retention:")} ${result.retentionDays} days`);
288
+ lines.push(rail);
289
+ if (result.backups.length === 0) {
290
+ lines.push(`${rail} ${ui.dim("No backups found.")}`);
291
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
292
+ return lines;
293
+ }
294
+ const rows = result.backups.map((backup) => [
295
+ backup.id,
296
+ backup.backupType,
297
+ backup.status,
298
+ formatBackupSize(backup.size),
299
+ backup.createdAt
300
+ ]);
301
+ const widths = [
302
+ Math.max(2, ...rows.map((row) => row[0].length)),
303
+ Math.max(4, ...rows.map((row) => row[1].length)),
304
+ Math.max(6, ...rows.map((row) => row[2].length)),
305
+ Math.max(4, ...rows.map((row) => row[3].length)),
306
+ Math.max(7, ...rows.map((row) => row[4].length))
307
+ ];
308
+ lines.push(`${rail} ${ui.accent(formatColumns([
309
+ "Id",
310
+ "Type",
311
+ "Status",
312
+ "Size",
313
+ "Created"
314
+ ], widths))}`);
315
+ for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
316
+ if (result.hasMore) {
317
+ lines.push(rail);
318
+ lines.push(`${rail} ${ui.dim("More backups exist; raise --limit to see them.")}`);
319
+ }
320
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
321
+ return lines;
322
+ }
323
+ function serializeDatabaseBackupList(result) {
324
+ return {
325
+ ...serializeList({
326
+ context: {
327
+ project: result.projectName,
328
+ database: result.database.name
329
+ },
330
+ items: result.backups.map((backup) => ({
331
+ noun: "backup",
332
+ label: backup.id,
333
+ id: backup.id,
334
+ status: null
335
+ }))
336
+ }),
337
+ projectId: result.projectId,
338
+ database: result.database,
339
+ backups: result.backups,
340
+ retentionDays: result.retentionDays,
341
+ hasMore: result.hasMore
342
+ };
343
+ }
344
+ function renderDatabaseRestore(context, descriptor, result) {
345
+ const lines = renderMutate({
346
+ title: "Restoring database from backup.",
347
+ descriptor,
348
+ context: [
349
+ {
350
+ key: "project",
351
+ value: result.projectName
352
+ },
353
+ {
354
+ key: "database",
355
+ value: result.database.name
356
+ },
357
+ {
358
+ key: "id",
359
+ value: result.database.id,
360
+ tone: "dim"
361
+ },
362
+ {
363
+ key: "backup",
364
+ value: result.source.backupId
365
+ },
366
+ ...result.source.databaseId !== result.database.id ? [{
367
+ key: "source",
368
+ value: result.source.databaseId
369
+ }] : []
370
+ ],
371
+ operationDescription: "Restoring database",
372
+ operationCount: 1,
373
+ details: [`The restore is running; the database status is "${result.database.status ?? "recovering"}" until it completes.`, "Connections and credentials are preserved."]
374
+ }, context.ui);
375
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
376
+ return lines;
377
+ }
378
+ function serializeDatabaseRestore(result) {
379
+ return stripVerboseContext(result);
380
+ }
381
+ function renderDatabaseConnectionRotateStdout(_context, _descriptor, result) {
382
+ return [result.connectionString];
383
+ }
384
+ function renderDatabaseConnectionRotate(context, _descriptor, result) {
385
+ const ui = context.ui;
386
+ const lines = [
387
+ "Rotating connection...",
388
+ renderSummaryLine(ui, "success", `Rotated credentials for ${result.database ? `"${result.database.name}"` : `connection ${result.connection.id}`}. The previous credentials no longer work.`),
389
+ " The connection URL below is shown once, so save it now."
390
+ ];
391
+ if (ui.verbose) {
392
+ lines.push("");
393
+ lines.push(...renderDatabaseConnectionRotateVerboseRows(context, result));
394
+ }
395
+ return lines;
396
+ }
397
+ function serializeDatabaseConnectionRotate(result) {
398
+ return result;
399
+ }
400
+ function renderDatabaseConnectionRotateVerboseRows(context, result) {
401
+ return renderMetadataRows([...result.database ? [["database", formatResourceWithId(context, result.database.name, result.database.id)]] : [], ["connection", formatResourceWithId(context, result.connection.name, result.connection.id)]]);
402
+ }
403
+ function formatBackupSize(size) {
404
+ if (size === null) return "unknown";
405
+ if (size < 1024) return `${size} B`;
406
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KiB`;
407
+ if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MiB`;
408
+ return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GiB`;
409
+ }
410
+ function formatUsageTimestamp(value) {
411
+ return value || "unknown";
412
+ }
413
+ /** A metric the API did not report reads "unknown", and a measurement
414
+ * with no unit prints alone. See `v8/postgres/presentation.ts`. */
415
+ function formatUsageMetric(metric) {
416
+ if (metric.used === null) return "unknown";
417
+ return metric.unit ? `${metric.used} ${metric.unit}` : String(metric.used);
418
+ }
419
+ /** The status the API reported, or the word for none. `isDefault` is a
420
+ * different fact and never stands in for a status. */
239
421
  function formatStatus(database) {
240
- return database.status ?? (database.isDefault ? "default" : "unknown");
422
+ return database.status ?? "unknown";
423
+ }
424
+ /** "unscoped" is a claim that the database belongs to no branch, so only
425
+ * an absent `branchId` may produce it. The API does not always send a
426
+ * branch name beside the id it does send. */
427
+ function branchLabel(database) {
428
+ return database.branchName ?? database.branchId ?? "unscoped";
241
429
  }
242
430
  function formatDatabaseTarget(projectName, branchName) {
243
431
  return branchName ? `${projectName} / ${branchName}` : projectName;
@@ -271,4 +459,4 @@ function renderMetadataRows(rows) {
271
459
  return rows.map(([key, value]) => ` ${formatColumns([key, value], widths)}`);
272
460
  }
273
461
  //#endregion
274
- export { renderDatabaseConnectionCreate, renderDatabaseConnectionCreateStdout, renderDatabaseConnectionList, renderDatabaseConnectionRemove, renderDatabaseCreate, renderDatabaseCreateStdout, renderDatabaseList, renderDatabaseRemove, renderDatabaseShow, serializeDatabaseConnectionCreate, serializeDatabaseConnectionList, serializeDatabaseConnectionRemove, serializeDatabaseCreate, serializeDatabaseList, serializeDatabaseRemove, serializeDatabaseShow };
462
+ export { renderDatabaseBackupList, renderDatabaseConnectionCreate, renderDatabaseConnectionCreateStdout, renderDatabaseConnectionList, renderDatabaseConnectionRemove, renderDatabaseConnectionRotate, renderDatabaseConnectionRotateStdout, renderDatabaseCreate, renderDatabaseCreateStdout, renderDatabaseList, renderDatabaseRemove, renderDatabaseRestore, renderDatabaseShow, renderDatabaseUsage, serializeDatabaseBackupList, serializeDatabaseConnectionCreate, serializeDatabaseConnectionList, serializeDatabaseConnectionRemove, serializeDatabaseConnectionRotate, serializeDatabaseCreate, serializeDatabaseList, serializeDatabaseRemove, serializeDatabaseRestore, serializeDatabaseShow, serializeDatabaseUsage };
@@ -0,0 +1,26 @@
1
+ import { renderShow } from "../output/patterns.js";
2
+ //#region src/presenters/feedback.ts
3
+ function renderFeedbackSuccess(context, descriptor, result) {
4
+ return renderShow({
5
+ title: "Feedback sent. Thank you!",
6
+ descriptor,
7
+ fields: [
8
+ ...result.id ? [{
9
+ key: "id",
10
+ value: result.id
11
+ }] : [],
12
+ {
13
+ key: "sent as",
14
+ value: result.email ?? "anonymous",
15
+ tone: result.email ? "default" : "dim"
16
+ },
17
+ {
18
+ key: "included",
19
+ value: `CLI ${result.context.cliVersion}, node ${result.context.nodeVersion}, ${result.context.platform} ${result.context.arch}`,
20
+ tone: "dim"
21
+ }
22
+ ]
23
+ }, context.ui);
24
+ }
25
+ //#endregion
26
+ export { renderFeedbackSuccess };
@@ -0,0 +1,30 @@
1
+ import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
2
+ import { renderNextSteps, renderSummaryLine } from "../shell/ui.js";
3
+ import { COMPUTE_CONFIG_JSON_FILENAME } from "@prisma/compute-sdk/config";
4
+ //#region src/presenters/init.ts
5
+ function renderInit(context, _descriptor, result) {
6
+ const ui = context.ui;
7
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
8
+ const lines = [renderSummaryLine(ui, "success", result.converted ? `Converted ${COMPUTE_CONFIG_JSON_FILENAME} to ${result.configPath}` : `Wrote ${result.configPath}`)];
9
+ if (result.types.status === "installed") lines.push(renderSummaryLine(ui, "success", `Installed ${result.types.package} (config types)`));
10
+ else if ((result.types.status === "skipped" || result.types.status === "declined") && result.types.installCommand) lines.push(` ${ui.dim(`For editor types: ${result.types.installCommand}`)}`);
11
+ switch (result.link.status) {
12
+ case "linked":
13
+ lines.push(renderSummaryLine(ui, "success", `Linked "${result.directory}" to Project "${result.link.project?.name ?? ""}"`));
14
+ break;
15
+ case "already-linked": break;
16
+ case "skipped":
17
+ case "declined":
18
+ lines.push(` ${ui.dim(`Not linked to a Project yet; link with ${formatCommand(["project", "link"])}.`)}`);
19
+ break;
20
+ case "failed": break;
21
+ }
22
+ const linked = result.link.status === "linked" || result.link.status === "already-linked";
23
+ lines.push(...renderNextSteps([formatCommand(["app", "deploy"]), ...linked ? [] : [formatCommand(["project", "link"])]]));
24
+ return lines;
25
+ }
26
+ function serializeInit(result) {
27
+ return result;
28
+ }
29
+ //#endregion
30
+ export { renderInit, serializeInit };