Skip to content

Commit cae35f1

Browse files
authored
📦 NEW: Use Langbase API key in .env file for deploy (#136)
1 parent f77edcf commit cae35f1

File tree

7 files changed

+114
-141
lines changed

7 files changed

+114
-141
lines changed

‎packages/baseai/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
"cli-table3": "^0.6.5",
6161
"cli-welcome": "^3.0.0",
6262
"compute-cosine-similarity": "^1.1.0",
63-
"conf": "^13.0.1",
6463
"cosmiconfig": "^9.0.0",
6564
"cosmiconfig-typescript-loader": "^5.0.0",
6665
"dotenv": "^16.4.5",

‎packages/baseai/src/add/index.ts

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { getStoredAuth } from '@/auth';
21
import { dim, dimItalic } from '@/utils/formatting';
32
import { getAvailablePipes } from '@/utils/get-available-pipes';
43
import { getAvailableTools } from '@/utils/get-available-tools';
54
import { heading } from '@/utils/heading';
65
import icons from '@/utils/icons';
76
import { isToolPresent } from '@/utils/is-tool-present';
7+
import { retrieveAuthentication } from '@/utils/retrieve-credentials';
88
import { formatCode } from '@/utils/ts-format-code';
99
import * as p from '@clack/prompts';
1010
import slugify from '@sindresorhus/slugify';
@@ -43,37 +43,6 @@ function extractLoginName(loginAndPipe: string) {
4343
};
4444
}
4545

46-
/**
47-
* Represents an account with login credentials and an API key.
48-
*/
49-
interface Account {
50-
login: string;
51-
apiKey: string;
52-
}
53-
54-
/**
55-
* Retrieves the stored authentication account.
56-
*
57-
* This function attempts to retrieve the stored authentication account
58-
* asynchronously. If the account is found, it is returned. If no account
59-
* is found or an error occurs during retrieval, `null` is returned.
60-
*
61-
* @returns {Promise<Account | null>} A promise that resolves to the stored
62-
* authentication account, or `null` if no account is found or an error occurs.
63-
*/
64-
async function retrieveAuthentication(): Promise<Account | null> {
65-
try {
66-
const account = await getStoredAuth();
67-
if (!account) return null;
68-
69-
return account;
70-
} catch (error) {
71-
p.log.error(
72-
`Error retrieving stored auth: ${(error as Error).message}`
73-
);
74-
return null;
75-
}
76-
}
7746

7847
/**
7948
* Fetches a pipe from Langbase using the provided login and name.
@@ -93,9 +62,17 @@ async function getPipe({
9362
name: string;
9463
spinner: Spinner;
9564
}) {
96-
spinner.start('Fetching pipe from Langbase');
9765
try {
98-
const account = await retrieveAuthentication();
66+
const account = await retrieveAuthentication({ spinner });
67+
if (!account) {
68+
p.log.error(
69+
'Authentication failed. Please run "npx baseai auth" to authenticate.'
70+
);
71+
return;
72+
}
73+
74+
spinner.start('Fetching pipe from Langbase');
75+
9976
const API_URL = `https://api.langbase.com/v1/pipes/${login}/${name}`;
10077

10178
const createResponse = await fetch(API_URL, {

‎packages/baseai/src/auth/index.ts

Lines changed: 36 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,11 @@ import {
99
outro,
1010
password
1111
} from '@clack/prompts';
12-
import Conf from 'conf';
13-
import fs from 'fs';
12+
import fs from 'fs/promises';
1413
import open from 'open';
1514
import path from 'path';
1615
import color from 'picocolors';
1716

18-
const config = new Conf({
19-
projectName: 'baseai'
20-
});
21-
22-
interface Account {
23-
login: string;
24-
apiKey: string;
25-
}
26-
2717
export async function auth() {
2818
p.intro(
2919
heading({
@@ -72,17 +62,6 @@ export async function auth() {
7262
process.exit(1);
7363
}
7464

75-
// Store in Conf (old functionality)
76-
const newAccount: Account = { login, apiKey };
77-
const existingAccounts = (config.get('accounts') as Account[]) || [];
78-
const updatedAccounts = [...existingAccounts, newAccount];
79-
config.set('accounts', updatedAccounts);
80-
81-
// Store in .env file (new functionality)
82-
// const envKeyName = apiKey.startsWith('user_')
83-
// ? 'LANGBASE_USER_API_KEY'
84-
// : 'LANGBASE_ORG_API_KEY';
85-
8665
const envKeyName = 'LANGBASE_API_KEY';
8766
const envContent = `\n# Langbase API key for https://langbase.com/${login}\n${envKeyName}=${apiKey}\n\n`;
8867

@@ -99,37 +78,45 @@ export async function auth() {
9978
const baiConfig = await loadConfig();
10079
let envFile = baiConfig.envFilePath || '.env';
10180

102-
fs.appendFileSync(path.join(process.cwd(), envFile), envContent);
81+
const envFileContent = await fs.readFile(envFile, 'utf-8');
82+
83+
const oldKey = envFileContent
84+
.split('\n')
85+
.reverse() // Reverse to get the latest key if there are multiple
86+
.find(line => line.includes('LANGBASE_API_KEY'))
87+
?.split('=')[1];
88+
89+
if (oldKey) {
90+
const shouldOverwrite = await confirm({
91+
message: `API key found in ${envFile}. Overwrite?`
92+
});
93+
94+
if (isCancel(shouldOverwrite)) {
95+
cancel('Operation cancelled.');
96+
process.exit(0);
97+
}
98+
99+
if (!shouldOverwrite) {
100+
outro(
101+
color.yellow('Operation cancelled. API key not overwritten.')
102+
);
103+
process.exit(0);
104+
}
105+
106+
const newEnvContent = envFileContent.replace(
107+
new RegExp(`LANGBASE_API_KEY=${oldKey}`),
108+
envContent.trim()
109+
);
110+
111+
await fs.writeFile(path.join(process.cwd(), envFile), newEnvContent);
112+
} else {
113+
await fs.appendFile(path.join(process.cwd(), envFile), envContent);
114+
}
103115

104116
outro(
105117
color.green(
106-
`Authentication successful. Credentials stored in config and ${envFile}`
118+
`Authentication successful. Credentials stored in ${envFile}`
107119
)
108120
);
109-
console.log(color.dim(`Config file location: ${config.path}`));
110121
process.exit(0);
111122
}
112-
113-
export function getStoredAuth(): Account | undefined {
114-
const accounts = (config.get('accounts') as Account[]) || [];
115-
const currentLogin = config.get('currentAccount') as string | undefined;
116-
117-
if (currentLogin) {
118-
return accounts.find(account => account.login === currentLogin);
119-
}
120-
121-
return accounts[0]; // Return the first account if no current account is set
122-
}
123-
124-
export function getStoredAccounts(): Account[] {
125-
return (config.get('accounts') as Account[]) || [];
126-
}
127-
128-
export function setCurrentAccount(login: string): boolean {
129-
const accounts = getStoredAccounts();
130-
if (accounts.some(account => account.login === login)) {
131-
config.set('currentAccount', login);
132-
return true;
133-
}
134-
return false;
135-
}

‎packages/baseai/src/deploy/document.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import {
77
handleError,
88
handleInvalidConfig,
99
listMemoryDocuments,
10-
retrieveAuthentication,
1110
uploadDocumentsToMemory,
12-
type Account
1311
} from '.';
1412
import path from 'path';
1513
import fs from 'fs/promises';
@@ -21,6 +19,7 @@ import {
2119
} from '@/utils/memory/load-memory-files';
2220
import type { MemoryI } from 'types/memory';
2321
import { compareDocumentLists } from '@/utils/memory/compare-docs-list';
22+
import { retrieveAuthentication, type Account } from '@/utils/retrieve-credentials';
2423

2524
type Spinner = ReturnType<typeof p.spinner>;
2625

‎packages/baseai/src/deploy/index.ts

Lines changed: 7 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import path from 'path';
1818
import color from 'picocolors';
1919
import { type MemoryI } from 'types/memory';
2020
import type { Pipe, PipeOld } from 'types/pipe';
21-
import { getStoredAuth } from './../auth/index';
2221
import {
2322
handleGitSyncMemories,
2423
updateDeployedCommitHash
@@ -28,11 +27,10 @@ import {
2827
generateUpgradeInstructions,
2928
isOldMemoryConfigFormat
3029
} from '@/utils/memory/handle-old-memory-config';
31-
32-
export interface Account {
33-
login: string;
34-
apiKey: string;
35-
}
30+
import {
31+
retrieveAuthentication,
32+
type Account
33+
} from '@/utils/retrieve-credentials';
3634

3735
interface ErrorResponse {
3836
error?: { message: string };
@@ -161,26 +159,6 @@ async function readToolsDirectory({
161159
}
162160
}
163161

164-
export async function retrieveAuthentication({
165-
spinner
166-
}: {
167-
spinner: Spinner;
168-
}): Promise<Account | null> {
169-
spinner.start('Retrieving stored authentication');
170-
try {
171-
const account = await getStoredAuth();
172-
if (!account) {
173-
handleNoAccountFound({ spinner });
174-
return null;
175-
}
176-
spinner.stop(`Deploying as ${color.cyan(account.login)}`);
177-
return account;
178-
} catch (error) {
179-
handleAuthError({ spinner, error });
180-
return null;
181-
}
182-
}
183-
184162
async function deployPipes({
185163
spinner,
186164
pipes,
@@ -357,23 +335,6 @@ function handleDirectoryReadError({
357335
}
358336
}
359337

360-
function handleNoAccountFound({ spinner }: { spinner: Spinner }): void {
361-
spinner.stop('No account found');
362-
p.log.warn('No account found. Please authenticate first.');
363-
p.log.info(`Run: ${color.green('npx baseai auth')}`);
364-
}
365-
366-
function handleAuthError({
367-
spinner,
368-
error
369-
}: {
370-
spinner: Spinner;
371-
error: unknown;
372-
}): void {
373-
spinner.stop('Failed to retrieve authentication');
374-
p.log.error(`Error retrieving stored auth: ${(error as Error).message}`);
375-
}
376-
377338
export function handleInvalidConfig({
378339
spinner,
379340
name,
@@ -932,11 +893,6 @@ async function getSignedUploadUrl({
932893
memoryName
933894
});
934895

935-
const isOrgAccount = account.apiKey.includes(':');
936-
937-
const ownerLogin = isOrgAccount
938-
? account.apiKey.split(':')[0]
939-
: account.login;
940896
try {
941897
const response = await fetch(uploadDocument, {
942898
method: 'POST',
@@ -947,7 +903,6 @@ async function getSignedUploadUrl({
947903
body: JSON.stringify({
948904
meta,
949905
memoryName,
950-
ownerLogin,
951906
fileName: documentName
952907
})
953908
});
@@ -1184,10 +1139,10 @@ export async function deploySingleMemory({
11841139
// Retrieve authentication
11851140
const account = await retrieveAuthentication({ spinner });
11861141
if (!account) {
1187-
p.log.error(
1188-
'Authentication failed. Please run "npx baseai auth" to authenticate.'
1142+
p.outro(
1143+
`No account found. Skipping deployment. \n Run: ${cyan('npx baseai@latest auth')}`
11891144
);
1190-
return;
1145+
process.exit(1);
11911146
}
11921147

11931148
// Call deployMemory function
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { loadConfig } from './config/config-handler';
2+
import fs from 'fs/promises';
3+
import * as p from '@clack/prompts';
4+
import color from 'picocolors';
5+
6+
export interface Account {
7+
apiKey: string;
8+
}
9+
10+
type Spinner = ReturnType<typeof p.spinner>;
11+
12+
function handleNoAccountFound({ spinner }: { spinner: Spinner }): void {
13+
spinner.stop('No account found');
14+
p.log.warn('No account found. Please authenticate first.');
15+
p.log.info(`Run: ${color.green('npx baseai auth')}`);
16+
}
17+
function handleAuthError({
18+
spinner,
19+
error
20+
}: {
21+
spinner: Spinner;
22+
error: unknown;
23+
}): void {
24+
spinner.stop('Failed to retrieve authentication');
25+
p.log.error(`Error retrieving stored auth: ${(error as Error).message}`);
26+
}
27+
28+
export async function retrieveAuthentication({
29+
spinner
30+
}: {
31+
spinner: Spinner;
32+
}): Promise<Account | null> {
33+
spinner.start('Retrieving stored authentication');
34+
try {
35+
const baiConfig = await loadConfig();
36+
let envFile = baiConfig.envFilePath || '.env';
37+
const envFileContent = await fs.readFile(envFile, 'utf-8');
38+
39+
const apiKey = envFileContent
40+
.split('\n')
41+
.reverse()
42+
.find(line => line.includes('LANGBASE_API_KEY='))
43+
?.split('=')[1];
44+
45+
if (!apiKey) {
46+
handleNoAccountFound({ spinner });
47+
return null;
48+
}
49+
50+
spinner.stop('Retrieved stored authentication');
51+
52+
return {
53+
apiKey
54+
};
55+
} catch (error) {
56+
handleAuthError({ spinner, error });
57+
return null;
58+
}
59+
}

‎pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)