Skip to content

Commit

Permalink
Merge pull request #86 from jucasoliveira/85-typeerror-cannot-read-pr…
Browse files Browse the repository at this point in the history
…operties-of-undefined-reading-status

Fix console.log and file deletion bugs
  • Loading branch information
jucasoliveira authored Nov 21, 2023
2 parents 7479be7 + b4efb15 commit c8ee466
Show file tree
Hide file tree
Showing 9 changed files with 3,714 additions and 3,592 deletions.
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.3",
"version": "1.7.4",
"main": "lib/index.js",
"description": "Get GPT like chatGPT on your terminal",
"scripts": {
Expand Down
5 changes: 5 additions & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

let context: any[] = [];

/**
* Adds a new context to the existing context array.
*
* @param {any} text - The text to be added to the context array.
*/
export function addContext(text: any) {
context = [...context, text];
}
Expand Down
115 changes: 78 additions & 37 deletions src/encrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,101 @@ import * as crypto from "crypto";
const algorithm = "aes-256-cbc";
const secretKey = "terminalGPT";

/**
* 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) {
const iv = crypto.randomBytes(16);
const key = crypto.scryptSync(secretKey, "salt", 32);
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text);
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");
encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString("hex") + ":" + encrypted.toString("hex");
}

/**
* Decrypts the given text using a specific algorithm and secret key.
*
* @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();
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();
}

/**
* Saves a custom URL to a file.
*
* @param {string | undefined} url - The custom URL to be saved. If undefined, the file is deleted.
* @return {void}
*/
export function saveCustomUrl(url: string | undefined) {
if (url === undefined) {
fs.unlinkSync(`${__dirname}/customUrl.txt`);
} else {
fs.writeFileSync(`${__dirname}/customUrl.txt`, url);
}
if (url === undefined) {
fs.unlinkSync(`${__dirname}/customUrl.txt`);
} else {
fs.writeFileSync(`${__dirname}/customUrl.txt`, url);
}
}

/**
* Retrieves a custom URL from a text file if it exists.
*
* @return {string | undefined} The custom URL if it exists, otherwise undefined.
*/
export function getCustomUrl(): string | undefined {
if (fs.existsSync(`${__dirname}/customUrl.txt`)) {
return fs.readFileSync(
`${__dirname}/customUrl.txt`,
"utf8"
);
}
return undefined
if (fs.existsSync(`${__dirname}/customUrl.txt`)) {
return fs.readFileSync(`${__dirname}/customUrl.txt`, "utf8");
}
return undefined;
}

/**
* Saves the API key to a file.
*
* @param {string} apiKey - The API key to save.
* @return {void} This function does not return anything.
*/
export function saveApiKey(apiKey: string) {
fs.writeFileSync(`${__dirname}/apiKey.txt`, apiKey);
fs.writeFileSync(`${__dirname}/apiKey.txt`, apiKey);
}

/**
* Deletes the API key file if it exists.
*
* @return {boolean} Returns true if the API key file was deleted, false otherwise.
*/
export function deleteApiKey() {
fs.unlinkSync(`${__dirname}/apiKey.txt`);
const apiKeyFilePath = `${__dirname}/apiKey.txt`;
if (fs.existsSync(apiKeyFilePath)) {
fs.unlinkSync(apiKeyFilePath);
return true;
}
return false;
}

/**
* Retrieves the API key from the "apiKey.txt" file, decrypts it, and returns it.
*
* @return {string | null} The decrypted API key, or null if the file does not exist.
*/
export function getApiKey(): string | null {
if (fs.existsSync(`${__dirname}/apiKey.txt`)) {
const getEncryptedScript = fs.readFileSync(
`${__dirname}/apiKey.txt`,
"utf8"
);
return decrypt(getEncryptedScript);
}
return null;
}
if (fs.existsSync(`${__dirname}/apiKey.txt`)) {
const getEncryptedScript = fs.readFileSync(
`${__dirname}/apiKey.txt`,
"utf8"
);
return decrypt(getEncryptedScript);
}
return null;
}
2 changes: 2 additions & 0 deletions src/gpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default async (
temperature: opts.temperature ? Number(opts.temperature) : 1,
})
.then((res) => {
console.log("here");
if (typeof res.choices[0].message !== "undefined") {
addContext(res.choices[0].message);
spinner.stop();
Expand All @@ -41,6 +42,7 @@ export default async (
}
})
.catch((err) => {
console.log(err);
spinner.stop();
switch (err["response"]["status"]) {
case 404:
Expand Down
5 changes: 5 additions & 0 deletions src/gradient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ const referenceGradient: string[] = [
...gradientColors,
];

/**
* Generates the frames for a gradient animation.
*
* @return {string[]} An array of strings representing the frames of the animation.
*/
export function getGradientAnimFrames() {
const frames: string[] = [];
for (let start = 0; start < gradientColors.length * 2; start++) {
Expand Down
165 changes: 85 additions & 80 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,99 +6,104 @@ import prompts from "prompts";

import intro from "./intro";

import {apiKeyPrompt, generateResponse} from "./utils";
import { apiKeyPrompt, generateResponse } from "./utils";

import {deleteApiKey, saveCustomUrl} from "./encrypt";
import { deleteApiKey, saveCustomUrl } from "./encrypt";

import * as c from "commander";

const commander = new c.Command()
const commander = new c.Command();
commander
.command("chat")
.option("-e, --engine <engine>", "GPT model to use")
.option("-t, --temperature <temperature>", "Response temperature")
.option("-m, --markdown", "Show markdown in the terminal")
.usage(`"<project-directory>" [options]`)
.action(async (opts) => {
intro();
apiKeyPrompt()
.then((apiKey: string | null) => {
const prompt = async () => {
const response = await prompts({
type: "text",
name: "value",
message: `${chalk.blueBright("You: ")}`,
validate: () => {
return true
},
onState: (state) => {
if (state.aborted) {
process.exit(0);
}
}
})
.command("chat")
.option("-e, --engine <engine>", "GPT model to use")
.option("-t, --temperature <temperature>", "Response temperature")
.option("-m, --markdown", "Show markdown in the terminal")
.usage(`"<project-directory>" [options]`)
.action(async (opts) => {
intro();
apiKeyPrompt().then((apiKey: string | null) => {
const prompt = async () => {
const response = await prompts({
type: "text",
name: "value",
message: `${chalk.blueBright("You: ")}`,
validate: () => {
return true;
},
onState: (state) => {
if (state.aborted) {
process.exit(0);
}
},
});

switch (response.value) {
case "exit":
return process.exit(0);
case "clear":
return process.stdout.write("\x1Bc");
default:
if (apiKey != null) {
generateResponse(apiKey, prompt, response, opts);
}
return;
}
};
prompt();
});
switch (response.value) {
case "exit":
return process.exit(0);
case "clear":
return process.stdout.write("\x1Bc");
default:
if (apiKey != null) {
generateResponse(apiKey, prompt, response, opts);
}
return;
}
};
prompt();
});
});

// create commander to delete api key
commander
.command("delete")
.description("Delete your API key")
.action(async () => {
const response = await prompts({
type: "select",
name: "value",
message: "Are you sure?",
choices: [
{title: "Yes", value: "yes"},
{title: "No - exit", value: "no"},
],
initial: 0,
});

if (response.value) {
return process.exit(0)
}
.command("delete")
.description("Delete your API key")
.action(async () => {
const response = await prompts({
type: "select",
name: "value",
message: "Are you sure?",
choices: [
{ title: "Yes", value: "yes" },
{ title: "No - exit", value: "no" },
],
initial: 0,
});

deleteApiKey();
if (response.value === "yes") {
const apiKeyDeleted = deleteApiKey();
if (apiKeyDeleted) {
console.log("API key deleted");
});
} else {
console.log("API key file not found, no action taken.");
}
return process.exit(0);
} else {
console.log("Deletion cancelled");
return process.exit(0);
}
});

commander
.command("endpoint")
.option("--set <url>", "Set your custom endpoint")
.option("-r, --reset", "Reset the API endpoint to default ")
.description("Configure your API endpoint")
.action(async () => {
console.log("Send empty to set default openai endpoint")
prompts({
type: "text",
name: "value",
validate: (t) =>
t.search(/(https?:\/(\/.)+).*/g) === 0 || t === "" ? true : "Urls only allowed",
message: "Insert endpoint: "
})
.then(response =>
(response.value as string)
.replace('/chat/completions', '')
)
.then(value => /(https?:\/(\/.)+).*/g.test(value) ? value : undefined)
.then(saveCustomUrl)
}
)
.command("endpoint")
.option("--set <url>", "Set your custom endpoint")
.option("-r, --reset", "Reset the API endpoint to default ")
.description("Configure your API endpoint")
.action(async () => {
console.log("Send empty to set default openai endpoint");
prompts({
type: "text",
name: "value",
validate: (t) =>
t.search(/(https?:\/(\/.)+).*/g) === 0 || t === ""
? true
: "Urls only allowed",
message: "Insert endpoint: ",
})
.then((response) =>
(response.value as string).replace("/chat/completions", "")
)
.then((value) => (/(https?:\/(\/.)+).*/g.test(value) ? value : undefined))
.then(saveCustomUrl);
});

commander.parse(process.argv);
Loading

0 comments on commit c8ee466

Please sign in to comment.