caroushell 0.1.23 → 0.1.24
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/dist/app.js +21 -8
- package/dist/carousel.js +11 -1
- package/dist/config.js +0 -4
- package/dist/hello-new-user.js +17 -5
- package/dist/history-suggester.js +2 -1
- package/dist/main.js +7 -1
- package/dist/spawner.js +13 -4
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -5,7 +5,6 @@ const terminal_1 = require("./terminal");
|
|
|
5
5
|
const keyboard_1 = require("./keyboard");
|
|
6
6
|
const carousel_1 = require("./carousel");
|
|
7
7
|
const history_suggester_1 = require("./history-suggester");
|
|
8
|
-
const ai_suggester_1 = require("./ai-suggester");
|
|
9
8
|
const file_suggester_1 = require("./file-suggester");
|
|
10
9
|
const spawner_1 = require("./spawner");
|
|
11
10
|
const logs_1 = require("./logs");
|
|
@@ -18,14 +17,14 @@ class App {
|
|
|
18
17
|
this.terminal = deps.terminal ?? new terminal_1.Terminal();
|
|
19
18
|
this.keyboard = deps.keyboard ?? new keyboard_1.Keyboard();
|
|
20
19
|
this.history = deps.topPanel ?? new history_suggester_1.HistorySuggester();
|
|
21
|
-
this.
|
|
20
|
+
this.bottomSuggester = deps.bottomPanel ?? new carousel_1.NullSuggester();
|
|
22
21
|
this.files = deps.files ?? new file_suggester_1.FileSuggester();
|
|
23
|
-
this.suggesters = deps.suggesters ?? [this.history, this.
|
|
22
|
+
this.suggesters = deps.suggesters ?? [this.history, this.bottomSuggester, this.files];
|
|
24
23
|
this.carousel = new carousel_1.Carousel({
|
|
25
24
|
top: this.history,
|
|
26
|
-
bottom: this.
|
|
25
|
+
bottom: this.bottomSuggester,
|
|
27
26
|
topRows: 2,
|
|
28
|
-
bottomRows: 2,
|
|
27
|
+
bottomRows: this.bottomSuggester instanceof carousel_1.NullSuggester ? 0 : 2,
|
|
29
28
|
terminal: this.terminal,
|
|
30
29
|
});
|
|
31
30
|
this.queueUpdateSuggestions = () => {
|
|
@@ -137,9 +136,9 @@ class App {
|
|
|
137
136
|
};
|
|
138
137
|
}
|
|
139
138
|
async init() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
for (const s of this.suggesters) {
|
|
140
|
+
await s.init();
|
|
141
|
+
}
|
|
143
142
|
}
|
|
144
143
|
async run() {
|
|
145
144
|
await this.init();
|
|
@@ -185,6 +184,7 @@ class App {
|
|
|
185
184
|
this.terminal.write("\n");
|
|
186
185
|
this.keyboard.disableCapture();
|
|
187
186
|
this.terminal.disableWrites();
|
|
187
|
+
await this.preBroadcastCommand(cmd);
|
|
188
188
|
try {
|
|
189
189
|
const storeInHistory = await (0, spawner_1.runUserCommand)(cmd);
|
|
190
190
|
if (storeInHistory) {
|
|
@@ -291,6 +291,19 @@ class App {
|
|
|
291
291
|
this.usingFileSuggestions = true;
|
|
292
292
|
this.carousel.setTopSuggester(this.files);
|
|
293
293
|
}
|
|
294
|
+
async preBroadcastCommand(cmd) {
|
|
295
|
+
const listeners = this.suggesters
|
|
296
|
+
.map((suggester) => suggester.onCommandWillRun?.(cmd))
|
|
297
|
+
.filter(Boolean);
|
|
298
|
+
if (listeners.length === 0)
|
|
299
|
+
return;
|
|
300
|
+
try {
|
|
301
|
+
await Promise.all(listeners);
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
(0, logs_1.logLine)("suggester onCommandWillRun error: " + err?.message);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
294
307
|
async broadcastCommand(cmd) {
|
|
295
308
|
const listeners = this.suggesters
|
|
296
309
|
.map((suggester) => suggester.onCommandRan?.(cmd))
|
package/dist/carousel.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Carousel = void 0;
|
|
3
|
+
exports.Carousel = exports.NullSuggester = void 0;
|
|
4
4
|
exports.getDisplayWidth = getDisplayWidth;
|
|
5
5
|
const logs_1 = require("./logs");
|
|
6
6
|
const terminal_1 = require("./terminal");
|
|
@@ -57,6 +57,16 @@ function getDisplayWidth(text) {
|
|
|
57
57
|
}
|
|
58
58
|
return width;
|
|
59
59
|
}
|
|
60
|
+
class NullSuggester {
|
|
61
|
+
constructor() {
|
|
62
|
+
this.prefix = "";
|
|
63
|
+
}
|
|
64
|
+
async init() { }
|
|
65
|
+
async refreshSuggestions() { }
|
|
66
|
+
latest() { return []; }
|
|
67
|
+
descriptionForAi() { return ""; }
|
|
68
|
+
}
|
|
69
|
+
exports.NullSuggester = NullSuggester;
|
|
60
70
|
class Carousel {
|
|
61
71
|
constructor(opts) {
|
|
62
72
|
this.index = 0;
|
package/dist/config.js
CHANGED
|
@@ -88,7 +88,6 @@ async function doesConfigExist() {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
async function getConfig() {
|
|
91
|
-
const configPath = getConfigPath();
|
|
92
91
|
const raw = await readConfigFile();
|
|
93
92
|
const envApiKey = process.env.CAROUSHELL_API_KEY || process.env.GEMINI_API_KEY || undefined;
|
|
94
93
|
const envApiUrl = process.env.CAROUSHELL_API_URL || undefined;
|
|
@@ -107,9 +106,6 @@ async function getConfig() {
|
|
|
107
106
|
if (!resolved.model && geminiApiKey) {
|
|
108
107
|
resolved.model = GEMINI_DEFAULT_MODEL;
|
|
109
108
|
}
|
|
110
|
-
if (!resolved.apiUrl || !resolved.apiKey || !resolved.model) {
|
|
111
|
-
throw new Error(`Config at ${configPath} is missing required fields. Please include apiUrl, apiKey, and model (or just GEMINI_API_KEY).`);
|
|
112
|
-
}
|
|
113
109
|
return resolved;
|
|
114
110
|
}
|
|
115
111
|
function isGeminiUrl(url) {
|
package/dist/hello-new-user.js
CHANGED
|
@@ -70,7 +70,23 @@ async function runHelloNewUserFlow(configPath) {
|
|
|
70
70
|
await fs_1.promises.mkdir(dir, { recursive: true });
|
|
71
71
|
console.log("");
|
|
72
72
|
console.log("Welcome to Caroushell!");
|
|
73
|
-
console.log(
|
|
73
|
+
console.log("");
|
|
74
|
+
const rl = readline_1.default.createInterface({
|
|
75
|
+
input: process.stdin,
|
|
76
|
+
output: process.stdout,
|
|
77
|
+
});
|
|
78
|
+
const wantsAi = (await prompt("Do you want to set up AI auto-complete? (y/n): ", rl))
|
|
79
|
+
.trim()
|
|
80
|
+
.toLowerCase();
|
|
81
|
+
if (wantsAi !== "y" && wantsAi !== "yes") {
|
|
82
|
+
rl.close();
|
|
83
|
+
// Writing noAi or any key will skip the new user flow next run
|
|
84
|
+
await fs_1.promises.writeFile(configPath, "noAi = true\n", "utf8");
|
|
85
|
+
console.log("\nSkipping AI setup. You can set it up later by editing " + configPath);
|
|
86
|
+
console.log("");
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
console.log(`\nLet's set up AI suggestions. You'll need an API endpoint URL, a key, and model id. These will be stored at ${configPath}`);
|
|
74
90
|
console.log("");
|
|
75
91
|
console.log("Some example endpoints you can paste:");
|
|
76
92
|
console.log(" - OpenRouter: https://openrouter.ai/api/v1");
|
|
@@ -78,10 +94,6 @@ async function runHelloNewUserFlow(configPath) {
|
|
|
78
94
|
console.log(" - Google: https://generativelanguage.googleapis.com/v1beta/openai");
|
|
79
95
|
console.log("");
|
|
80
96
|
console.log("Press Ctrl+C any time to abort.\n");
|
|
81
|
-
const rl = readline_1.default.createInterface({
|
|
82
|
-
input: process.stdin,
|
|
83
|
-
output: process.stdout,
|
|
84
|
-
});
|
|
85
97
|
let apiUrl = "";
|
|
86
98
|
while (!apiUrl) {
|
|
87
99
|
const answer = (await prompt("API URL: ", rl)).trim();
|
|
@@ -48,9 +48,10 @@ class HistorySuggester {
|
|
|
48
48
|
.catch(() => { });
|
|
49
49
|
await fs_1.promises.appendFile(this.filePath, this.serializeHistoryEntry(command), "utf8");
|
|
50
50
|
}
|
|
51
|
-
async
|
|
51
|
+
async onCommandWillRun(command) {
|
|
52
52
|
await this.add(command);
|
|
53
53
|
}
|
|
54
|
+
async onCommandRan(command) { }
|
|
54
55
|
latest() {
|
|
55
56
|
return this.filteredItems;
|
|
56
57
|
}
|
package/dist/main.js
CHANGED
|
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
const app_1 = require("./app");
|
|
7
|
+
const ai_suggester_1 = require("./ai-suggester");
|
|
8
|
+
const carousel_1 = require("./carousel");
|
|
7
9
|
const hello_new_user_1 = require("./hello-new-user");
|
|
8
10
|
const logs_1 = require("./logs");
|
|
9
11
|
const config_1 = require("./config");
|
|
@@ -25,7 +27,11 @@ async function main() {
|
|
|
25
27
|
if (!(await (0, config_1.doesConfigExist)())) {
|
|
26
28
|
await (0, hello_new_user_1.runHelloNewUserFlow)((0, config_1.getConfigPath)());
|
|
27
29
|
}
|
|
28
|
-
const
|
|
30
|
+
const config = await (0, config_1.getConfig)();
|
|
31
|
+
const bottomPanel = config.apiUrl && config.apiKey && config.model
|
|
32
|
+
? new ai_suggester_1.AISuggester()
|
|
33
|
+
: new carousel_1.NullSuggester();
|
|
34
|
+
const app = new app_1.App({ bottomPanel });
|
|
29
35
|
await app.run();
|
|
30
36
|
}
|
|
31
37
|
main().catch((err) => {
|
package/dist/spawner.js
CHANGED
|
@@ -144,10 +144,19 @@ async function runUserCommand(command) {
|
|
|
144
144
|
stdio: "inherit",
|
|
145
145
|
windowsVerbatimArguments: true,
|
|
146
146
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
// While a user command owns the terminal, Ctrl+C should interrupt that command
|
|
148
|
+
// without taking down the parent Caroushell process.
|
|
149
|
+
const ignoreSigint = () => { };
|
|
150
|
+
process.on("SIGINT", ignoreSigint);
|
|
151
|
+
try {
|
|
152
|
+
await new Promise((resolve, reject) => {
|
|
153
|
+
proc.on("error", reject);
|
|
154
|
+
proc.on("close", () => resolve());
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
process.off("SIGINT", ignoreSigint);
|
|
159
|
+
}
|
|
151
160
|
// Why save failed commands? Well eg sometimes we want to run a test
|
|
152
161
|
// many times until we fix it.
|
|
153
162
|
return true;
|