@prisma/cli 3.0.0-beta.17 → 3.0.0-beta.19
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.
- package/dist/adapters/local-state.js +14 -3
- package/dist/adapters/token-storage.js +1 -1
- package/dist/cli2.js +6 -2
- package/dist/commands/agent/index.js +60 -0
- package/dist/commands/app/index.js +5 -3
- package/dist/commands/auth/index.js +1 -1
- package/dist/commands/build/index.js +29 -0
- package/dist/commands/git/index.js +1 -1
- package/dist/commands/project/index.js +1 -1
- package/dist/controllers/agent.js +228 -0
- package/dist/controllers/app.js +157 -73
- package/dist/controllers/auth.js +38 -3
- package/dist/controllers/build.js +88 -0
- package/dist/controllers/project.js +2 -2
- package/dist/controllers/select-prompt-port.js +1 -0
- package/dist/lib/agent/cli-command.js +17 -0
- package/dist/lib/agent/constants.js +12 -0
- package/dist/lib/agent/package-manager.js +99 -0
- package/dist/lib/agent/setup-status.js +83 -0
- package/dist/lib/app/app-provider.js +3 -3
- package/dist/lib/app/branch-database-deploy.js +3 -2
- package/dist/lib/app/branch-database.js +2 -1
- package/dist/lib/app/build-settings.js +1 -1
- package/dist/lib/app/build.js +2 -2
- package/dist/lib/app/bun-project.js +1 -1
- package/dist/lib/app/compute-config.js +27 -29
- package/dist/lib/app/deploy-plan.js +1 -0
- package/dist/lib/app/env-file.js +1 -1
- package/dist/lib/app/local-dev.js +1 -1
- package/dist/lib/app/production-deploy-gate.js +2 -1
- package/dist/lib/auth/login.js +1 -1
- package/dist/lib/git/local-branch.js +1 -1
- package/dist/lib/project/interactive-setup.js +1 -0
- package/dist/lib/project/local-pin.js +1 -1
- package/dist/lib/project/resolution.js +2 -2
- package/dist/lib/project/setup.js +1 -1
- package/dist/presenters/agent.js +74 -0
- package/dist/presenters/auth.js +1 -1
- package/dist/presenters/project.js +1 -1
- package/dist/shell/cli-command.js +12 -0
- package/dist/shell/command-arguments.js +7 -1
- package/dist/shell/command-meta.js +88 -0
- package/dist/shell/command-runner.js +3 -3
- package/dist/shell/help.js +6 -5
- package/dist/shell/prompt.js +7 -3
- package/dist/shell/runtime.js +2 -2
- package/dist/shell/update-check.js +2 -2
- package/package.json +4 -3
|
@@ -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 { resolvePackageRunner, resolvePackageRunnerSync, resolveSkillsPackageRunner };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { PRISMA_SKILLS_LOCK_FILENAME } from "./constants.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readFile, stat } from "node:fs/promises";
|
|
4
|
+
import { findComputeConfigDir } from "@prisma/compute-sdk/config";
|
|
5
|
+
//#region src/lib/agent/setup-status.ts
|
|
6
|
+
async function readPrismaAgentSetupStatus(options) {
|
|
7
|
+
const setupCwd = await resolvePrismaAgentSetupCwd(options);
|
|
8
|
+
const skillsLockPath = path.join(setupCwd, PRISMA_SKILLS_LOCK_FILENAME);
|
|
9
|
+
const [skillsInstalled, promptDismissedAt] = await Promise.all([hasPrismaSkillsLock(skillsLockPath, options.signal, options.requiredSkill), options.stateStore?.readAgentSetupPromptDismissedAt() ?? null]);
|
|
10
|
+
return {
|
|
11
|
+
skillsLockPath: path.basename(skillsLockPath),
|
|
12
|
+
skillsInstalled,
|
|
13
|
+
promptDismissedAt
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
async function resolvePrismaAgentSetupCwd(options) {
|
|
17
|
+
options.signal.throwIfAborted();
|
|
18
|
+
const configDir = await findComputeConfigDir(options.cwd, options.signal);
|
|
19
|
+
options.signal.throwIfAborted();
|
|
20
|
+
return configDir ?? options.cwd;
|
|
21
|
+
}
|
|
22
|
+
function isPrismaAgentSetupComplete(status) {
|
|
23
|
+
return status.skillsInstalled;
|
|
24
|
+
}
|
|
25
|
+
function shouldOfferPrismaAgentSetup(status) {
|
|
26
|
+
return !isPrismaAgentSetupComplete(status) && !status.promptDismissedAt;
|
|
27
|
+
}
|
|
28
|
+
async function isLikelyProjectDirectory(options) {
|
|
29
|
+
return (await Promise.all([
|
|
30
|
+
"package.json",
|
|
31
|
+
"prisma.compute.ts",
|
|
32
|
+
"prisma.config.ts",
|
|
33
|
+
".git"
|
|
34
|
+
].map((fileName) => pathExists(path.join(options.cwd, fileName), options.signal)))).some(Boolean);
|
|
35
|
+
}
|
|
36
|
+
async function hasPrismaSkillsLock(filePath, signal, requiredSkill) {
|
|
37
|
+
try {
|
|
38
|
+
const raw = await readFile(filePath, {
|
|
39
|
+
encoding: "utf8",
|
|
40
|
+
signal
|
|
41
|
+
});
|
|
42
|
+
return hasPrismaSkillsLockEntry(JSON.parse(raw), requiredSkill);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (isNotFoundError(error) || error instanceof SyntaxError) return false;
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function hasPrismaSkillsLockEntry(value, requiredSkill) {
|
|
49
|
+
if (!isRecord(value)) return false;
|
|
50
|
+
if (requiredSkill) return skillLockEntryUsesPrismaSource(readSkillLockEntries(value)[requiredSkill]);
|
|
51
|
+
if (readLegacySources(value).includes("prisma/skills")) return true;
|
|
52
|
+
return Object.values(readSkillLockEntries(value)).some(skillLockEntryUsesPrismaSource);
|
|
53
|
+
}
|
|
54
|
+
function readLegacySources(value) {
|
|
55
|
+
return Array.isArray(value.sources) ? value.sources.filter((source) => typeof source === "string") : [];
|
|
56
|
+
}
|
|
57
|
+
function readSkillLockEntries(value) {
|
|
58
|
+
return isRecord(value.skills) ? value.skills : {};
|
|
59
|
+
}
|
|
60
|
+
function skillLockEntryUsesPrismaSource(value) {
|
|
61
|
+
return isRecord(value) && value.source === "prisma/skills";
|
|
62
|
+
}
|
|
63
|
+
function isRecord(value) {
|
|
64
|
+
return typeof value === "object" && value !== null;
|
|
65
|
+
}
|
|
66
|
+
async function pathExists(filePath, signal) {
|
|
67
|
+
signal.throwIfAborted();
|
|
68
|
+
try {
|
|
69
|
+
await stat(filePath);
|
|
70
|
+
signal.throwIfAborted();
|
|
71
|
+
return true;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (signal.aborted) throw error;
|
|
74
|
+
if (isNotFoundError(error)) return false;
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function isNotFoundError(error) {
|
|
79
|
+
const code = error.code;
|
|
80
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { isLikelyProjectDirectory, readPrismaAgentSetupStatus, resolvePrismaAgentSetupCwd, shouldOfferPrismaAgentSetup };
|
|
@@ -404,9 +404,9 @@ async function listComputeServices(client, options) {
|
|
|
404
404
|
liveUrl: toAbsoluteUrl(service.appEndpointDomain ?? null)
|
|
405
405
|
}));
|
|
406
406
|
}
|
|
407
|
-
async function listComputeServiceDomains(client,
|
|
407
|
+
async function listComputeServiceDomains(client, appId, signal) {
|
|
408
408
|
const result = await client.GET("/v1/apps/{appId}/domains", {
|
|
409
|
-
params: { path: { appId
|
|
409
|
+
params: { path: { appId } },
|
|
410
410
|
signal
|
|
411
411
|
});
|
|
412
412
|
if (result.error || !result.data) throw domainApiCallError("Failed to list custom domains", result.response, result.error);
|
|
@@ -418,7 +418,7 @@ function normalizeDomainRecord(domain) {
|
|
|
418
418
|
type: domain.type,
|
|
419
419
|
url: domain.url,
|
|
420
420
|
hostname: domain.hostname,
|
|
421
|
-
|
|
421
|
+
appId: domain.appId,
|
|
422
422
|
status: domain.status,
|
|
423
423
|
foundryStatus: domain.foundryStatus,
|
|
424
424
|
failureReason: domain.failureReason,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { formatCommandArgument } from "../../shell/command-arguments.js";
|
|
1
2
|
import { CliError, usageError } from "../../shell/errors.js";
|
|
2
|
-
import { confirmPrompt } from "../../shell/prompt.js";
|
|
3
3
|
import { renderSummaryLine } from "../../shell/ui.js";
|
|
4
4
|
import { canPrompt } from "../../shell/runtime.js";
|
|
5
|
-
import {
|
|
5
|
+
import { confirmPrompt } from "../../shell/prompt.js";
|
|
6
6
|
import "../project/setup.js";
|
|
7
7
|
import { hasBranchDatabaseSignal, inspectBranchDatabaseSignal } from "./branch-database.js";
|
|
8
8
|
import path from "node:path";
|
|
@@ -51,6 +51,7 @@ async function maybeSetupBranchDatabase(context, provider, projectId, branch, op
|
|
|
51
51
|
if (!await confirmPrompt({
|
|
52
52
|
input: context.runtime.stdin,
|
|
53
53
|
output: context.output.stderr,
|
|
54
|
+
signal: context.runtime.signal,
|
|
54
55
|
message: databasePromptMessage(branch),
|
|
55
56
|
initialValue: false
|
|
56
57
|
})) return emptyBranchDatabaseSetupOutcome();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { access, readFile, readdir, stat } from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
import { access, readFile, readdir, stat } from "node:fs/promises";
|
|
3
3
|
//#region src/lib/app/branch-database.ts
|
|
4
4
|
const SKIPPED_DIRECTORIES = new Set([
|
|
5
5
|
".git",
|
|
6
|
+
".agents",
|
|
6
7
|
".next",
|
|
7
8
|
".nuxt",
|
|
8
9
|
".output",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { resolveBuildSettings, resolveConfiguredBuildSettings } from "@prisma/compute-sdk";
|
|
4
4
|
//#region src/lib/app/build-settings.ts
|
|
5
5
|
/** Legacy build-settings file: no longer read or written, only detected for migration. */
|
package/dist/lib/app/build.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./build-settings.js";
|
|
2
|
-
import { FRAMEWORKS } from "@prisma/compute-sdk/config";
|
|
3
|
-
import "node:fs/promises";
|
|
4
2
|
import "node:path";
|
|
3
|
+
import "node:fs/promises";
|
|
4
|
+
import { FRAMEWORKS } from "@prisma/compute-sdk/config";
|
|
5
5
|
import { normalizeArtifactSymlinks, resolveBuildStrategy } from "@prisma/compute-sdk";
|
|
6
6
|
//#region src/lib/app/build.ts
|
|
7
7
|
const RESOLVED_APP_BUILD_TYPES = [...frameworkBuildTypesByLastOccurrence()];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { access, readFile } from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
import { access, readFile } from "node:fs/promises";
|
|
3
3
|
//#region src/lib/app/bun-project.ts
|
|
4
4
|
async function readBunPackageJson(appPath, signal) {
|
|
5
5
|
const packageJsonPath = path.join(appPath, "package.json");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError } from "../../shell/errors.js";
|
|
2
|
-
import { COMPUTE_CONFIG_FILENAME, COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeTargetAppDir, frameworkByKey, inferComputeTargetFromCwd, loadComputeConfig, selectComputeDeployTarget } from "@prisma/compute-sdk/config";
|
|
3
2
|
import path from "node:path";
|
|
3
|
+
import { COMPUTE_CONFIG_FILENAME, COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeTargetAppDir, frameworkByKey, inferComputeTargetFromCwd, loadComputeConfig, selectComputeDeployTarget } from "@prisma/compute-sdk/config";
|
|
4
4
|
import { matchError } from "better-result";
|
|
5
5
|
//#region src/lib/app/compute-config.ts
|
|
6
6
|
/**
|
|
@@ -18,47 +18,45 @@ function computeFrameworkToBuildType(framework) {
|
|
|
18
18
|
function mergeComputeDeployInputs(options) {
|
|
19
19
|
const { cli, target, configFilename } = options;
|
|
20
20
|
const configAnnotation = `set by ${configFilename}`;
|
|
21
|
-
const framework = cli.framework
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
value: target.framework,
|
|
26
|
-
annotation: configAnnotation
|
|
27
|
-
} : void 0;
|
|
28
|
-
const entrypoint = cli.entrypoint ? {
|
|
29
|
-
value: cli.entrypoint,
|
|
30
|
-
annotation: "set by --entry"
|
|
31
|
-
} : target?.entry ? {
|
|
32
|
-
value: target.entry,
|
|
33
|
-
annotation: configAnnotation
|
|
34
|
-
} : void 0;
|
|
35
|
-
const httpPort = cli.httpPort ? {
|
|
36
|
-
value: cli.httpPort,
|
|
37
|
-
annotation: "set by --http-port"
|
|
38
|
-
} : target?.httpPort ? {
|
|
39
|
-
value: String(target.httpPort),
|
|
40
|
-
annotation: configAnnotation
|
|
41
|
-
} : void 0;
|
|
21
|
+
const framework = mergeStringInput(cli.framework, "set by --framework", target?.framework ?? void 0, configAnnotation);
|
|
22
|
+
const entrypoint = mergeStringInput(cli.entrypoint, "set by --entry", target?.entry ?? void 0, configAnnotation);
|
|
23
|
+
const httpPort = mergeStringInput(cli.httpPort, "set by --http-port", target?.httpPort ? String(target.httpPort) : void 0, configAnnotation);
|
|
24
|
+
const region = mergeStringInput(cli.region, "set by --region", target?.region ?? void 0, configAnnotation);
|
|
42
25
|
const cliEnvInputs = cli.envInputs && cli.envInputs.length > 0 ? cli.envInputs : void 0;
|
|
43
26
|
const configEnvInputs = target && target.envInputs.length > 0 ? target.envInputs : void 0;
|
|
44
27
|
const envInputs = cliEnvInputs ?? configEnvInputs;
|
|
45
|
-
const configAppName = target
|
|
46
|
-
value: target.name,
|
|
47
|
-
annotation: configAnnotation
|
|
48
|
-
} : target?.key ? {
|
|
49
|
-
value: target.key,
|
|
50
|
-
annotation: configAnnotation
|
|
51
|
-
} : void 0;
|
|
28
|
+
const configAppName = readConfigAppName(target, configAnnotation);
|
|
52
29
|
return {
|
|
53
30
|
framework,
|
|
54
31
|
entrypoint,
|
|
55
32
|
httpPort,
|
|
33
|
+
region,
|
|
56
34
|
envInputs,
|
|
57
35
|
envInputsFromConfig: !cliEnvInputs && configEnvInputs !== void 0,
|
|
58
36
|
configAppName,
|
|
59
37
|
appRoot: target?.root ?? void 0
|
|
60
38
|
};
|
|
61
39
|
}
|
|
40
|
+
function mergeStringInput(cliValue, cliAnnotation, configValue, configAnnotation) {
|
|
41
|
+
if (cliValue !== void 0) return {
|
|
42
|
+
value: cliValue,
|
|
43
|
+
annotation: cliAnnotation
|
|
44
|
+
};
|
|
45
|
+
if (configValue) return {
|
|
46
|
+
value: configValue,
|
|
47
|
+
annotation: configAnnotation
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function readConfigAppName(target, configAnnotation) {
|
|
51
|
+
if (target?.name) return {
|
|
52
|
+
value: target.name,
|
|
53
|
+
annotation: configAnnotation
|
|
54
|
+
};
|
|
55
|
+
if (target?.key) return {
|
|
56
|
+
value: target.key,
|
|
57
|
+
annotation: configAnnotation
|
|
58
|
+
};
|
|
59
|
+
}
|
|
62
60
|
/**
|
|
63
61
|
* Merges CLI inputs for the local `app build` and `app run` commands with a
|
|
64
62
|
* selected config target. Explicit flags win; `--build-type auto` is the
|
|
@@ -30,6 +30,7 @@ function perAppInputsForDeployAll(inputs) {
|
|
|
30
30
|
["--framework", inputs.framework],
|
|
31
31
|
["--entry", inputs.entrypoint],
|
|
32
32
|
["--http-port", inputs.httpPort],
|
|
33
|
+
["--region", inputs.region],
|
|
33
34
|
["--env", inputs.envAssignments?.length ? inputs.envAssignments : void 0],
|
|
34
35
|
[inputs.appIdEnvVar.name, inputs.appIdEnvVar.value]
|
|
35
36
|
].filter(([, value]) => value !== void 0).map(([flag]) => flag);
|
package/dist/lib/app/env-file.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { usageError } from "../../shell/errors.js";
|
|
2
2
|
import { validateKey } from "./env-config.js";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
5
|
import { parse } from "dotenv";
|
|
6
6
|
//#region src/lib/app/env-file.ts
|
|
7
7
|
const ASSIGNMENT_KEY_PATTERN = /^\s*(?:export\s+)?([^#=\s]+)\s*=/;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError } from "../../shell/errors.js";
|
|
2
|
-
import { confirmPrompt } from "../../shell/prompt.js";
|
|
3
2
|
import { canPrompt } from "../../shell/runtime.js";
|
|
3
|
+
import { confirmPrompt } from "../../shell/prompt.js";
|
|
4
4
|
//#region src/lib/app/production-deploy-gate.ts
|
|
5
5
|
async function enforceProductionDeployGate(context, provider, options) {
|
|
6
6
|
if (options.branchKind !== "production") return { firstProductionDeploy: false };
|
|
@@ -25,6 +25,7 @@ async function enforceProductionDeployGate(context, provider, options) {
|
|
|
25
25
|
if (!await confirmPrompt({
|
|
26
26
|
input: context.runtime.stdin,
|
|
27
27
|
output: context.output.stderr,
|
|
28
|
+
signal: context.runtime.signal,
|
|
28
29
|
message: "Deploy to production?",
|
|
29
30
|
initialValue: false
|
|
30
31
|
})) throw productionDeployCancelledError();
|
package/dist/lib/auth/login.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getApiBaseUrl } from "./client.js";
|
|
2
2
|
import { FileTokenStorage } from "../../adapters/token-storage.js";
|
|
3
|
-
import open from "open";
|
|
4
3
|
import { AuthError, createManagementApiSdk } from "@prisma/management-api-sdk";
|
|
4
|
+
import open from "open";
|
|
5
5
|
import events from "node:events";
|
|
6
6
|
import http from "node:http";
|
|
7
7
|
import readline from "node:readline/promises";
|
|
@@ -40,6 +40,7 @@ async function promptForProjectSetupChoice(options) {
|
|
|
40
40
|
const rawName = await textPrompt({
|
|
41
41
|
input: options.context.runtime.stdin,
|
|
42
42
|
output: options.context.runtime.stderr,
|
|
43
|
+
signal: options.context.runtime.signal,
|
|
43
44
|
message: "Project name",
|
|
44
45
|
placeholder: suggestedName.name,
|
|
45
46
|
validate: (value) => validateProjectSetupNameText(value, suggestedName.name)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
3
|
import { Result, TaggedError, UnhandledException } from "better-result";
|
|
4
4
|
//#region src/lib/project/local-pin.ts
|
|
5
5
|
const LOCAL_RESOLUTION_PIN_RELATIVE_PATH = ".prisma/local.json";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { formatCommandArgument } from "../../shell/command-arguments.js";
|
|
1
2
|
import { CliError } from "../../shell/errors.js";
|
|
2
3
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "./local-pin.js";
|
|
3
|
-
import { formatCommandArgument } from "../../shell/command-arguments.js";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
5
4
|
import path from "node:path";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
6
|
import { Result, TaggedError, matchError } from "better-result";
|
|
7
7
|
//#region src/lib/project/resolution.ts
|
|
8
8
|
var ProjectNotFoundError = class extends TaggedError("ProjectNotFoundError")() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import "../../shell/command-arguments.js";
|
|
1
2
|
import { CliError, usageError } from "../../shell/errors.js";
|
|
2
3
|
import { shortenHomePath } from "../fs/home-path.js";
|
|
3
4
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
|
|
4
|
-
import "../../shell/command-arguments.js";
|
|
5
5
|
import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { Result, matchError } from "better-result";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { formatShellCommand } from "../shell/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 };
|
package/dist/presenters/auth.js
CHANGED
|
@@ -25,7 +25,7 @@ function renderAuthSuccess(context, descriptor, command, result) {
|
|
|
25
25
|
context: rows,
|
|
26
26
|
operationDescription: "Applying authentication session changes",
|
|
27
27
|
operationCount: 1,
|
|
28
|
-
details: ["Session stored in local CLI state."]
|
|
28
|
+
details: ["Session stored in local CLI state.", ...result.agentSetupTip ? [`Install Prisma skills for this project with ${result.agentSetupTip.command}.`] : []]
|
|
29
29
|
}, context.ui);
|
|
30
30
|
}
|
|
31
31
|
if (command === "auth.logout") return renderMutate({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
1
2
|
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
2
3
|
import { padDisplay, renderNextSteps, renderSummaryLine, renderVerboseBlock } from "../shell/ui.js";
|
|
3
4
|
import { shortenHomePath } from "../lib/fs/home-path.js";
|
|
4
|
-
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
5
5
|
import { renderMutate, renderShow, serializeList } from "../output/patterns.js";
|
|
6
6
|
import { renderResolvedProjectContextBlock } from "./verbose-context.js";
|
|
7
7
|
import stringWidth from "string-width";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const PRISMA_CLI_PACKAGE_SPEC = `@prisma/cli@latest`;
|
|
2
|
+
const DEFAULT_PRISMA_CLI_PACKAGE_RUNNER = ["npx", "-y"];
|
|
3
|
+
const PRISMA_CLI_BINARY = "prisma-cli";
|
|
4
|
+
function formatPrismaCliCommand(args, options = {}) {
|
|
5
|
+
return [...getPrismaCliCommandPrefix(options), ...args].join(" ");
|
|
6
|
+
}
|
|
7
|
+
function getPrismaCliCommandPrefix({ invocation = "package", packageRunner = DEFAULT_PRISMA_CLI_PACKAGE_RUNNER }) {
|
|
8
|
+
if (invocation === "binary") return [PRISMA_CLI_BINARY];
|
|
9
|
+
return [...packageRunner, PRISMA_CLI_PACKAGE_SPEC];
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { formatPrismaCliCommand };
|
|
@@ -2,5 +2,11 @@
|
|
|
2
2
|
function formatCommandArgument(value) {
|
|
3
3
|
return /^[A-Za-z0-9._/-]+$/.test(value) && !value.startsWith("-") ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
4
4
|
}
|
|
5
|
+
function formatShellCommand(command) {
|
|
6
|
+
return command.map(formatShellCommandWord).join(" ");
|
|
7
|
+
}
|
|
8
|
+
function formatShellCommandWord(value) {
|
|
9
|
+
return /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
10
|
+
}
|
|
5
11
|
//#endregion
|
|
6
|
-
export { formatCommandArgument };
|
|
12
|
+
export { formatCommandArgument, formatShellCommand };
|