Skip to content

Commit 7760bd0

Browse files
Merge pull request #90 from warpy-ai/main
New changes on terminalGPT
2 parents b288435 + e01c1a2 commit 7760bd0

24 files changed

+1047
-482
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,6 @@ dist
110110
*.txt
111111
*.jsonl
112112
.yarn
113-
lib
113+
lib
114+
115+
credentials.json

__tests__/encrypt.spec.ts

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,71 @@
1-
import * as crypto from "../src/encrypt";
2-
3-
import fs from "fs";
4-
1+
import { expect } from "chai";
2+
import * as fs from "fs";
53
import * as path from "path";
4+
import {
5+
encrypt,
6+
decrypt,
7+
getCredentials,
8+
saveCredentials,
9+
deleteCredentials,
10+
} from "../src/creds";
611

7-
import {expect} from "chai";
12+
describe("Encryption and Decryption", () => {
13+
it("should return a string in the format 'IV:encrypted_text'", () => {
14+
const result = encrypt("test");
15+
expect(result).to.be.a("string");
16+
expect(result).to.include(":");
17+
});
818

9-
describe("encrypt()", () => {
10-
it("should return a string in the format 'IV:encrypted_text'", () => {
11-
const result = crypto.encrypt("test");
12-
expect(result).to.match(/^[a-f0-9]{32}:([a-f0-9]{2})*$/);
13-
});
19+
it("should return a different result each time it is called", () => {
20+
const result1 = encrypt("test");
21+
const result2 = encrypt("test");
22+
expect(result1).to.not.equal(result2);
23+
});
1424

15-
it("should return a different result each time it is called", () => {
16-
const result1 = crypto.encrypt("test");
17-
const result2 = crypto.encrypt("test");
18-
expect(result1).not.equal(result2);
19-
});
20-
});
25+
it("should return the original input text when given a valid encrypted string", () => {
26+
const encrypted = encrypt("test");
27+
const decrypted = decrypt(encrypted);
28+
expect(decrypted).to.equal("test");
29+
});
2130

22-
describe("decrypt()", () => {
23-
it("should return the original input text when given a valid encrypted string", () => {
24-
const encrypted = crypto.encrypt("test");
25-
const result = crypto.decrypt(encrypted);
26-
expect(result).to.equal("test");
27-
});
28-
29-
it("should throw an error when given an invalid encrypted string", () => {
30-
expect(() => crypto.decrypt("invalid")).to.throw();
31-
});
31+
it("should throw an error when given an invalid encrypted string", () => {
32+
expect(() => decrypt("invalid:string")).to.throw();
33+
});
3234
});
3335

34-
describe("Api Key ", () => {
35-
// the path to the apiKey.txt file should point to ../src/apiKey.txt
36-
const apiKeyPath = path.resolve(__dirname, "../src/apiKey.txt");
36+
describe("Credentials Management", () => {
37+
const credentialsPath = path.resolve(__dirname, "../src/credentials.json");
3738

38-
const removeLink = () => {
39-
try {
40-
fs.unlinkSync(apiKeyPath)
41-
} catch { /* empty */
42-
}
43-
}
39+
beforeEach(() => {
40+
deleteCredentials();
41+
});
4442

45-
beforeEach(removeLink)
46-
afterEach(removeLink)
43+
afterEach(() => {
44+
deleteCredentials();
45+
});
4746

48-
it("should return null if the API key has not been saved", () => {
49-
const result = crypto.getApiKey();
50-
expect(result).to.equal(null);
47+
it("should return null values if credentials have not been saved", () => {
48+
const result = getCredentials();
49+
expect(result).to.deep.equal({
50+
apiKey: null,
51+
engine: null,
52+
tavilyApiKey: null,
5153
});
54+
});
5255

53-
it("should return the API key if it has been saved", () => {
54-
const encrypted = crypto.encrypt("test");
55-
crypto.saveApiKey(encrypted)
56-
const result = crypto.getApiKey();
57-
expect(result).to.equal("test");
58-
});
56+
it("should save and retrieve credentials correctly", () => {
57+
saveCredentials("testApiKey", "testEngine", "testTavilyApiKey");
58+
const result = getCredentials();
59+
expect(result.apiKey).to.equal("testApiKey");
60+
expect(result.engine).to.equal("testEngine");
61+
expect(result.tavilyApiKey).to.equal("testTavilyApiKey");
62+
});
5963

60-
it("should save the given API key to a file", () => {
61-
if (!fs.existsSync(apiKeyPath)) {
62-
fs.writeFileSync(apiKeyPath, "");
63-
}
64-
const encryptedText = crypto.encrypt("test");
65-
crypto.saveApiKey(encryptedText);
66-
const result = crypto.getApiKey()// fs.readFileSync(apiKeyPath, "utf8");
67-
68-
expect(result).to.equal("test");
69-
});
64+
it("should encrypt the API key when saving", () => {
65+
saveCredentials("testApiKey", "testEngine", "testTavilyApiKey");
66+
const rawData = fs.readFileSync(credentialsPath, "utf-8");
67+
const savedCredentials = JSON.parse(rawData);
68+
expect(savedCredentials.apiKey).to.not.equal("testApiKey");
69+
expect(decrypt(savedCredentials.apiKey)).to.equal("testApiKey");
70+
});
7071
});

__tests__/utils.spec.ts

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,35 @@ import * as path from "path";
22

33
import fs from "fs";
44

5-
import {expect} from "chai";
5+
import { expect } from "chai";
66

7+
import { apiKeyPrompt /*, generateResponse*/ } from "../src/utils";
78

8-
import {apiKeyPrompt/*, generateResponse*/} from "../src/utils";
9-
10-
import {encrypt} from "../src/encrypt";
11-
9+
import { encrypt } from "../src/creds";
1210

1311
describe("apiKeyPrompt()", () => {
14-
const apiKeyPath = path.resolve(__dirname, "../src/apiKey.txt");
12+
const credentialsPath = path.resolve(__dirname, "../src/credentials.json");
1513

16-
beforeEach(() => {
17-
if (!fs.existsSync(apiKeyPath)) {
18-
fs.writeFileSync(apiKeyPath, encrypt("test"));
19-
}
20-
});
14+
beforeEach(() => {
15+
if (!fs.existsSync(credentialsPath)) {
16+
fs.writeFileSync(
17+
credentialsPath,
18+
JSON.stringify({
19+
apiKey: encrypt("test"),
20+
engine: "test",
21+
tavilyApiKey: encrypt("test"),
22+
})
23+
);
24+
}
25+
});
2126

22-
it("the api key prompt to user should return a string", async () => {
23-
const result = await apiKeyPrompt()
24-
expect(result).to.be.a("string")
25-
})
26-
})
27+
it("the api key prompt to user should return an object with apiKey and engine", async () => {
28+
const result = await apiKeyPrompt();
29+
expect(result).to.be.an("object");
30+
expect(result).to.have.property("apiKey");
31+
expect(result).to.have.property("engine");
32+
});
33+
});
2734

2835
// FIXME cannot be executed without a real api key
2936
// describe("generateResponse()", () => {
@@ -66,25 +73,25 @@ describe("apiKeyPrompt()", () => {
6673
// });
6774
// });
6875

69-
// it("Should create a instance of Configuration", async () => {
70-
// const
71-
// apiKey = "abx",
72-
// prompt = jest.fn(),
73-
// options = {},
74-
// response = {
75-
// data: {
76-
// choices: ["abc", "def"],
77-
// },
78-
// }
79-
//
80-
// const getResponse = await generateResponse(
81-
// apiKey,
82-
// prompt,
83-
// options,
84-
// response
85-
// )
86-
//
87-
// // expect(getResponse).throw(Error)
88-
// expect(getResponse).to.be.a("string");
89-
// });
76+
// it("Should create a instance of Configuration", async () => {
77+
// const
78+
// apiKey = "abx",
79+
// prompt = jest.fn(),
80+
// options = {},
81+
// response = {
82+
// data: {
83+
// choices: ["abc", "def"],
84+
// },
85+
// }
86+
//
87+
// const getResponse = await generateResponse(
88+
// apiKey,
89+
// prompt,
90+
// options,
91+
// response
92+
// )
93+
//
94+
// // expect(getResponse).throw(Error)
95+
// expect(getResponse).to.be.a("string");
96+
// });
9097
// });

bun.lockb

234 KB
Binary file not shown.

package.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
{
22
"name": "terminalgpt",
3-
"version": "1.7.4",
3+
"version": "1.7.5",
44
"main": "lib/index.js",
55
"description": "Get GPT like chatGPT on your terminal",
66
"scripts": {
77
"tgpt": "node lib/index.js",
88
"test": "jest --ci --coverage --verbose",
9-
"dev": "ts-node src/index.ts chat --engine gpt-4 --temperature 0.7",
10-
"dev:markdown": "ts-node src/index.ts chat --engine gpt-4 --temperature 0.7 --markdown",
11-
"tunne": "ts-node src/index.ts chat --engine gpt-4 --temperature 0.7 --finetunning true --limit 1",
9+
"dev": "ts-node src/index.ts chat --temperature 0.7",
1210
"dev:delete": "ts-node src/index.ts delete",
1311
"postinstall": "tsc"
1412
},
@@ -32,16 +30,21 @@
3230
}
3331
},
3432
"dependencies": {
33+
"@anthropic-ai/sdk": "^0.27.2",
34+
"@dqbd/tiktoken": "^1.0.16",
35+
"@google/generative-ai": "^0.17.1",
3536
"@types/gradient-string": "^1.1.5",
3637
"@types/marked": "^6.0.0",
3738
"@types/marked-terminal": "^6.0.1",
3839
"@types/node": "^16.0.0",
3940
"@types/prompts": "^2.4.8",
41+
"axios": "^1.7.7",
4042
"chalk": "^4.1.2",
4143
"clipboardy": "2.3.0",
4244
"commander": "^9.5.0",
4345
"compromise": "^14.8.1",
4446
"gradient-string": "^2.0.2",
47+
"hnswlib-node": "^3.0.0",
4548
"lowdb": "^5.1.0",
4649
"markdown": "^0.5.0",
4750
"marked": "^9.1.6",
@@ -66,5 +69,6 @@
6669
"ts-jest": "^29.1.1",
6770
"ts-node": "^10.9.1",
6871
"typescript": "^5.1.6"
69-
}
72+
},
73+
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
7074
}

src/commands/autossugestion.ts

Whitespace-only changes.

src/commands/clear.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const clearFunc = () => {
2+
process.stdout.write("\x1Bc");
3+
};
4+
5+
export default clearFunc;

src/commands/exit.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import chalk from "chalk";
2+
3+
const exitFunc = () => {
4+
console.log(chalk.yellow("Goodbye!"));
5+
process.exit(0);
6+
};
7+
8+
export default exitFunc;

src/commands/file.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import chalk from "chalk";
2+
import { handleFileReference } from "../handlers/fileHandler"; // Assuming this function exists
3+
import { apiKeyPrompt, promptResponse } from "../utils"; // Assuming this function exists
4+
5+
const fileFunc = async (userInput: string) => {
6+
const creds = await apiKeyPrompt();
7+
// we need to call file handler here
8+
const [, filePath, ...promptParts] = userInput.split(" ");
9+
const promptText = promptParts.join(" ");
10+
if (filePath) {
11+
await handleFileReference(filePath, promptText);
12+
if (creds.apiKey != null) {
13+
await promptResponse(creds.engine, creds.apiKey, userInput, {});
14+
}
15+
} else {
16+
console.log(
17+
chalk.yellow("Please provide a file path. Usage: @file <path> [prompt]")
18+
);
19+
}
20+
};
21+
22+
export default fileFunc;

src/commands/index.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import exitFunc from "./exit";
2+
import clearFunc from "./clear";
3+
import fileFunc from "./file";
4+
import webFunc from "./web";
5+
6+
export interface Plugin {
7+
keyword: string;
8+
execute: (userInput: string) => Promise<void> | void;
9+
}
10+
11+
const plugins: Plugin[] = [];
12+
13+
const registerPlugin = (
14+
keyword: string,
15+
execute: (userInput: string) => Promise<void> | void
16+
) => {
17+
plugins.push({ keyword, execute });
18+
};
19+
20+
export const mapPlugins = (userInput: string): Plugin | undefined => {
21+
return plugins.find((plugin) => userInput.startsWith(plugin.keyword));
22+
};
23+
24+
export const initializePlugins = () => {
25+
// Register your plugins here
26+
registerPlugin("exit", exitFunc);
27+
registerPlugin("clear", clearFunc);
28+
registerPlugin("@file", fileFunc);
29+
registerPlugin("@web", webFunc);
30+
};
31+
32+
export const executeCommand = (userInput: string): boolean => {
33+
const command = plugins.find((plugin) =>
34+
userInput.startsWith(plugin.keyword)
35+
);
36+
if (command) {
37+
command.execute(userInput);
38+
return true;
39+
}
40+
return false;
41+
};
42+
43+
export default executeCommand;

0 commit comments

Comments
 (0)