@barekey/cli 0.4.0 → 0.5.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.
- package/README.md +53 -12
- package/bun.lock +9 -3
- package/dist/auth-provider.js +7 -4
- package/dist/command-utils.js +6 -6
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.js +47 -0
- package/dist/commands/auth.js +31 -9
- package/dist/commands/billing.d.ts +2 -0
- package/dist/commands/billing.js +59 -0
- package/dist/commands/env.js +157 -125
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +32 -0
- package/dist/commands/org.d.ts +2 -0
- package/dist/commands/org.js +85 -0
- package/dist/commands/project.d.ts +2 -0
- package/dist/commands/project.js +99 -0
- package/dist/commands/stage.d.ts +2 -0
- package/dist/commands/stage.js +125 -0
- package/dist/commands/target-prompts.d.ts +184 -0
- package/dist/commands/target-prompts.js +312 -0
- package/dist/commands/typegen.d.ts +2 -2
- package/dist/commands/typegen.js +57 -32
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/context/session-id.d.ts +11 -0
- package/dist/context/session-id.js +14 -0
- package/dist/contracts/index.d.ts +499 -0
- package/dist/contracts/index.js +313 -0
- package/dist/credentials-store.js +70 -11
- package/dist/http.d.ts +34 -0
- package/dist/http.js +56 -2
- package/dist/index.js +12 -0
- package/dist/runtime-config.js +14 -26
- package/dist/typegen/core.d.ts +45 -0
- package/dist/typegen/core.js +219 -0
- package/dist/types.d.ts +5 -3
- package/package.json +2 -2
- package/src/auth-provider.ts +8 -5
- package/src/command-utils.ts +6 -6
- package/src/commands/audit.ts +63 -0
- package/src/commands/auth.ts +45 -39
- package/src/commands/billing.ts +70 -0
- package/src/commands/env.ts +211 -218
- package/src/commands/init.ts +47 -0
- package/src/commands/org.ts +104 -0
- package/src/commands/project.ts +130 -0
- package/src/commands/stage.ts +167 -0
- package/src/commands/target-prompts.ts +357 -0
- package/src/commands/typegen.ts +71 -45
- package/src/constants.ts +1 -1
- package/src/context/session-id.ts +14 -0
- package/src/contracts/index.ts +376 -0
- package/src/credentials-store.ts +86 -12
- package/src/http.ts +78 -2
- package/src/index.ts +12 -0
- package/src/runtime-config.ts +19 -32
- package/src/typegen/core.ts +311 -0
- package/src/types.ts +5 -3
- package/test/command-utils.test.ts +47 -0
- package/test/credentials-store.test.ts +40 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type TypegenManifest } from "../contracts/index.js";
|
|
2
|
+
export type CliTypegenMode = "semantic" | "minimal";
|
|
3
|
+
export type CliRuntimeMode = "centralized" | "standalone";
|
|
4
|
+
export type CliTypegenResult = {
|
|
5
|
+
written: boolean;
|
|
6
|
+
path: string;
|
|
7
|
+
serverPath: string;
|
|
8
|
+
publicPath: string;
|
|
9
|
+
manifestVersion: string;
|
|
10
|
+
};
|
|
11
|
+
type TypegenIdentity = {
|
|
12
|
+
baseUrl: string | null;
|
|
13
|
+
orgSlug: string;
|
|
14
|
+
projectSlug: string;
|
|
15
|
+
stageSlug: string;
|
|
16
|
+
typegenMode: CliTypegenMode;
|
|
17
|
+
runtimeMode: CliRuntimeMode;
|
|
18
|
+
localEnvRoot: string | null;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Renders the generated SDK declaration files for one manifest.
|
|
22
|
+
*
|
|
23
|
+
* @param manifest The decoded typegen manifest.
|
|
24
|
+
* @param mode The requested typegen mode.
|
|
25
|
+
* @returns The rendered server and public declaration contents.
|
|
26
|
+
* @remarks The CLI owns this rendering so it no longer depends on SDK runtime helpers.
|
|
27
|
+
* @lastModified 2026-03-19
|
|
28
|
+
* @author GPT-5.4
|
|
29
|
+
*/
|
|
30
|
+
export declare function renderGeneratedTypesForManifest(manifest: TypegenManifest, mode: CliTypegenMode): {
|
|
31
|
+
serverContents: string;
|
|
32
|
+
publicContents: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Writes generated SDK declaration files into the installed `@barekey/sdk` package.
|
|
36
|
+
*
|
|
37
|
+
* @param manifest The decoded typegen manifest.
|
|
38
|
+
* @param identity The manifest identity used for metadata freshness.
|
|
39
|
+
* @returns The typegen write result.
|
|
40
|
+
* @remarks This preserves the existing generated file locations without importing the SDK package at runtime.
|
|
41
|
+
* @lastModified 2026-03-19
|
|
42
|
+
* @author GPT-5.4
|
|
43
|
+
*/
|
|
44
|
+
export declare function writeInstalledSdkGeneratedTypes(manifest: TypegenManifest, identity: TypegenIdentity): Promise<CliTypegenResult>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { access, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Schema } from "effect";
|
|
5
|
+
import { TypegenManifestSchema } from "../contracts/index.js";
|
|
6
|
+
const MANIFEST_VERSION_PATTERN = /\/\* barekey-manifest-version: ([^\n]+) \*\//;
|
|
7
|
+
const TypegenMetadataSchema = Schema.Struct({
|
|
8
|
+
version: Schema.Number,
|
|
9
|
+
last: Schema.String,
|
|
10
|
+
baseUrl: Schema.NullOr(Schema.String),
|
|
11
|
+
orgSlug: Schema.NullOr(Schema.String),
|
|
12
|
+
projectSlug: Schema.NullOr(Schema.String),
|
|
13
|
+
stageSlug: Schema.NullOr(Schema.String),
|
|
14
|
+
typegenMode: Schema.NullOr(Schema.Literal("semantic", "minimal")),
|
|
15
|
+
runtimeMode: Schema.NullOr(Schema.Literal("centralized", "standalone")),
|
|
16
|
+
localEnvRoot: Schema.NullOr(Schema.String),
|
|
17
|
+
});
|
|
18
|
+
function renderLinearMilestones(milestones) {
|
|
19
|
+
if (milestones.length === 0) {
|
|
20
|
+
return "readonly []";
|
|
21
|
+
}
|
|
22
|
+
return `readonly [${milestones
|
|
23
|
+
.map((milestone) => `readonly [${JSON.stringify(milestone.at)}, ${String(milestone.percentage)}]`)
|
|
24
|
+
.join(", ")}]`;
|
|
25
|
+
}
|
|
26
|
+
function renderRolloutMetadataType(row) {
|
|
27
|
+
const renderedMilestones = renderLinearMilestones(row.rolloutMilestones ?? []);
|
|
28
|
+
if (row.rolloutFunction === "step") {
|
|
29
|
+
return `Step<${renderedMilestones}>`;
|
|
30
|
+
}
|
|
31
|
+
if (row.rolloutFunction === "ease_in_out") {
|
|
32
|
+
return `EaseInOut<${renderedMilestones}>`;
|
|
33
|
+
}
|
|
34
|
+
return `Linear<${renderedMilestones}>`;
|
|
35
|
+
}
|
|
36
|
+
function renderVariableType(row, mode) {
|
|
37
|
+
if (mode === "minimal") {
|
|
38
|
+
return row.typeScriptType;
|
|
39
|
+
}
|
|
40
|
+
const descriptor = `{ Type: ${row.typeScriptType}; Kind: ${JSON.stringify(row.kind)}; Visibility: ${JSON.stringify(row.visibility)}; Rollout: ${row.kind === "rollout" ? renderRolloutMetadataType(row) : "never"} }`;
|
|
41
|
+
return `Env<${descriptor}>`;
|
|
42
|
+
}
|
|
43
|
+
function buildGeneratedTypesContents(manifest, input) {
|
|
44
|
+
const mapLines = manifest.variables
|
|
45
|
+
.slice()
|
|
46
|
+
.filter(input.include)
|
|
47
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
48
|
+
.map((row) => ` ${JSON.stringify(row.name)}: ${renderVariableType(row, input.mode)};`)
|
|
49
|
+
.join("\n");
|
|
50
|
+
const importLine = input.mode === "semantic"
|
|
51
|
+
? `import type { EaseInOut, Env, Linear, Step } from "${input.typeModulePath}";\n\n`
|
|
52
|
+
: "";
|
|
53
|
+
return `/* eslint-disable */\n/* This file is generated by barekey typegen. */\n/* barekey-manifest-version: ${manifest.manifestVersion} */\n\n${importLine}declare module "${input.declaredModulePath}" {\n interface ${input.interfaceName} {\n${mapLines.length > 0 ? mapLines : ""}\n }\n}\n\nexport {};\n`;
|
|
54
|
+
}
|
|
55
|
+
function readManifestVersion(contents) {
|
|
56
|
+
return contents?.match(MANIFEST_VERSION_PATTERN)?.[1]?.trim() ?? null;
|
|
57
|
+
}
|
|
58
|
+
async function readTextFile(filePath) {
|
|
59
|
+
try {
|
|
60
|
+
return await readFile(filePath, "utf8");
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const nodeError = error;
|
|
64
|
+
if (nodeError?.code === "ENOENT") {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function writeTextFileAtomic(filePath, value) {
|
|
71
|
+
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
72
|
+
try {
|
|
73
|
+
await writeFile(temporaryPath, value, "utf8");
|
|
74
|
+
await rename(temporaryPath, filePath);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
try {
|
|
78
|
+
await unlink(temporaryPath);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Best-effort cleanup only.
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function resolveInstalledSdkTypegenTarget() {
|
|
87
|
+
const require = createRequire(path.join(process.cwd(), "__barekey_typegen__.cjs"));
|
|
88
|
+
let resolvedEntrypoint;
|
|
89
|
+
try {
|
|
90
|
+
resolvedEntrypoint = require.resolve("@barekey/sdk/package.json");
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
let current = path.dirname(resolvedEntrypoint);
|
|
96
|
+
while (true) {
|
|
97
|
+
const candidatePackageJson = path.join(current, "package.json");
|
|
98
|
+
const rawPackageJson = await readTextFile(candidatePackageJson);
|
|
99
|
+
if (rawPackageJson !== null) {
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(rawPackageJson);
|
|
102
|
+
if (parsed.name === "@barekey/sdk") {
|
|
103
|
+
const serverGeneratedTypesPath = path.join(current, "generated.server.d.ts");
|
|
104
|
+
const publicGeneratedTypesPath = path.join(current, "generated.public.d.ts");
|
|
105
|
+
await access(serverGeneratedTypesPath);
|
|
106
|
+
await access(publicGeneratedTypesPath);
|
|
107
|
+
return {
|
|
108
|
+
packageRoot: current,
|
|
109
|
+
serverGeneratedTypesPath,
|
|
110
|
+
publicGeneratedTypesPath,
|
|
111
|
+
typegenMetadataPath: path.join(current, "typegen.json"),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Keep walking upward.
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const parent = path.dirname(current);
|
|
120
|
+
if (parent === current) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
current = parent;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function buildTypegenMetadataContents(lastGeneratedAt, identity) {
|
|
127
|
+
return `${JSON.stringify({
|
|
128
|
+
version: 1,
|
|
129
|
+
last: lastGeneratedAt.toISOString(),
|
|
130
|
+
baseUrl: identity.baseUrl,
|
|
131
|
+
orgSlug: identity.orgSlug,
|
|
132
|
+
projectSlug: identity.projectSlug,
|
|
133
|
+
stageSlug: identity.stageSlug,
|
|
134
|
+
typegenMode: identity.typegenMode,
|
|
135
|
+
runtimeMode: identity.runtimeMode,
|
|
136
|
+
localEnvRoot: identity.localEnvRoot,
|
|
137
|
+
}, null, 2)}\n`;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Renders the generated SDK declaration files for one manifest.
|
|
141
|
+
*
|
|
142
|
+
* @param manifest The decoded typegen manifest.
|
|
143
|
+
* @param mode The requested typegen mode.
|
|
144
|
+
* @returns The rendered server and public declaration contents.
|
|
145
|
+
* @remarks The CLI owns this rendering so it no longer depends on SDK runtime helpers.
|
|
146
|
+
* @lastModified 2026-03-19
|
|
147
|
+
* @author GPT-5.4
|
|
148
|
+
*/
|
|
149
|
+
export function renderGeneratedTypesForManifest(manifest, mode) {
|
|
150
|
+
const decodedManifest = Schema.decodeUnknownSync(TypegenManifestSchema)(manifest);
|
|
151
|
+
return {
|
|
152
|
+
serverContents: buildGeneratedTypesContents(decodedManifest, {
|
|
153
|
+
mode,
|
|
154
|
+
typeModulePath: "./dist/types.js",
|
|
155
|
+
declaredModulePath: "./dist/types.js",
|
|
156
|
+
interfaceName: "BarekeyGeneratedTypeMap",
|
|
157
|
+
include: () => true,
|
|
158
|
+
}),
|
|
159
|
+
publicContents: buildGeneratedTypesContents(decodedManifest, {
|
|
160
|
+
mode,
|
|
161
|
+
typeModulePath: "./dist/public-types.js",
|
|
162
|
+
declaredModulePath: "./dist/public-types.js",
|
|
163
|
+
interfaceName: "BarekeyPublicGeneratedTypeMap",
|
|
164
|
+
include: (row) => row.visibility === "public",
|
|
165
|
+
}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Writes generated SDK declaration files into the installed `@barekey/sdk` package.
|
|
170
|
+
*
|
|
171
|
+
* @param manifest The decoded typegen manifest.
|
|
172
|
+
* @param identity The manifest identity used for metadata freshness.
|
|
173
|
+
* @returns The typegen write result.
|
|
174
|
+
* @remarks This preserves the existing generated file locations without importing the SDK package at runtime.
|
|
175
|
+
* @lastModified 2026-03-19
|
|
176
|
+
* @author GPT-5.4
|
|
177
|
+
*/
|
|
178
|
+
export async function writeInstalledSdkGeneratedTypes(manifest, identity) {
|
|
179
|
+
const target = await resolveInstalledSdkTypegenTarget();
|
|
180
|
+
if (target === null) {
|
|
181
|
+
return {
|
|
182
|
+
written: false,
|
|
183
|
+
path: "",
|
|
184
|
+
serverPath: "",
|
|
185
|
+
publicPath: "",
|
|
186
|
+
manifestVersion: manifest.manifestVersion,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const [existingServerContents, existingPublicContents] = await Promise.all([
|
|
190
|
+
readTextFile(target.serverGeneratedTypesPath),
|
|
191
|
+
readTextFile(target.publicGeneratedTypesPath),
|
|
192
|
+
]);
|
|
193
|
+
const rendered = renderGeneratedTypesForManifest(manifest, identity.typegenMode);
|
|
194
|
+
if (existingServerContents === rendered.serverContents &&
|
|
195
|
+
existingPublicContents === rendered.publicContents &&
|
|
196
|
+
readManifestVersion(existingServerContents) === manifest.manifestVersion &&
|
|
197
|
+
readManifestVersion(existingPublicContents) === manifest.manifestVersion) {
|
|
198
|
+
await writeTextFileAtomic(target.typegenMetadataPath, buildTypegenMetadataContents(new Date(), identity));
|
|
199
|
+
return {
|
|
200
|
+
written: false,
|
|
201
|
+
path: target.serverGeneratedTypesPath,
|
|
202
|
+
serverPath: target.serverGeneratedTypesPath,
|
|
203
|
+
publicPath: target.publicGeneratedTypesPath,
|
|
204
|
+
manifestVersion: manifest.manifestVersion,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
await Promise.all([
|
|
208
|
+
writeTextFileAtomic(target.serverGeneratedTypesPath, rendered.serverContents),
|
|
209
|
+
writeTextFileAtomic(target.publicGeneratedTypesPath, rendered.publicContents),
|
|
210
|
+
]);
|
|
211
|
+
await writeTextFileAtomic(target.typegenMetadataPath, buildTypegenMetadataContents(new Date(), identity));
|
|
212
|
+
return {
|
|
213
|
+
written: true,
|
|
214
|
+
path: target.serverGeneratedTypesPath,
|
|
215
|
+
serverPath: target.serverGeneratedTypesPath,
|
|
216
|
+
publicPath: target.publicGeneratedTypesPath,
|
|
217
|
+
manifestVersion: manifest.manifestVersion,
|
|
218
|
+
};
|
|
219
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ export type CliCredentials = {
|
|
|
4
4
|
accessTokenExpiresAtMs: number;
|
|
5
5
|
refreshTokenExpiresAtMs: number;
|
|
6
6
|
clerkUserId: string;
|
|
7
|
-
orgId: string;
|
|
8
|
-
orgSlug: string;
|
|
7
|
+
orgId: string | null;
|
|
8
|
+
orgSlug: string | null;
|
|
9
|
+
lastOrgId?: string | null;
|
|
10
|
+
lastOrgSlug?: string | null;
|
|
9
11
|
};
|
|
10
12
|
export type CliConfig = {
|
|
11
13
|
baseUrl: string;
|
|
12
|
-
|
|
14
|
+
activeSessionId: string;
|
|
13
15
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barekey/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Barekey command line interface",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"packageManager": "bun@1.2.22",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@clack/prompts": "^0.11.0",
|
|
18
|
-
"@barekey/sdk": "^0.5.0",
|
|
19
18
|
"commander": "^14.0.1",
|
|
19
|
+
"effect": "3.19.19",
|
|
20
20
|
"open": "^10.2.0",
|
|
21
21
|
"picocolors": "^1.1.1"
|
|
22
22
|
},
|
package/src/auth-provider.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { postJson } from "./http.js";
|
|
2
2
|
import type { CliCredentials } from "./types.js";
|
|
3
3
|
import { loadConfig, loadCredentials, saveCredentials } from "./credentials-store.js";
|
|
4
|
+
import { buildSessionId } from "./context/session-id.js";
|
|
4
5
|
|
|
5
6
|
type CliAuthProvider = {
|
|
6
7
|
getAccessToken(): Promise<string>;
|
|
@@ -13,7 +14,7 @@ export function createCliAuthProvider(): CliAuthProvider {
|
|
|
13
14
|
|
|
14
15
|
async function readCurrentCredentials(): Promise<{
|
|
15
16
|
baseUrl: string;
|
|
16
|
-
|
|
17
|
+
sessionId: string;
|
|
17
18
|
credentials: CliCredentials;
|
|
18
19
|
}> {
|
|
19
20
|
const config = await loadConfig();
|
|
@@ -21,7 +22,7 @@ export function createCliAuthProvider(): CliAuthProvider {
|
|
|
21
22
|
throw new Error("Not logged in. Run barekey login first.");
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
const credentials = await loadCredentials(config.
|
|
25
|
+
const credentials = await loadCredentials(config.activeSessionId);
|
|
25
26
|
if (credentials === null) {
|
|
26
27
|
throw new Error("CLI credentials are missing. Run barekey login again.");
|
|
27
28
|
}
|
|
@@ -30,13 +31,13 @@ export function createCliAuthProvider(): CliAuthProvider {
|
|
|
30
31
|
|
|
31
32
|
return {
|
|
32
33
|
baseUrl: config.baseUrl,
|
|
33
|
-
|
|
34
|
+
sessionId: config.activeSessionId,
|
|
34
35
|
credentials,
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
async function refreshIfNeeded(): Promise<CliCredentials> {
|
|
39
|
-
const { baseUrl,
|
|
40
|
+
const { baseUrl, credentials } = await readCurrentCredentials();
|
|
40
41
|
const now = Date.now();
|
|
41
42
|
if (!forceRefresh && credentials.accessTokenExpiresAtMs > now + 10_000) {
|
|
42
43
|
return credentials;
|
|
@@ -66,9 +67,11 @@ export function createCliAuthProvider(): CliAuthProvider {
|
|
|
66
67
|
clerkUserId: refreshed.clerkUserId,
|
|
67
68
|
orgId: refreshed.orgId,
|
|
68
69
|
orgSlug: refreshed.orgSlug,
|
|
70
|
+
lastOrgId: refreshed.orgId,
|
|
71
|
+
lastOrgSlug: refreshed.orgSlug,
|
|
69
72
|
};
|
|
70
73
|
|
|
71
|
-
await saveCredentials(
|
|
74
|
+
await saveCredentials(buildSessionId(baseUrl, refreshed.clerkUserId), nextCredentials);
|
|
72
75
|
cachedCredentials = nextCredentials;
|
|
73
76
|
forceRefresh = false;
|
|
74
77
|
return nextCredentials;
|
package/src/command-utils.ts
CHANGED
|
@@ -49,14 +49,14 @@ export async function requireLocalSession(): Promise<LocalSession> {
|
|
|
49
49
|
throw new Error("Not logged in. Run barekey auth login first.");
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const credentials = await loadCredentials(config.
|
|
52
|
+
const credentials = await loadCredentials(config.activeSessionId);
|
|
53
53
|
if (credentials === null) {
|
|
54
54
|
throw new Error("Saved credentials not found. Run barekey auth login again.");
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
return {
|
|
58
58
|
baseUrl: config.baseUrl,
|
|
59
|
-
accountId: config.
|
|
59
|
+
accountId: config.activeSessionId,
|
|
60
60
|
credentials,
|
|
61
61
|
};
|
|
62
62
|
}
|
|
@@ -74,14 +74,14 @@ export async function resolveTarget(
|
|
|
74
74
|
|
|
75
75
|
const projectSlug = options.project?.trim() || runtime?.config.project || "";
|
|
76
76
|
const stageSlug = options.stage?.trim() || runtime?.config.environment || "";
|
|
77
|
-
const orgSlug = options.org?.trim() || runtime?.config.org ||
|
|
77
|
+
const orgSlug = options.org?.trim() || runtime?.config.org || "";
|
|
78
78
|
|
|
79
|
-
if (!isStandalone && (projectSlug.length === 0 || stageSlug.length === 0)) {
|
|
79
|
+
if (!isStandalone && (orgSlug.length === 0 || projectSlug.length === 0 || stageSlug.length === 0)) {
|
|
80
80
|
const hint = runtime
|
|
81
|
-
? `Found ${runtime.path} but project/environment is incomplete.`
|
|
81
|
+
? `Found ${runtime.path} but organization/project/environment is incomplete.`
|
|
82
82
|
: "No barekey.json found in current directory tree.";
|
|
83
83
|
throw new Error(
|
|
84
|
-
`${hint}
|
|
84
|
+
`${hint} Run barekey init or pass --org/--project/--stage.`,
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import { createCliAuthProvider } from "../auth-provider.js";
|
|
4
|
+
import { requireLocalSession, toJsonOutput, type EnvTargetOptions } from "../command-utils.js";
|
|
5
|
+
import { AuditListResponseSchema } from "../contracts/index.js";
|
|
6
|
+
import { postJson } from "../http.js";
|
|
7
|
+
import { promptForOrganizationSlug } from "./target-prompts.js";
|
|
8
|
+
|
|
9
|
+
async function runAuditList(
|
|
10
|
+
options: EnvTargetOptions & {
|
|
11
|
+
limit?: string;
|
|
12
|
+
json?: boolean;
|
|
13
|
+
},
|
|
14
|
+
) {
|
|
15
|
+
const local = await requireLocalSession();
|
|
16
|
+
const authProvider = createCliAuthProvider();
|
|
17
|
+
const accessToken = await authProvider.getAccessToken();
|
|
18
|
+
const orgSlug = await promptForOrganizationSlug(options.org);
|
|
19
|
+
const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
|
|
20
|
+
|
|
21
|
+
const response = await postJson({
|
|
22
|
+
baseUrl: local.baseUrl,
|
|
23
|
+
path: "/v1/cli/audit/list",
|
|
24
|
+
accessToken,
|
|
25
|
+
payload: {
|
|
26
|
+
orgSlug,
|
|
27
|
+
projectSlug: options.project?.trim() || null,
|
|
28
|
+
limit: Number.isFinite(limit) ? limit : 25,
|
|
29
|
+
},
|
|
30
|
+
schema: AuditListResponseSchema,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (options.json) {
|
|
34
|
+
toJsonOutput(true, response);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (response.items.length === 0) {
|
|
39
|
+
console.log("No audit events found.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const item of response.items) {
|
|
44
|
+
console.log(`${new Date(item.occurredAtMs).toISOString()} ${item.category} ${item.title}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function registerAuditCommands(program: Command): void {
|
|
49
|
+
const audit = program.command("audit").description("Audit history");
|
|
50
|
+
|
|
51
|
+
audit
|
|
52
|
+
.command("list")
|
|
53
|
+
.description("List recent audit events")
|
|
54
|
+
.option("--org <slug>", "Organization slug")
|
|
55
|
+
.option("--project <slug>", "Project slug")
|
|
56
|
+
.option("--limit <count>", "Maximum number of events", "25")
|
|
57
|
+
.option("--json", "Machine-readable output", false)
|
|
58
|
+
.action(
|
|
59
|
+
async (options: EnvTargetOptions & { limit?: string; json?: boolean }) => {
|
|
60
|
+
await runAuditList(options);
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
}
|
package/src/commands/auth.ts
CHANGED
|
@@ -7,12 +7,19 @@ import pc from "picocolors";
|
|
|
7
7
|
import open from "open";
|
|
8
8
|
|
|
9
9
|
import { createCliAuthProvider } from "../auth-provider.js";
|
|
10
|
+
import { buildSessionId } from "../context/session-id.js";
|
|
10
11
|
import {
|
|
11
12
|
clearConfig,
|
|
12
13
|
deleteCredentials,
|
|
13
14
|
saveConfig,
|
|
14
15
|
saveCredentials,
|
|
15
16
|
} from "../credentials-store.js";
|
|
17
|
+
import {
|
|
18
|
+
CliSessionResponseSchema,
|
|
19
|
+
DevicePollResponseSchema,
|
|
20
|
+
DeviceStartResponseSchema,
|
|
21
|
+
RefreshResponseSchema,
|
|
22
|
+
} from "../contracts/index.js";
|
|
16
23
|
import { getJson, postJson } from "../http.js";
|
|
17
24
|
import { requireLocalSession, resolveBaseUrl, toJsonOutput } from "../command-utils.js";
|
|
18
25
|
|
|
@@ -50,6 +57,14 @@ function resolveVerificationUri(baseUrl: string, verificationUri: string): strin
|
|
|
50
57
|
return verificationUri;
|
|
51
58
|
}
|
|
52
59
|
|
|
60
|
+
function resolveDisplayName(input: {
|
|
61
|
+
displayName: string | null;
|
|
62
|
+
clerkUserId: string;
|
|
63
|
+
}): string {
|
|
64
|
+
const displayName = input.displayName?.trim();
|
|
65
|
+
return displayName && displayName.length > 0 ? displayName : input.clerkUserId;
|
|
66
|
+
}
|
|
67
|
+
|
|
53
68
|
async function runLogin(options: { baseUrl?: string }): Promise<void> {
|
|
54
69
|
const baseUrl = await resolveBaseUrl(options.baseUrl);
|
|
55
70
|
intro("Barekey CLI login");
|
|
@@ -60,18 +75,13 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
|
|
|
60
75
|
let pollActive = false;
|
|
61
76
|
|
|
62
77
|
try {
|
|
63
|
-
const started = await postJson
|
|
64
|
-
deviceCode: string;
|
|
65
|
-
userCode: string;
|
|
66
|
-
verificationUri: string;
|
|
67
|
-
intervalSec: number;
|
|
68
|
-
expiresInSec: number;
|
|
69
|
-
}>({
|
|
78
|
+
const started = await postJson({
|
|
70
79
|
baseUrl,
|
|
71
80
|
path: "/v1/cli/device/start",
|
|
72
81
|
payload: {
|
|
73
82
|
clientName: resolveClientName(),
|
|
74
83
|
},
|
|
84
|
+
schema: DeviceStartResponseSchema,
|
|
75
85
|
});
|
|
76
86
|
const verificationUri = resolveVerificationUri(baseUrl, started.verificationUri);
|
|
77
87
|
|
|
@@ -93,27 +103,13 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
|
|
|
93
103
|
pollActive = true;
|
|
94
104
|
|
|
95
105
|
while (Date.now() < expiresAtMs) {
|
|
96
|
-
const poll = await postJson
|
|
97
|
-
| {
|
|
98
|
-
status: "pending";
|
|
99
|
-
intervalSec: number;
|
|
100
|
-
}
|
|
101
|
-
| {
|
|
102
|
-
status: "approved";
|
|
103
|
-
accessToken: string;
|
|
104
|
-
refreshToken: string;
|
|
105
|
-
accessTokenExpiresAtMs: number;
|
|
106
|
-
refreshTokenExpiresAtMs: number;
|
|
107
|
-
orgId: string;
|
|
108
|
-
orgSlug: string;
|
|
109
|
-
clerkUserId: string;
|
|
110
|
-
}
|
|
111
|
-
>({
|
|
106
|
+
const poll = await postJson({
|
|
112
107
|
baseUrl,
|
|
113
108
|
path: "/v1/cli/device/poll",
|
|
114
109
|
payload: {
|
|
115
110
|
deviceCode: started.deviceCode,
|
|
116
111
|
},
|
|
112
|
+
schema: DevicePollResponseSchema,
|
|
117
113
|
});
|
|
118
114
|
|
|
119
115
|
if (poll.status === "pending") {
|
|
@@ -121,24 +117,38 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
|
|
|
121
117
|
continue;
|
|
122
118
|
}
|
|
123
119
|
|
|
124
|
-
const
|
|
125
|
-
await saveCredentials(accountId, {
|
|
120
|
+
const refreshed = RefreshResponseSchema.make({
|
|
126
121
|
accessToken: poll.accessToken,
|
|
127
122
|
refreshToken: poll.refreshToken,
|
|
128
123
|
accessTokenExpiresAtMs: poll.accessTokenExpiresAtMs,
|
|
129
124
|
refreshTokenExpiresAtMs: poll.refreshTokenExpiresAtMs,
|
|
130
|
-
clerkUserId: poll.clerkUserId,
|
|
131
125
|
orgId: poll.orgId,
|
|
132
126
|
orgSlug: poll.orgSlug,
|
|
127
|
+
clerkUserId: poll.clerkUserId,
|
|
128
|
+
displayName: poll.displayName,
|
|
129
|
+
email: poll.email,
|
|
130
|
+
});
|
|
131
|
+
const sessionId = buildSessionId(baseUrl, refreshed.clerkUserId);
|
|
132
|
+
|
|
133
|
+
await saveCredentials(sessionId, {
|
|
134
|
+
accessToken: refreshed.accessToken,
|
|
135
|
+
refreshToken: refreshed.refreshToken,
|
|
136
|
+
accessTokenExpiresAtMs: refreshed.accessTokenExpiresAtMs,
|
|
137
|
+
refreshTokenExpiresAtMs: refreshed.refreshTokenExpiresAtMs,
|
|
138
|
+
clerkUserId: refreshed.clerkUserId,
|
|
139
|
+
orgId: refreshed.orgId,
|
|
140
|
+
orgSlug: refreshed.orgSlug,
|
|
141
|
+
lastOrgId: refreshed.orgId,
|
|
142
|
+
lastOrgSlug: refreshed.orgSlug,
|
|
133
143
|
});
|
|
134
144
|
await saveConfig({
|
|
135
145
|
baseUrl,
|
|
136
|
-
|
|
146
|
+
activeSessionId: sessionId,
|
|
137
147
|
});
|
|
138
148
|
|
|
139
149
|
pollSpinner.stop("Login approved");
|
|
140
150
|
pollActive = false;
|
|
141
|
-
outro(`
|
|
151
|
+
outro(`Signed in as ${pc.bold(resolveDisplayName(refreshed))}.`);
|
|
142
152
|
return;
|
|
143
153
|
}
|
|
144
154
|
|
|
@@ -158,7 +168,7 @@ async function runLogin(options: { baseUrl?: string }): Promise<void> {
|
|
|
158
168
|
|
|
159
169
|
async function runLogout(): Promise<void> {
|
|
160
170
|
const local = await requireLocalSession();
|
|
161
|
-
await postJson
|
|
171
|
+
await postJson({
|
|
162
172
|
baseUrl: local.baseUrl,
|
|
163
173
|
path: "/v1/cli/logout",
|
|
164
174
|
payload: {
|
|
@@ -174,15 +184,11 @@ async function runWhoami(options: { json?: boolean }): Promise<void> {
|
|
|
174
184
|
const local = await requireLocalSession();
|
|
175
185
|
const authProvider = createCliAuthProvider();
|
|
176
186
|
const accessToken = await authProvider.getAccessToken();
|
|
177
|
-
const session = await getJson
|
|
178
|
-
clerkUserId: string;
|
|
179
|
-
orgId: string;
|
|
180
|
-
orgSlug: string;
|
|
181
|
-
source: "clerk" | "cli";
|
|
182
|
-
}>({
|
|
187
|
+
const session = await getJson({
|
|
183
188
|
baseUrl: local.baseUrl,
|
|
184
189
|
path: "/v1/cli/session",
|
|
185
190
|
accessToken,
|
|
191
|
+
schema: CliSessionResponseSchema,
|
|
186
192
|
});
|
|
187
193
|
|
|
188
194
|
if (options.json) {
|
|
@@ -190,9 +196,10 @@ async function runWhoami(options: { json?: boolean }): Promise<void> {
|
|
|
190
196
|
return;
|
|
191
197
|
}
|
|
192
198
|
|
|
193
|
-
console.log(
|
|
194
|
-
|
|
195
|
-
|
|
199
|
+
console.log(resolveDisplayName(session));
|
|
200
|
+
if (session.email !== null) {
|
|
201
|
+
console.log(session.email);
|
|
202
|
+
}
|
|
196
203
|
}
|
|
197
204
|
|
|
198
205
|
export function registerAuthCommands(program: Command): void {
|
|
@@ -221,7 +228,6 @@ export function registerAuthCommands(program: Command): void {
|
|
|
221
228
|
await runWhoami(options);
|
|
222
229
|
});
|
|
223
230
|
|
|
224
|
-
// Backward-compatible top-level auth aliases.
|
|
225
231
|
program
|
|
226
232
|
.command("login")
|
|
227
233
|
.description("Alias for barekey auth login")
|