@prisma/compute-cli 0.1.0
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/README.md +118 -0
- package/bin/compute +4 -0
- package/bin/compute-bun +4 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +28 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/deploy.d.ts +56 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +106 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +84 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/helpers.d.ts +27 -0
- package/dist/commands/helpers.d.ts.map +1 -0
- package/dist/commands/helpers.js +128 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +17 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +17 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/projects.d.ts +2 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +43 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/services.d.ts +2 -0
- package/dist/commands/services.d.ts.map +1 -0
- package/dist/commands/services.js +318 -0
- package/dist/commands/services.js.map +1 -0
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.d.ts.map +1 -0
- package/dist/commands/versions.js +321 -0
- package/dist/commands/versions.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth/client.d.ts +7 -0
- package/dist/lib/auth/client.d.ts.map +1 -0
- package/dist/lib/auth/client.js +26 -0
- package/dist/lib/auth/client.js.map +1 -0
- package/dist/lib/auth/guard.d.ts +12 -0
- package/dist/lib/auth/guard.d.ts.map +1 -0
- package/dist/lib/auth/guard.js +39 -0
- package/dist/lib/auth/guard.js.map +1 -0
- package/dist/lib/auth/login.d.ts +15 -0
- package/dist/lib/auth/login.d.ts.map +1 -0
- package/dist/lib/auth/login.js +128 -0
- package/dist/lib/auth/login.js.map +1 -0
- package/dist/lib/auth/token-storage.d.ts +9 -0
- package/dist/lib/auth/token-storage.d.ts.map +1 -0
- package/dist/lib/auth/token-storage.js +55 -0
- package/dist/lib/auth/token-storage.js.map +1 -0
- package/dist/lib/compute-config.d.ts +12 -0
- package/dist/lib/compute-config.d.ts.map +1 -0
- package/dist/lib/compute-config.js +69 -0
- package/dist/lib/compute-config.js.map +1 -0
- package/dist/lib/format.d.ts +2 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +11 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/interaction.d.ts +17 -0
- package/dist/lib/interaction.d.ts.map +1 -0
- package/dist/lib/interaction.js +131 -0
- package/dist/lib/interaction.js.map +1 -0
- package/dist/lib/json-output.d.ts +3 -0
- package/dist/lib/json-output.d.ts.map +1 -0
- package/dist/lib/json-output.js +9 -0
- package/dist/lib/json-output.js.map +1 -0
- package/dist/lib/progress.d.ts +6 -0
- package/dist/lib/progress.d.ts.map +1 -0
- package/dist/lib/progress.js +156 -0
- package/dist/lib/progress.js.map +1 -0
- package/package.json +45 -0
- package/src/cli.ts +30 -0
- package/src/commands/deploy.ts +127 -0
- package/src/commands/env.ts +104 -0
- package/src/commands/helpers.ts +153 -0
- package/src/commands/login.ts +18 -0
- package/src/commands/logout.ts +18 -0
- package/src/commands/projects.ts +54 -0
- package/src/commands/services.ts +385 -0
- package/src/commands/versions.ts +389 -0
- package/src/index.ts +2 -0
- package/src/lib/auth/client.ts +39 -0
- package/src/lib/auth/guard.ts +51 -0
- package/src/lib/auth/login.ts +175 -0
- package/src/lib/auth/token-storage.ts +73 -0
- package/src/lib/compute-config.ts +95 -0
- package/src/lib/format.ts +16 -0
- package/src/lib/interaction.ts +166 -0
- package/src/lib/json-output.ts +9 -0
- package/src/lib/progress.ts +172 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { BunBuild, ComputeClient, PreBuilt } from "@prisma/compute-sdk";
|
|
3
|
+
import { defineCommand } from "citty";
|
|
4
|
+
import { writeComputeConfig } from "../lib/compute-config.ts";
|
|
5
|
+
import { createDeployInteraction } from "../lib/interaction.ts";
|
|
6
|
+
import { printJsonResult } from "../lib/json-output.ts";
|
|
7
|
+
import { createDeployProgress } from "../lib/progress.ts";
|
|
8
|
+
import {
|
|
9
|
+
endFlow,
|
|
10
|
+
getConfigPath,
|
|
11
|
+
handleError,
|
|
12
|
+
parseEnvFromArgs,
|
|
13
|
+
parsePollOptions,
|
|
14
|
+
parsePortMapping,
|
|
15
|
+
readComputeConfig,
|
|
16
|
+
startFlow,
|
|
17
|
+
withAuthAndSignal,
|
|
18
|
+
} from "./helpers.ts";
|
|
19
|
+
|
|
20
|
+
export const deployCommand = defineCommand({
|
|
21
|
+
meta: { name: "deploy", description: "Build and deploy your application" },
|
|
22
|
+
args: {
|
|
23
|
+
project: { type: "string", description: "Project ID" },
|
|
24
|
+
service: { type: "string", description: "Compute service ID" },
|
|
25
|
+
path: { type: "string", default: ".", description: "App directory" },
|
|
26
|
+
entrypoint: { type: "string", description: "Relative app entrypoint" },
|
|
27
|
+
serviceName: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Name for a new compute service",
|
|
30
|
+
},
|
|
31
|
+
region: { type: "string", description: "Region for a new compute service" },
|
|
32
|
+
skipBuild: {
|
|
33
|
+
type: "boolean",
|
|
34
|
+
default: false,
|
|
35
|
+
description: "Skip build, deploy pre-built artifact",
|
|
36
|
+
},
|
|
37
|
+
env: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Environment variables (repeat --env KEY=VALUE)",
|
|
40
|
+
},
|
|
41
|
+
httpPort: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "HTTP port your application listens on",
|
|
44
|
+
},
|
|
45
|
+
timeout: {
|
|
46
|
+
type: "string",
|
|
47
|
+
default: "120",
|
|
48
|
+
description: "Poll timeout in seconds",
|
|
49
|
+
},
|
|
50
|
+
pollInterval: {
|
|
51
|
+
type: "string",
|
|
52
|
+
default: "1000",
|
|
53
|
+
description: "Poll interval in milliseconds",
|
|
54
|
+
},
|
|
55
|
+
json: { type: "boolean", default: false, description: "Output JSON" },
|
|
56
|
+
},
|
|
57
|
+
run: async ({ args, rawArgs }) => {
|
|
58
|
+
const exitCode = await withAuthAndSignal(
|
|
59
|
+
async (managementClient, signal) => {
|
|
60
|
+
const flowStarted = !args.json && startFlow("Prisma Compute · Deploy");
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const sdk = new ComputeClient(managementClient);
|
|
64
|
+
const appPath = path.resolve(process.cwd(), args.path);
|
|
65
|
+
const envVars = parseEnvFromArgs(rawArgs);
|
|
66
|
+
const portMapping = parsePortMapping(args.httpPort);
|
|
67
|
+
|
|
68
|
+
const configPath = getConfigPath();
|
|
69
|
+
const currentConfig = await readComputeConfig(configPath);
|
|
70
|
+
|
|
71
|
+
const projectId = args.project ?? currentConfig?.projectId;
|
|
72
|
+
const serviceId = args.service ?? currentConfig?.serviceId;
|
|
73
|
+
|
|
74
|
+
let strategy: PreBuilt | BunBuild;
|
|
75
|
+
if (args.skipBuild) {
|
|
76
|
+
if (!args.entrypoint) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"When --skip-build is set you must provide --entrypoint",
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
strategy = new PreBuilt({ appPath, entrypoint: args.entrypoint });
|
|
82
|
+
} else {
|
|
83
|
+
strategy = new BunBuild({ appPath, entrypoint: args.entrypoint });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = await sdk.deploy({
|
|
87
|
+
strategy,
|
|
88
|
+
projectId,
|
|
89
|
+
serviceId,
|
|
90
|
+
serviceName: args.serviceName,
|
|
91
|
+
region: args.region,
|
|
92
|
+
envVars,
|
|
93
|
+
portMapping: portMapping ?? currentConfig?.portMapping,
|
|
94
|
+
...parsePollOptions(args),
|
|
95
|
+
interaction: args.json ? undefined : createDeployInteraction(),
|
|
96
|
+
progress: args.json ? undefined : createDeployProgress(),
|
|
97
|
+
signal,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (args.json) {
|
|
101
|
+
printJsonResult(result);
|
|
102
|
+
return result.isOk() ? 0 : 1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (result.isErr()) {
|
|
106
|
+
endFlow(flowStarted, "Deploy flow failed.");
|
|
107
|
+
console.error(result.error.message);
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
await writeComputeConfig(configPath, result.value.resolvedConfig);
|
|
112
|
+
|
|
113
|
+
console.log(`Version: ${result.value.versionId}`);
|
|
114
|
+
console.log("");
|
|
115
|
+
console.log("Deployment URL:");
|
|
116
|
+
console.log(result.value.deploymentUrl);
|
|
117
|
+
endFlow(flowStarted, "Deploy flow completed.");
|
|
118
|
+
return 0;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return handleError(error);
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ComputeClient } from "@prisma/compute-sdk";
|
|
2
|
+
import { defineCommand } from "citty";
|
|
3
|
+
import { writeComputeConfig } from "../lib/compute-config.ts";
|
|
4
|
+
import { createDeployInteraction } from "../lib/interaction.ts";
|
|
5
|
+
import { printJsonResult } from "../lib/json-output.ts";
|
|
6
|
+
import { createUpdateEnvProgress } from "../lib/progress.ts";
|
|
7
|
+
import {
|
|
8
|
+
endFlow,
|
|
9
|
+
getConfigPath,
|
|
10
|
+
handleError,
|
|
11
|
+
parseEnvFromArgs,
|
|
12
|
+
parsePollOptions,
|
|
13
|
+
parsePortMapping,
|
|
14
|
+
readComputeConfig,
|
|
15
|
+
startFlow,
|
|
16
|
+
withAuthAndSignal,
|
|
17
|
+
} from "./helpers.ts";
|
|
18
|
+
|
|
19
|
+
const envUpdateCommand = defineCommand({
|
|
20
|
+
meta: {
|
|
21
|
+
name: "update",
|
|
22
|
+
description: "Update environment variables (creates a new version)",
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
project: { type: "string", description: "Project ID" },
|
|
26
|
+
service: { type: "string", description: "Compute service ID" },
|
|
27
|
+
env: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Environment variables (repeat --env KEY=VALUE)",
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
httpPort: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "HTTP port your application listens on",
|
|
35
|
+
},
|
|
36
|
+
timeout: {
|
|
37
|
+
type: "string",
|
|
38
|
+
default: "120",
|
|
39
|
+
description: "Poll timeout in seconds",
|
|
40
|
+
},
|
|
41
|
+
pollInterval: {
|
|
42
|
+
type: "string",
|
|
43
|
+
default: "1000",
|
|
44
|
+
description: "Poll interval in milliseconds",
|
|
45
|
+
},
|
|
46
|
+
json: { type: "boolean", default: false, description: "Output JSON" },
|
|
47
|
+
},
|
|
48
|
+
run: async ({ args, rawArgs }) => {
|
|
49
|
+
const exitCode = await withAuthAndSignal(
|
|
50
|
+
async (managementClient, signal) => {
|
|
51
|
+
const flowStarted =
|
|
52
|
+
!args.json && startFlow("Prisma Compute · Env Update");
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const sdk = new ComputeClient(managementClient);
|
|
56
|
+
const envVars = parseEnvFromArgs(rawArgs);
|
|
57
|
+
const portMapping = parsePortMapping(args.httpPort);
|
|
58
|
+
|
|
59
|
+
const configPath = getConfigPath();
|
|
60
|
+
const currentConfig = await readComputeConfig(configPath);
|
|
61
|
+
|
|
62
|
+
const result = await sdk.updateEnv({
|
|
63
|
+
projectId: args.project ?? currentConfig?.projectId,
|
|
64
|
+
serviceId: args.service ?? currentConfig?.serviceId,
|
|
65
|
+
envVars,
|
|
66
|
+
portMapping: portMapping ?? currentConfig?.portMapping,
|
|
67
|
+
...parsePollOptions(args),
|
|
68
|
+
interaction: args.json ? undefined : createDeployInteraction(),
|
|
69
|
+
progress: args.json ? undefined : createUpdateEnvProgress(),
|
|
70
|
+
signal,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (args.json) {
|
|
74
|
+
printJsonResult(result);
|
|
75
|
+
return result.isOk() ? 0 : 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (result.isErr()) {
|
|
79
|
+
console.error(result.error.message);
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await writeComputeConfig(configPath, result.value.resolvedConfig);
|
|
84
|
+
|
|
85
|
+
console.log(`Version: ${result.value.versionId}`);
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log("Deployment URL:");
|
|
88
|
+
console.log(result.value.deploymentUrl);
|
|
89
|
+
endFlow(flowStarted, "Env update completed.");
|
|
90
|
+
return 0;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return handleError(error);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const envGroup = defineCommand({
|
|
102
|
+
meta: { name: "env", description: "Environment variable management" },
|
|
103
|
+
subCommands: { update: envUpdateCommand },
|
|
104
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { intro, outro } from "@clack/prompts";
|
|
3
|
+
import type { ManagementApiClient } from "@prisma/management-api-sdk";
|
|
4
|
+
import { requireAuth } from "../lib/auth/guard.ts";
|
|
5
|
+
import {
|
|
6
|
+
COMPUTE_CONFIG_FILENAME,
|
|
7
|
+
readComputeConfig,
|
|
8
|
+
} from "../lib/compute-config.ts";
|
|
9
|
+
import { isInteractiveSession, isPromptCancelled } from "../lib/interaction.ts";
|
|
10
|
+
|
|
11
|
+
export function parseEnvAssignments(entries: string[]): Record<string, string> {
|
|
12
|
+
const envVars: Record<string, string> = {};
|
|
13
|
+
for (const value of entries) {
|
|
14
|
+
const delimiter = value.indexOf("=");
|
|
15
|
+
if (delimiter <= 0) {
|
|
16
|
+
throw new Error(`Invalid --env value \`${value}\`; expected KEY=VALUE`);
|
|
17
|
+
}
|
|
18
|
+
const key = value.slice(0, delimiter).trim();
|
|
19
|
+
const envValue = value.slice(delimiter + 1);
|
|
20
|
+
if (key.length === 0) {
|
|
21
|
+
throw new Error(`Invalid --env key in \`${value}\``);
|
|
22
|
+
}
|
|
23
|
+
envVars[key] = envValue;
|
|
24
|
+
}
|
|
25
|
+
return envVars;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function extractEnvBatches(rawArgs: string[]): string[] {
|
|
29
|
+
const batches: string[] = [];
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
32
|
+
const arg = rawArgs[i];
|
|
33
|
+
if (arg === undefined) continue;
|
|
34
|
+
if (arg === "--") break;
|
|
35
|
+
|
|
36
|
+
if (arg === "--env") {
|
|
37
|
+
const value = rawArgs[i + 1];
|
|
38
|
+
if (value === undefined || value.startsWith("-")) {
|
|
39
|
+
batches.push("");
|
|
40
|
+
} else {
|
|
41
|
+
batches.push(value);
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (arg.startsWith("--env=")) {
|
|
48
|
+
batches.push(arg.slice("--env=".length));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return batches;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function parseEnvFromArgs(rawArgs: string[]): Record<string, string> {
|
|
56
|
+
// Citty still collapses repeated string flags, so preserve each --env from raw args.
|
|
57
|
+
const batches = extractEnvBatches(rawArgs);
|
|
58
|
+
return parseEnvAssignments(batches);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function parsePortMapping(
|
|
62
|
+
httpPort: string | undefined,
|
|
63
|
+
): { http: number } | undefined {
|
|
64
|
+
if (!httpPort) return undefined;
|
|
65
|
+
const port = Number.parseInt(httpPort, 10);
|
|
66
|
+
if (Number.isNaN(port)) {
|
|
67
|
+
throw new Error(`Invalid --http-port value: ${httpPort}`);
|
|
68
|
+
}
|
|
69
|
+
return { http: port };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseIntOrThrow(value: string, flag: string): number {
|
|
73
|
+
const n = Number.parseInt(value, 10);
|
|
74
|
+
if (Number.isNaN(n)) {
|
|
75
|
+
throw new Error(`Invalid ${flag} value: ${value}`);
|
|
76
|
+
}
|
|
77
|
+
return n;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function parsePollOptions(args: {
|
|
81
|
+
timeout: string;
|
|
82
|
+
pollInterval: string;
|
|
83
|
+
}) {
|
|
84
|
+
return {
|
|
85
|
+
timeoutSeconds: parseIntOrThrow(args.timeout, "--timeout"),
|
|
86
|
+
pollIntervalMs: parseIntOrThrow(args.pollInterval, "--poll-interval"),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getConfigPath(): string {
|
|
91
|
+
return path.join(process.cwd(), COMPUTE_CONFIG_FILENAME);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { readComputeConfig };
|
|
95
|
+
|
|
96
|
+
export function createCommandAbortSignal(): {
|
|
97
|
+
signal: AbortSignal;
|
|
98
|
+
cleanup(): void;
|
|
99
|
+
} {
|
|
100
|
+
const controller = new AbortController();
|
|
101
|
+
let cleanedUp = false;
|
|
102
|
+
|
|
103
|
+
function cleanup(): void {
|
|
104
|
+
if (cleanedUp) return;
|
|
105
|
+
cleanedUp = true;
|
|
106
|
+
process.off("SIGINT", onAbort);
|
|
107
|
+
process.off("SIGTERM", onAbort);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function onAbort(): void {
|
|
111
|
+
cleanup();
|
|
112
|
+
controller.abort();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
process.once("SIGINT", onAbort);
|
|
116
|
+
process.once("SIGTERM", onAbort);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
signal: controller.signal,
|
|
120
|
+
cleanup,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function withAuthAndSignal(
|
|
125
|
+
fn: (client: ManagementApiClient, signal: AbortSignal) => Promise<number>,
|
|
126
|
+
): Promise<number> {
|
|
127
|
+
const client = await requireAuth();
|
|
128
|
+
if (!client) return 1;
|
|
129
|
+
const { signal, cleanup } = createCommandAbortSignal();
|
|
130
|
+
try {
|
|
131
|
+
return await fn(client, signal);
|
|
132
|
+
} finally {
|
|
133
|
+
cleanup();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function handleError(error: unknown): number {
|
|
138
|
+
if (isPromptCancelled(error)) return 1;
|
|
139
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function startFlow(title: string): boolean {
|
|
144
|
+
if (!isInteractiveSession()) return false;
|
|
145
|
+
intro(title);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function endFlow(started: boolean, message = "Done."): void {
|
|
150
|
+
if (started) outro(message);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { isInteractiveSession };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { login as startLoginFlow } from "../lib/auth/login.ts";
|
|
3
|
+
|
|
4
|
+
export const loginCommand = defineCommand({
|
|
5
|
+
meta: { name: "login", description: "Authenticate with Prisma" },
|
|
6
|
+
run: async () => {
|
|
7
|
+
try {
|
|
8
|
+
console.log("Opening browser for authentication...");
|
|
9
|
+
await startLoginFlow();
|
|
10
|
+
console.log("Successfully authenticated.");
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error(
|
|
13
|
+
`Login failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
14
|
+
);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { FileTokenStorage } from "../lib/auth/token-storage.ts";
|
|
3
|
+
|
|
4
|
+
export const logoutCommand = defineCommand({
|
|
5
|
+
meta: { name: "logout", description: "Clear stored credentials" },
|
|
6
|
+
run: async () => {
|
|
7
|
+
try {
|
|
8
|
+
const tokenStorage = new FileTokenStorage();
|
|
9
|
+
await tokenStorage.clearTokens();
|
|
10
|
+
console.log("Logged out.");
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error(
|
|
13
|
+
`Logout failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
14
|
+
);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ComputeClient } from "@prisma/compute-sdk";
|
|
2
|
+
import { defineCommand } from "citty";
|
|
3
|
+
import { formatTable } from "../lib/format.ts";
|
|
4
|
+
import { printJsonResult } from "../lib/json-output.ts";
|
|
5
|
+
import { handleError, withAuthAndSignal } from "./helpers.ts";
|
|
6
|
+
|
|
7
|
+
const projectsListCommand = defineCommand({
|
|
8
|
+
meta: { name: "list", description: "List projects in your workspace" },
|
|
9
|
+
args: {
|
|
10
|
+
json: { type: "boolean", default: false, description: "Output JSON" },
|
|
11
|
+
},
|
|
12
|
+
run: async ({ args }) => {
|
|
13
|
+
const exitCode = await withAuthAndSignal(
|
|
14
|
+
async (managementClient, signal) => {
|
|
15
|
+
try {
|
|
16
|
+
const sdk = new ComputeClient(managementClient);
|
|
17
|
+
const result = await sdk.listProjects({ signal });
|
|
18
|
+
|
|
19
|
+
if (args.json) {
|
|
20
|
+
printJsonResult(result);
|
|
21
|
+
return result.isOk() ? 0 : 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (result.isErr()) {
|
|
25
|
+
console.error(result.error.message);
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (result.value.length === 0) {
|
|
30
|
+
console.log("No projects found.");
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(
|
|
35
|
+
formatTable(
|
|
36
|
+
["ID", "Name"],
|
|
37
|
+
result.value.map((p) => [p.id, p.name]),
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
return 0;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return handleError(error);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const projectsGroup = defineCommand({
|
|
52
|
+
meta: { name: "projects", description: "Project management" },
|
|
53
|
+
subCommands: { list: projectsListCommand },
|
|
54
|
+
});
|