Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(package.json): bump version to 1.7.6 #91

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions __tests__/encrypt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ describe("Encryption and Decryption", () => {
expect(decrypted).to.equal("test");
});

it("should throw an error when given an invalid encrypted string", () => {
expect(() => decrypt("invalid:string")).to.throw();
it("should handle invalid encrypted strings correctly", () => {
expect(decrypt("invalid:string")).to.equal("invalid:string");
expect(decrypt("invalidstring")).to.equal("invalidstring");
expect(decrypt("")).to.be.null;
});
});

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "terminalgpt",
"version": "1.7.5",
"version": "1.7.6",
"main": "lib/index.js",
"description": "Get GPT like chatGPT on your terminal",
"scripts": {
Expand Down
105 changes: 79 additions & 26 deletions src/creds.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
import * as fs from "fs";

import * as crypto from "crypto";
import * as path from "path";

const algorithm = "aes-256-cbc";
const secretKey = "terminalGPT";
const secretKeyFile = path.join(__dirname, "secret_key.txt");

// Function to generate and save a secret key
function generateAndSaveSecretKey(): string {
const secretKey = crypto.randomBytes(32).toString("hex");
fs.writeFileSync(secretKeyFile, secretKey, "utf8");
return secretKey;
}

// Function to get or create the secret key
function getSecretKey(): string {
if (fs.existsSync(secretKeyFile)) {
return fs.readFileSync(secretKeyFile, "utf8");
} else {
return generateAndSaveSecretKey();
}
}

const secretKey = getSecretKey();

function isEncrypted(text: string): boolean {
return text.includes(":") && /^[0-9a-f]+:[0-9a-f]+$/.test(text);
}

/**
* Encrypts the given text using the specified algorithm, secret key, and initialization vector.
*
* @param {string} text - The text to be encrypted.
* @return {string} The encrypted text in the format: IV:encryptedText.
*/
export function encrypt(text: string) {
export function encrypt(text: string): string {
if (isEncrypted(text)) {
return text;

Check warning on line 38 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 39 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
const iv = crypto.randomBytes(16);
const key = crypto.scryptSync(secretKey, "salt", 32);
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text);

encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString("hex") + ":" + encrypted.toString("hex");
let encrypted = cipher.update(text, "utf8", "hex");
encrypted += cipher.final("hex");
return iv.toString("hex") + ":" + encrypted;
}

/**
Expand All @@ -27,15 +51,33 @@
* @param {string} text - The text to be decrypted.
* @return {string} - The decrypted text.
*/
export function decrypt(text: string) {
const textParts = text.split(":");
const iv = Buffer.from(textParts.shift()!, "hex");
const encryptedText = Buffer.from(textParts.join(":"), "hex");
const key = crypto.scryptSync(secretKey, "salt", 32);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
export function decrypt(text: string | undefined): string | null {
if (!text) {
return null;
}

if (!isEncrypted(text)) {
return text;
}

const [ivHex, encryptedHex] = text.split(":");
if (!ivHex || !encryptedHex) {
return null;

Check warning on line 65 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 66 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

try {
const iv = Buffer.from(ivHex, "hex");
const encrypted = Buffer.from(encryptedHex, "hex");
const key = crypto.scryptSync(secretKey, "salt", 32);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encrypted);
decrypted = Buffer.concat([decrypted, decipher.final()]);
const result = decrypted.toString("utf8");
return result;
} catch (error) {
console.error("Decryption error:", error);

Check warning on line 78 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
return null;

Check warning on line 79 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}
}

/**
Expand All @@ -61,13 +103,18 @@
export function saveCredentials(
apiKey: string,
engine: string,
tavilyApiKey: string
tavilyApiKey?: string
) {
const credentials = { apiKey: encrypt(apiKey), engine, tavilyApiKey };
const credentials = {
apiKey: encrypt(apiKey),
engine,
tavilyApiKey: tavilyApiKey ? encrypt(tavilyApiKey) : undefined,

Check warning on line 111 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
};
fs.writeFileSync(
`${__dirname}/credentials.json`,
JSON.stringify(credentials)
JSON.stringify(credentials, null, 2)
);
console.log("Credentials saved successfully");
}

/**
Expand All @@ -81,14 +128,20 @@
tavilyApiKey: string | null;
} {
if (fs.existsSync(`${__dirname}/credentials.json`)) {
const data = fs.readFileSync(`${__dirname}/credentials.json`, "utf8");
const { apiKey, engine, tavilyApiKey } = JSON.parse(data);
return {
apiKey: apiKey ? decrypt(apiKey) : null,
engine,
tavilyApiKey: tavilyApiKey || null,
};
try {
const data = fs.readFileSync(`${__dirname}/credentials.json`, "utf8");
const { apiKey, engine, tavilyApiKey } = JSON.parse(data);
return {
apiKey: apiKey ? decrypt(apiKey) : null,

Check warning on line 135 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
engine: engine || null,

Check warning on line 136 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
tavilyApiKey: tavilyApiKey ? decrypt(tavilyApiKey) : null,

Check warning on line 137 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
};
} catch (error) {
console.error("Error reading or parsing credentials:", error);

Check warning on line 140 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
return { apiKey: null, engine: null, tavilyApiKey: null };

Check warning on line 141 in src/creds.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}
}
console.log("Credentials file not found");
return { apiKey: null, engine: null, tavilyApiKey: null };
}

Expand Down
4 changes: 1 addition & 3 deletions src/engine/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export const AnthropicEngine = async (
}
) => {
const apiKeyValue = await apiKey;

const anthropic = new AnthropicClient({ apiKey: apiKeyValue });
const spinner = loadWithRocketGradient("Thinking...").start();

try {
const relevantContext = getContext(prompt);
console.log("Relevant context:", relevantContext); // Debug log

let messages: ContextItem[] = [];
let systemMessage = "";
Expand All @@ -46,8 +46,6 @@ export const AnthropicEngine = async (
}
}

console.log("Final messages:", messages); // Debug log

const requestParams: MessageCreateParamsNonStreaming = {
model: opts.model || "claude-3-opus-20240229",
messages: messages,
Expand Down
18 changes: 9 additions & 9 deletions src/handlers/webHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,27 @@
import axios from "axios";
import { addContext } from "../context";
import chalk from "chalk";
import { getCredentials, saveCredentials, encrypt, decrypt } from "../creds";
import { saveCredentials, getCredentials } from "../creds";
import readline from "readline";

export async function handleWebResearch(query: string, userPrompt: string) {
try {
let credentials = getCredentials();
let credentials = await getCredentials();

if (!credentials.tavilyApiKey) {
console.log(chalk.yellow("Tavily API key not found."));
console.log(
chalk.cyan("Please visit https://tavily.com to get an API key.")
);
const apiKey = await promptForApiKey();
const encryptedTavilyApiKey = encrypt(apiKey);
const tavilyApiKey = await promptForApiKey();
saveCredentials(
credentials.apiKey || "",
credentials.engine || "",
encryptedTavilyApiKey
tavilyApiKey
);
credentials = getCredentials();
}

const tavilyApiKey = decrypt(credentials.tavilyApiKey!);
const tavilyApiKey = credentials.tavilyApiKey!;

console.log(chalk.yellow(`Searching the web for: "${query}"...`));

Expand All @@ -38,7 +36,7 @@ export async function handleWebResearch(query: string, userPrompt: string) {
const searchResults = response.data.results
.map(
(result: any) =>
`Title: ${result.title}\nSnippet: ${result.snippet}\n\n`
`Title: ${result.title}\nSnippet: ${result.content}\n\n`
)
.join("");

Expand All @@ -57,6 +55,7 @@ export async function handleWebResearch(query: string, userPrompt: string) {
}
}

// Update the promptForApiKey function
async function promptForApiKey(): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
Expand All @@ -66,7 +65,8 @@ async function promptForApiKey(): Promise<string> {
return new Promise((resolve) => {
rl.question("Please enter your Tavily API key: ", (apiKey) => {
rl.close();
resolve(encrypt(apiKey.trim()));
// Remove encryption here
resolve(apiKey.trim());
});
});
}
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
},
];

if (!apiKey || !engine) {
const response = await prompts(questions);
// Save both API key and engine
saveCredentials(encrypt(response.apiKey), response.engine, "user");
saveCredentials(encrypt(response.apiKey), response.engine);

Check warning on line 58 in src/utils.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
return { apiKey: response.apiKey, engine: response.engine };
}

Check warning on line 60 in src/utils.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Expand Down
Loading