@prisma/cli 3.0.0-dev.93.1 → 3.0.0-dev.97.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.
|
@@ -11,6 +11,7 @@ const EMPTY_AUTH_CONTEXT = {
|
|
|
11
11
|
activeWorkspaceId: null,
|
|
12
12
|
workspaces: {}
|
|
13
13
|
};
|
|
14
|
+
const UNKNOWN_WORKSPACE_NAME = "Unknown workspace";
|
|
14
15
|
function getAuthContextFilePath(authFilePath) {
|
|
15
16
|
const extension = path.extname(authFilePath);
|
|
16
17
|
if (!extension) return `${authFilePath}.context.json`;
|
|
@@ -140,7 +141,7 @@ var FileTokenStorage = class {
|
|
|
140
141
|
return credentials.map((credential) => storedCredentialToTokens(credential)).filter((tokens) => tokens !== null).map((tokens) => {
|
|
141
142
|
const cached = context.state.workspaces[tokens.workspaceId];
|
|
142
143
|
const id = typeof cached?.id === "string" && cached.id.trim().length > 0 ? cached.id.trim() : tokens.workspaceId;
|
|
143
|
-
const name = typeof cached?.name === "string" && cached.name.trim().length > 0 ? cached.name.trim() :
|
|
144
|
+
const name = typeof cached?.name === "string" && cached.name.trim().length > 0 ? workspaceDisplayName(cached.name.trim(), tokens.workspaceId) : UNKNOWN_WORKSPACE_NAME;
|
|
144
145
|
const lastSeenAt = typeof cached?.lastSeenAt === "string" && cached.lastSeenAt.trim().length > 0 ? cached.lastSeenAt.trim() : null;
|
|
145
146
|
return {
|
|
146
147
|
id,
|
|
@@ -151,6 +152,10 @@ var FileTokenStorage = class {
|
|
|
151
152
|
};
|
|
152
153
|
});
|
|
153
154
|
}
|
|
155
|
+
async listWorkspaceTokens() {
|
|
156
|
+
this.signal?.throwIfAborted();
|
|
157
|
+
return (await this.readCredentialsFromDisk()).map((credential) => storedCredentialToTokens(credential)).filter((tokens) => tokens !== null);
|
|
158
|
+
}
|
|
154
159
|
async useWorkspace(workspaceRef) {
|
|
155
160
|
return this.withRefreshLock(() => this.useWorkspaceUnlocked(workspaceRef));
|
|
156
161
|
}
|
|
@@ -406,5 +411,8 @@ function workspaceMatchesRef(workspace, ref) {
|
|
|
406
411
|
function stripWorkspacePrefix(value) {
|
|
407
412
|
return value.startsWith("wksp_") ? value.slice(5) : value;
|
|
408
413
|
}
|
|
414
|
+
function workspaceDisplayName(name, credentialWorkspaceId) {
|
|
415
|
+
return name === credentialWorkspaceId ? UNKNOWN_WORKSPACE_NAME : name;
|
|
416
|
+
}
|
|
409
417
|
//#endregion
|
|
410
418
|
export { FileTokenStorage, WorkspaceSelectionError };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { attachCommandDescriptor } from "../../shell/command-meta.js";
|
|
2
2
|
import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
|
|
3
3
|
import { configureRuntimeCommand } from "../../shell/runtime.js";
|
|
4
|
-
import { runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout,
|
|
4
|
+
import { runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceUse } from "../../controllers/auth.js";
|
|
5
5
|
import { runCommand } from "../../shell/command-runner.js";
|
|
6
6
|
import { renderAuthSuccess, renderAuthWorkspaceList, renderAuthWorkspaceLogout, renderAuthWorkspaceUse, serializeAuthWorkspaceList, serializeAuthWorkspaceLogout, serializeAuthWorkspaceUse } from "../../presenters/auth.js";
|
|
7
7
|
import { Command, Option } from "commander";
|
|
@@ -54,7 +54,6 @@ function createAuthWorkspaceCommand(runtime) {
|
|
|
54
54
|
addCompactGlobalFlags(workspace);
|
|
55
55
|
workspace.addCommand(createAuthWorkspaceListCommand(runtime));
|
|
56
56
|
workspace.addCommand(createAuthWorkspaceUseCommand(runtime));
|
|
57
|
-
workspace.addCommand(createAuthWorkspaceSelectCommand(runtime));
|
|
58
57
|
workspace.addCommand(createAuthWorkspaceLogoutCommand(runtime));
|
|
59
58
|
return workspace;
|
|
60
59
|
}
|
|
@@ -69,17 +68,6 @@ function createAuthWorkspaceListCommand(runtime) {
|
|
|
69
68
|
});
|
|
70
69
|
return command;
|
|
71
70
|
}
|
|
72
|
-
function createAuthWorkspaceSelectCommand(runtime) {
|
|
73
|
-
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("select"), runtime), "auth.workspace.select");
|
|
74
|
-
addGlobalFlags(command);
|
|
75
|
-
command.action(async (options) => {
|
|
76
|
-
await runCommand(runtime, "auth.workspace.select", options, (context) => runAuthWorkspaceSelect(context), {
|
|
77
|
-
renderHuman: (context, descriptor, result) => renderAuthWorkspaceUse(context, descriptor, result),
|
|
78
|
-
renderJson: (result) => serializeAuthWorkspaceUse(result)
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
return command;
|
|
82
|
-
}
|
|
83
71
|
function createAuthWorkspaceLogoutCommand(runtime) {
|
|
84
72
|
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("logout"), runtime), "auth.workspace.logout");
|
|
85
73
|
command.argument("<id-or-name>", "Workspace id or exact name");
|
|
@@ -94,7 +82,7 @@ function createAuthWorkspaceLogoutCommand(runtime) {
|
|
|
94
82
|
}
|
|
95
83
|
function createAuthWorkspaceUseCommand(runtime) {
|
|
96
84
|
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("use"), runtime), "auth.workspace.use");
|
|
97
|
-
command.argument("
|
|
85
|
+
command.argument("[id-or-name]", "Workspace id or exact name");
|
|
98
86
|
addGlobalFlags(command);
|
|
99
87
|
command.action(async (workspaceRef, options) => {
|
|
100
88
|
await runCommand(runtime, "auth.workspace.use", options, (context) => runAuthWorkspaceUse(context, typeof workspaceRef === "string" ? workspaceRef : void 0), {
|
package/dist/controllers/auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SERVICE_TOKEN_ENV_VAR } from "../lib/auth/client.js";
|
|
1
|
+
import { CLIENT_ID, SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
2
2
|
import { FileTokenStorage, WorkspaceSelectionError } from "../adapters/token-storage.js";
|
|
3
3
|
import { authRequiredError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceSwitchUnavailableError } from "../shell/errors.js";
|
|
4
4
|
import { canPrompt } from "../shell/runtime.js";
|
|
@@ -6,6 +6,7 @@ import { performLogin, performLogout, readAuthState } from "../lib/auth/auth-ops
|
|
|
6
6
|
import { createAuthUseCases } from "../use-cases/auth.js";
|
|
7
7
|
import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
|
|
8
8
|
import { createSelectPromptPort } from "./select-prompt-port.js";
|
|
9
|
+
import { createManagementApiSdk } from "@prisma/management-api-sdk";
|
|
9
10
|
//#region src/controllers/auth.ts
|
|
10
11
|
function isRealMode(context) {
|
|
11
12
|
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
@@ -42,20 +43,15 @@ async function runAuthWorkspaceList(context) {
|
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
async function runAuthWorkspaceUse(context, workspaceRef) {
|
|
45
|
-
|
|
46
|
+
const trimmedWorkspaceRef = workspaceRef?.trim();
|
|
47
|
+
const selectedWorkspaceRef = trimmedWorkspaceRef ? trimmedWorkspaceRef : await selectWorkspaceSession(context);
|
|
46
48
|
return {
|
|
47
49
|
command: "auth.workspace.use",
|
|
48
|
-
result: isRealMode(context) ? await useRealAuthWorkspace(context,
|
|
50
|
+
result: isRealMode(context) ? await useRealAuthWorkspace(context, selectedWorkspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).useWorkspace(selectedWorkspaceRef),
|
|
49
51
|
warnings: [],
|
|
50
52
|
nextSteps: ["prisma-cli auth whoami", "prisma-cli project list"]
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
|
-
async function runAuthWorkspaceSelect(context) {
|
|
54
|
-
return {
|
|
55
|
-
...await runAuthWorkspaceUse(context, await selectWorkspaceSession(context)),
|
|
56
|
-
command: "auth.workspace.select"
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
55
|
async function runAuthWorkspaceLogout(context, workspaceRef) {
|
|
60
56
|
if (!workspaceRef?.trim()) throw usageError("Workspace required", "auth workspace logout needs a workspace id or cached workspace name.", "Pass a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
|
|
61
57
|
const result = isRealMode(context) ? await logoutRealAuthWorkspace(context, workspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).logoutWorkspace(workspaceRef);
|
|
@@ -82,7 +78,8 @@ async function requireAuthenticatedAuthState(context) {
|
|
|
82
78
|
}
|
|
83
79
|
async function listRealAuthWorkspaces(context) {
|
|
84
80
|
const rawServiceToken = context.runtime.env[SERVICE_TOKEN_ENV_VAR];
|
|
85
|
-
const
|
|
81
|
+
const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
|
|
82
|
+
const localWorkspaces = await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
|
|
86
83
|
if (rawServiceToken !== void 0) {
|
|
87
84
|
const authState = await readAuthState(context.runtime.env, context.runtime.signal);
|
|
88
85
|
return {
|
|
@@ -122,6 +119,7 @@ async function listRealAuthWorkspaces(context) {
|
|
|
122
119
|
async function useRealAuthWorkspace(context, workspaceRef) {
|
|
123
120
|
if (context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
|
|
124
121
|
const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
|
|
122
|
+
await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
|
|
125
123
|
try {
|
|
126
124
|
const result = await storage.useWorkspace(workspaceRef);
|
|
127
125
|
return {
|
|
@@ -142,6 +140,7 @@ async function useRealAuthWorkspace(context, workspaceRef) {
|
|
|
142
140
|
}
|
|
143
141
|
async function logoutRealAuthWorkspace(context, workspaceRef) {
|
|
144
142
|
const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
|
|
143
|
+
await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
|
|
145
144
|
try {
|
|
146
145
|
const result = await storage.logoutWorkspace(workspaceRef);
|
|
147
146
|
return {
|
|
@@ -167,7 +166,7 @@ async function selectWorkspaceSession(context) {
|
|
|
167
166
|
const workspaces = (realMode ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces()).workspaces.filter((workspace) => workspace.switchable);
|
|
168
167
|
if (workspaces.length === 0) throw usageError("No authenticated workspaces", "There are no local OAuth workspace sessions to select.", "Run prisma-cli auth login and authorize a workspace.", ["prisma-cli auth login"], "auth");
|
|
169
168
|
if (workspaces.length === 1) return workspaces[0].id;
|
|
170
|
-
if (!canPrompt(context)) throw usageError("Interactive workspace selection unavailable", "auth workspace
|
|
169
|
+
if (!canPrompt(context)) throw usageError("Interactive workspace selection unavailable", "auth workspace use needs an interactive terminal when no workspace is provided and more than one workspace is available.", "Run prisma-cli auth workspace use <id-or-name> with a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
|
|
171
170
|
return (await createSelectPromptPort(context).select({
|
|
172
171
|
message: "Select a workspace",
|
|
173
172
|
choices: workspaces.map((workspace) => ({
|
|
@@ -176,6 +175,78 @@ async function selectWorkspaceSession(context) {
|
|
|
176
175
|
}))
|
|
177
176
|
})).id;
|
|
178
177
|
}
|
|
178
|
+
async function hydrateLocalAuthWorkspaces(context, storage, workspaces) {
|
|
179
|
+
const candidates = workspaces.filter(needsWorkspaceMetadataHydration);
|
|
180
|
+
if (candidates.length === 0) return workspaces;
|
|
181
|
+
const tokensByCredentialWorkspaceId = new Map((await storage.listWorkspaceTokens()).map((tokens) => [tokens.workspaceId, tokens]));
|
|
182
|
+
let nextWorkspaces = workspaces;
|
|
183
|
+
for (const workspace of candidates) {
|
|
184
|
+
const tokens = tokensByCredentialWorkspaceId.get(workspace.credentialWorkspaceId);
|
|
185
|
+
if (!tokens) continue;
|
|
186
|
+
const resolved = await resolveOAuthWorkspaceMetadata(context, tokens);
|
|
187
|
+
if (!resolved) continue;
|
|
188
|
+
await rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved);
|
|
189
|
+
nextWorkspaces = nextWorkspaces.map((candidate) => candidate.credentialWorkspaceId === workspace.credentialWorkspaceId ? {
|
|
190
|
+
...candidate,
|
|
191
|
+
id: resolved.id,
|
|
192
|
+
name: resolved.name,
|
|
193
|
+
lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
194
|
+
} : candidate);
|
|
195
|
+
}
|
|
196
|
+
return nextWorkspaces;
|
|
197
|
+
}
|
|
198
|
+
async function rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved) {
|
|
199
|
+
try {
|
|
200
|
+
await storage.rememberWorkspace(tokens.workspaceId, resolved);
|
|
201
|
+
} catch {
|
|
202
|
+
context.runtime.signal?.throwIfAborted();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function needsWorkspaceMetadataHydration(workspace) {
|
|
206
|
+
return workspace.id === workspace.credentialWorkspaceId || workspace.name === "Unknown workspace" || workspace.name === workspace.credentialWorkspaceId;
|
|
207
|
+
}
|
|
208
|
+
async function resolveOAuthWorkspaceMetadata(context, tokens) {
|
|
209
|
+
const sdk = createManagementApiSdk({
|
|
210
|
+
clientId: CLIENT_ID,
|
|
211
|
+
redirectUri: "http://localhost:0/auth/callback",
|
|
212
|
+
tokenStorage: createSingleWorkspaceTokenStorage(new FileTokenStorage(context.runtime.env, context.runtime.signal, { activateOnSetTokens: false }), tokens),
|
|
213
|
+
apiBaseUrl: getApiBaseUrl(context.runtime.env)
|
|
214
|
+
});
|
|
215
|
+
try {
|
|
216
|
+
const { data } = await sdk.client.GET("/v1/workspaces/{id}", {
|
|
217
|
+
params: { path: { id: tokens.workspaceId } },
|
|
218
|
+
signal: context.runtime.signal
|
|
219
|
+
});
|
|
220
|
+
const id = stringOrNull(data?.data?.id) ?? tokens.workspaceId;
|
|
221
|
+
const name = stringOrNull(data?.data?.name) ?? id;
|
|
222
|
+
if (id === tokens.workspaceId && name === tokens.workspaceId) return null;
|
|
223
|
+
return {
|
|
224
|
+
id,
|
|
225
|
+
name
|
|
226
|
+
};
|
|
227
|
+
} catch {
|
|
228
|
+
context.runtime.signal?.throwIfAborted();
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function createSingleWorkspaceTokenStorage(storage, initialTokens) {
|
|
233
|
+
let currentTokens = initialTokens;
|
|
234
|
+
return {
|
|
235
|
+
getTokens: async () => currentTokens,
|
|
236
|
+
setTokens: async (tokens) => {
|
|
237
|
+
currentTokens = tokens;
|
|
238
|
+
await storage.setTokens(tokens);
|
|
239
|
+
},
|
|
240
|
+
clearTokens: async () => {
|
|
241
|
+
const tokens = currentTokens;
|
|
242
|
+
currentTokens = null;
|
|
243
|
+
if (tokens) await storage.clearTokensIfCurrent(tokens);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function stringOrNull(value) {
|
|
248
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
249
|
+
}
|
|
179
250
|
function toAuthWorkspace(workspace) {
|
|
180
251
|
return {
|
|
181
252
|
id: workspace.id,
|
|
@@ -242,4 +313,4 @@ function createAuthSuccess(command, result, nextSteps) {
|
|
|
242
313
|
};
|
|
243
314
|
}
|
|
244
315
|
//#endregion
|
|
245
|
-
export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout,
|
|
316
|
+
export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceUse };
|
|
@@ -60,7 +60,7 @@ const DESCRIPTORS = [
|
|
|
60
60
|
description: "Manage local authenticated workspaces",
|
|
61
61
|
examples: [
|
|
62
62
|
"prisma-cli auth workspace list",
|
|
63
|
-
"prisma-cli auth workspace
|
|
63
|
+
"prisma-cli auth workspace use",
|
|
64
64
|
"prisma-cli auth workspace use wksp_123",
|
|
65
65
|
"prisma-cli auth workspace logout wksp_123"
|
|
66
66
|
]
|
|
@@ -85,18 +85,11 @@ const DESCRIPTORS = [
|
|
|
85
85
|
"use"
|
|
86
86
|
],
|
|
87
87
|
description: "Switch the local CLI workspace",
|
|
88
|
-
examples: [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"prisma",
|
|
94
|
-
"auth",
|
|
95
|
-
"workspace",
|
|
96
|
-
"select"
|
|
97
|
-
],
|
|
98
|
-
description: "Interactively select the local CLI workspace",
|
|
99
|
-
examples: ["prisma-cli auth workspace select"]
|
|
88
|
+
examples: [
|
|
89
|
+
"prisma-cli auth workspace use",
|
|
90
|
+
"prisma-cli auth workspace use wksp_123",
|
|
91
|
+
"prisma-cli auth workspace use \"Acme Inc\""
|
|
92
|
+
]
|
|
100
93
|
},
|
|
101
94
|
{
|
|
102
95
|
id: "auth.workspace.logout",
|