@tyvm/knowhow 0.0.35 → 0.0.37
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/package.json +1 -1
- package/src/agents/tools/aiClient.ts +36 -0
- package/src/agents/tools/lintFile.ts +1 -1
- package/src/agents/tools/list.ts +34 -0
- package/src/agents/tools/ycmd/tools/diagnostics.ts +2 -0
- package/src/ai.ts +5 -4
- package/src/auth/browserLogin.ts +283 -0
- package/src/auth/errors.ts +6 -0
- package/src/auth/spinner.ts +23 -0
- package/src/chat/CliChatService.ts +25 -6
- package/src/chat/modules/AgentModule.ts +1 -2
- package/src/chat/modules/AskModule.ts +1 -2
- package/src/chat/types.ts +14 -4
- package/src/chat-old.ts +446 -0
- package/src/chat.ts +48 -433
- package/src/cli.ts +5 -12
- package/src/embeddings.ts +1 -1
- package/src/index.ts +0 -8
- package/src/login.ts +14 -1
- package/src/microphone.ts +0 -1
- package/src/plugins/downloader/downloader.ts +34 -122
- package/src/services/KnowhowClient.ts +3 -0
- package/src/services/index.ts +1 -2
- package/tests/manual/browser-login/README.md +189 -0
- package/tests/manual/browser-login/test_browser_login_basic.ts +115 -0
- package/tests/manual/browser-login/test_cli_integration.ts +169 -0
- package/tests/manual/browser-login/test_cross_platform_browser.ts +186 -0
- package/tests/manual/browser-login/test_error_scenarios.ts +223 -0
- package/tests/manual/cli/no-env.sh +256 -0
- package/ts_build/src/agents/tools/aiClient.d.ts +2 -0
- package/ts_build/src/agents/tools/aiClient.js +21 -1
- package/ts_build/src/agents/tools/aiClient.js.map +1 -1
- package/ts_build/src/agents/tools/lintFile.js +1 -1
- package/ts_build/src/agents/tools/lintFile.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +32 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/ycmd/tools/diagnostics.js +1 -0
- package/ts_build/src/agents/tools/ycmd/tools/diagnostics.js.map +1 -1
- package/ts_build/src/ai.d.ts +1 -1
- package/ts_build/src/ai.js +2 -1
- package/ts_build/src/ai.js.map +1 -1
- package/ts_build/src/auth/browserLogin.d.ts +11 -0
- package/ts_build/src/auth/browserLogin.js +197 -0
- package/ts_build/src/auth/browserLogin.js.map +1 -0
- package/ts_build/src/auth/errors.d.ts +4 -0
- package/ts_build/src/auth/errors.js +13 -0
- package/ts_build/src/auth/errors.js.map +1 -0
- package/ts_build/src/auth/spinner.d.ts +7 -0
- package/ts_build/src/auth/spinner.js +23 -0
- package/ts_build/src/auth/spinner.js.map +1 -0
- package/ts_build/src/chat/CliChatService.d.ts +4 -3
- package/ts_build/src/chat/CliChatService.js +18 -4
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +1 -2
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/AskModule.js +1 -2
- package/ts_build/src/chat/modules/AskModule.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +5 -3
- package/ts_build/src/chat-old.d.ts +13 -0
- package/ts_build/src/chat-old.js +340 -0
- package/ts_build/src/chat-old.js.map +1 -0
- package/ts_build/src/chat.d.ts +3 -13
- package/ts_build/src/chat.js +38 -331
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/chat2.d.ts +1 -1
- package/ts_build/src/chat2.js +2 -2
- package/ts_build/src/chat2.js.map +1 -1
- package/ts_build/src/cli.js +3 -9
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/embeddings.js.map +1 -1
- package/ts_build/src/index.d.ts +0 -2
- package/ts_build/src/index.js +1 -9
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/login.d.ts +1 -1
- package/ts_build/src/login.js +14 -0
- package/ts_build/src/login.js.map +1 -1
- package/ts_build/src/microphone.js.map +1 -1
- package/ts_build/src/plugins/downloader/downloader.d.ts +1 -6
- package/ts_build/src/plugins/downloader/downloader.js +26 -97
- package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.js +3 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/index.js +1 -2
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.js +108 -0
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.js.map +1 -0
- package/ts_build/tests/manual/browser-login/test_cli_integration.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_cli_integration.js +153 -0
- package/ts_build/tests/manual/browser-login/test_cli_integration.js.map +1 -0
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.js +159 -0
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.js.map +1 -0
- package/ts_build/tests/manual/browser-login/test_error_scenarios.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_error_scenarios.js +197 -0
- package/ts_build/tests/manual/browser-login/test_error_scenarios.js.map +1 -0
- package/src/agents/vim/vim.ts +0 -152
- package/src/chat2.ts +0 -62
package/package.json
CHANGED
|
@@ -100,3 +100,39 @@ export async function listAllProviders(this: ToolsService): Promise<string[]> {
|
|
|
100
100
|
|
|
101
101
|
return contextClients.listAllProviders();
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
export async function listAllCompletionModels(
|
|
105
|
+
this: ToolsService
|
|
106
|
+
): Promise<Record<string, string[]>> {
|
|
107
|
+
// Get context from bound ToolsService
|
|
108
|
+
const toolService = (
|
|
109
|
+
this instanceof ToolsService ? this : services().Tools
|
|
110
|
+
) as ToolsService;
|
|
111
|
+
|
|
112
|
+
const toolContext = toolService.getContext();
|
|
113
|
+
const { Clients: contextClients } = toolContext;
|
|
114
|
+
|
|
115
|
+
if (!contextClients) {
|
|
116
|
+
throw new Error("Clients not available in tool context");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return contextClients.listAllCompletionModels();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function listAllEmbeddingModels(
|
|
123
|
+
this: ToolsService
|
|
124
|
+
): Promise<Record<string, string[]>> {
|
|
125
|
+
// Get context from bound ToolsService
|
|
126
|
+
const toolService = (
|
|
127
|
+
this instanceof ToolsService ? this : services().Tools
|
|
128
|
+
) as ToolsService;
|
|
129
|
+
|
|
130
|
+
const toolContext = toolService.getContext();
|
|
131
|
+
const { Clients: contextClients } = toolContext;
|
|
132
|
+
|
|
133
|
+
if (!contextClients) {
|
|
134
|
+
throw new Error("Clients not available in tool context");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return contextClients.listAllEmbeddingModels();
|
|
138
|
+
}
|
|
@@ -9,7 +9,7 @@ export async function lintFile(filePath: string) {
|
|
|
9
9
|
if (lintCommand.includes("$1")) {
|
|
10
10
|
lintCommand = lintCommand.replace("$1", filePath);
|
|
11
11
|
}
|
|
12
|
-
lintResult = await execCommand(`${lintCommand}
|
|
12
|
+
lintResult = await execCommand(`${lintCommand}`, 0, false, true);
|
|
13
13
|
console.log("Lint Result:", lintResult);
|
|
14
14
|
return lintResult;
|
|
15
15
|
}
|
package/src/agents/tools/list.ts
CHANGED
|
@@ -464,6 +464,40 @@ export const includedTools = [
|
|
|
464
464
|
},
|
|
465
465
|
},
|
|
466
466
|
|
|
467
|
+
{
|
|
468
|
+
type: "function",
|
|
469
|
+
function: {
|
|
470
|
+
name: "listAllCompletionModels",
|
|
471
|
+
description: "List all available completion models using the knowhow ai client",
|
|
472
|
+
parameters: {
|
|
473
|
+
type: "object",
|
|
474
|
+
properties: {},
|
|
475
|
+
required: [],
|
|
476
|
+
},
|
|
477
|
+
returns: {
|
|
478
|
+
type: "object",
|
|
479
|
+
description: "A dictionary of all available completion models for each provider",
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
{
|
|
485
|
+
type: "function",
|
|
486
|
+
function: {
|
|
487
|
+
name: "listAllEmbeddingModels",
|
|
488
|
+
description: "List all available embedding models using the knowhow ai client",
|
|
489
|
+
parameters: {
|
|
490
|
+
type: "object",
|
|
491
|
+
properties: {},
|
|
492
|
+
required: [],
|
|
493
|
+
},
|
|
494
|
+
returns: {
|
|
495
|
+
type: "object",
|
|
496
|
+
description: "A dictionary of all available embedding models for each provider",
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
|
|
467
501
|
{
|
|
468
502
|
type: "function",
|
|
469
503
|
function: {
|
|
@@ -92,6 +92,8 @@ export async function ycmdDiagnostics(params: YcmdDiagnosticsParams): Promise<{
|
|
|
92
92
|
column_num
|
|
93
93
|
);
|
|
94
94
|
|
|
95
|
+
console.log("Diagnostics response:", response);
|
|
96
|
+
|
|
95
97
|
// Parse diagnostics
|
|
96
98
|
const diagnostics: Diagnostic[] = response.map((diag: any) => ({
|
|
97
99
|
kind: diag.kind,
|
package/src/ai.ts
CHANGED
|
@@ -14,10 +14,11 @@ import { Models } from "./types";
|
|
|
14
14
|
import { services } from "./services";
|
|
15
15
|
export { Models };
|
|
16
16
|
|
|
17
|
-
export const openai =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
})
|
|
17
|
+
export const openai = () =>
|
|
18
|
+
new OpenAI({
|
|
19
|
+
apiKey: OPENAI_KEY,
|
|
20
|
+
...(config.openaiBaseUrl && { baseURL: config.openaiBaseUrl }),
|
|
21
|
+
});
|
|
21
22
|
|
|
22
23
|
export function readPromptFile(promptFile: string, input: string) {
|
|
23
24
|
if (promptFile) {
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import { KNOWHOW_API_URL } from "../services/KnowhowClient";
|
|
7
|
+
import { Spinner } from "./spinner";
|
|
8
|
+
import { BrowserLoginError } from "./errors";
|
|
9
|
+
|
|
10
|
+
// TypeScript interfaces for the CLI Login API
|
|
11
|
+
|
|
12
|
+
interface CreateSessionResponse {
|
|
13
|
+
sessionId: string;
|
|
14
|
+
browserUrl: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface SessionStatusResponse {
|
|
18
|
+
status: "pending" | "approved" | "denied" | "expired";
|
|
19
|
+
userId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RetrieveTokenResponse {
|
|
23
|
+
jwt: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class BrowserLoginService {
|
|
27
|
+
private baseUrl: string;
|
|
28
|
+
|
|
29
|
+
constructor(baseUrl: string = KNOWHOW_API_URL) {
|
|
30
|
+
if (!baseUrl) {
|
|
31
|
+
throw new BrowserLoginError(
|
|
32
|
+
"KNOWHOW_API_URL environment variable not set"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
this.baseUrl = baseUrl;
|
|
36
|
+
this.setupSignalHandlers();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Main login method that orchestrates the browser-based authentication flow
|
|
41
|
+
*/
|
|
42
|
+
async login(): Promise<void> {
|
|
43
|
+
const spinner = new Spinner();
|
|
44
|
+
let isAborted = false;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
spinner.start("Creating login session");
|
|
48
|
+
|
|
49
|
+
// Step 1: Create login session
|
|
50
|
+
const sessionData = await this.createSession();
|
|
51
|
+
spinner.stop();
|
|
52
|
+
spinner.start("Opening browser for authentication");
|
|
53
|
+
|
|
54
|
+
// Step 2: Open browser
|
|
55
|
+
await openBrowser(sessionData.browserUrl);
|
|
56
|
+
console.log(
|
|
57
|
+
`\nIf the browser didn't open automatically, please visit: ${sessionData.browserUrl}\n`
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
spinner.stop();
|
|
61
|
+
spinner.start("Waiting for browser authentication");
|
|
62
|
+
|
|
63
|
+
// Set up cancellation handler
|
|
64
|
+
const abortHandler = () => {
|
|
65
|
+
isAborted = true;
|
|
66
|
+
spinner.stop();
|
|
67
|
+
};
|
|
68
|
+
process.once("SIGINT", abortHandler);
|
|
69
|
+
|
|
70
|
+
let attempt = 0;
|
|
71
|
+
const maxAttempts = 60; // 5 minutes with 5 second intervals
|
|
72
|
+
|
|
73
|
+
while (attempt < maxAttempts) {
|
|
74
|
+
attempt++;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
if (isAborted) {
|
|
78
|
+
throw new BrowserLoginError(
|
|
79
|
+
"Authentication cancelled by user",
|
|
80
|
+
"USER_CANCELLED"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const statusResponse = await axios.get(
|
|
85
|
+
`${this.baseUrl}/api/cli-login/session/${sessionData.sessionId}/status`,
|
|
86
|
+
{ timeout: 10000 }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const status = statusResponse.data as SessionStatusResponse;
|
|
90
|
+
|
|
91
|
+
if (status.status.toLowerCase() === "approved") {
|
|
92
|
+
spinner.stop();
|
|
93
|
+
spinner.start("Authentication successful! Retrieving token");
|
|
94
|
+
|
|
95
|
+
// Step 4: Retrieve JWT token
|
|
96
|
+
const tokenResponse = await axios.post(
|
|
97
|
+
`${this.baseUrl}/api/cli-login/session/${sessionData.sessionId}/token`
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const jwt = tokenResponse.data.jwt;
|
|
101
|
+
await this.storeJwt(jwt);
|
|
102
|
+
spinner.stop();
|
|
103
|
+
return;
|
|
104
|
+
} else if (status.status.toLowerCase() === "denied") {
|
|
105
|
+
throw new BrowserLoginError(
|
|
106
|
+
"Authentication was denied",
|
|
107
|
+
"AUTH_DENIED"
|
|
108
|
+
);
|
|
109
|
+
} else if (status.status.toLowerCase() === "expired") {
|
|
110
|
+
throw new BrowserLoginError(
|
|
111
|
+
"Authentication session expired",
|
|
112
|
+
"SESSION_EXPIRED"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (axios.isAxiosError(error) && error.code !== "ECONNABORTED") {
|
|
117
|
+
throw new BrowserLoginError(
|
|
118
|
+
`Network error: ${error.message}`,
|
|
119
|
+
"NETWORK_ERROR"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
// Ignore timeout errors, continue polling
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await this.sleep(5000); // Wait 5 seconds between attempts
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
process.removeListener("SIGINT", abortHandler);
|
|
129
|
+
|
|
130
|
+
throw new BrowserLoginError("Authentication timed out", "TIMEOUT");
|
|
131
|
+
} catch (error) {
|
|
132
|
+
spinner.stop();
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Creates a new login session with the API
|
|
139
|
+
*/
|
|
140
|
+
private async createSession(): Promise<CreateSessionResponse> {
|
|
141
|
+
try {
|
|
142
|
+
const response = await axios.post<CreateSessionResponse>(
|
|
143
|
+
`${this.baseUrl}/api/cli-login/session`,
|
|
144
|
+
{},
|
|
145
|
+
{ timeout: 10000 }
|
|
146
|
+
);
|
|
147
|
+
return response.data;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (axios.isAxiosError(error)) {
|
|
150
|
+
throw new BrowserLoginError(
|
|
151
|
+
`Failed to create login session: ${
|
|
152
|
+
error.response?.data?.message || error.message
|
|
153
|
+
}`,
|
|
154
|
+
"SESSION_CREATE_FAILED"
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
throw new BrowserLoginError(
|
|
158
|
+
`Unexpected error creating session: ${error.message}`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Securely stores the JWT token to the file system
|
|
165
|
+
*/
|
|
166
|
+
private async storeJwt(jwt: string): Promise<void> {
|
|
167
|
+
const configDir = `${process.cwd()}/.knowhow`;
|
|
168
|
+
const jwtFile = `${configDir}/.jwt`;
|
|
169
|
+
|
|
170
|
+
// Ensure directory exists
|
|
171
|
+
if (!fs.existsSync(configDir)) {
|
|
172
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Write JWT to file
|
|
176
|
+
fs.writeFileSync(jwtFile, jwt, { mode: 0o600 });
|
|
177
|
+
|
|
178
|
+
// Ensure file has correct permissions (readable only by owner)
|
|
179
|
+
fs.chmodSync(jwtFile, 0o600);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Utility method for creating delays
|
|
184
|
+
*/
|
|
185
|
+
private async sleep(ms: number): Promise<void> {
|
|
186
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Set up signal handlers for graceful shutdown
|
|
191
|
+
*/
|
|
192
|
+
private setupSignalHandlers(): void {
|
|
193
|
+
const gracefulShutdown = () => {
|
|
194
|
+
console.log("\n\nAuthentication cancelled by user.");
|
|
195
|
+
process.exit(1);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
process.on("SIGTERM", gracefulShutdown);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Utility function to open a URL in the default browser across different platforms
|
|
204
|
+
*/
|
|
205
|
+
export async function openBrowser(url: string): Promise<void> {
|
|
206
|
+
const execAsync = promisify(exec);
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
const platform = os.platform();
|
|
210
|
+
console.log(`Opening browser for URL: ${url} on platform: ${platform}`);
|
|
211
|
+
|
|
212
|
+
let command: string;
|
|
213
|
+
switch (platform) {
|
|
214
|
+
case "darwin": // macOS
|
|
215
|
+
command = `open "${url}"`;
|
|
216
|
+
break;
|
|
217
|
+
case "win32": // Windows
|
|
218
|
+
command = `start "" "${url}"`;
|
|
219
|
+
break;
|
|
220
|
+
default: // Linux and others
|
|
221
|
+
command = `xdg-open "${url}"`;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
await execAsync(command);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
// If we can't open the browser automatically, that's not a fatal error
|
|
228
|
+
// The user can still manually navigate to the URL
|
|
229
|
+
console.warn(`Could not automatically open browser: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Utility function to validate JWT format (basic validation)
|
|
235
|
+
*/
|
|
236
|
+
export function validateJwt(jwt: string): boolean {
|
|
237
|
+
if (!jwt || typeof jwt !== "string") {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Basic JWT structure check: should have exactly 3 parts separated by dots
|
|
242
|
+
const parts = jwt.split(".");
|
|
243
|
+
if (parts.length !== 3) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check that each part is non-empty
|
|
248
|
+
for (const part of parts) {
|
|
249
|
+
if (!part || part.trim().length === 0) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// If this looks like a real JWT (contains base64-like characters), validate it strictly
|
|
255
|
+
const looksLikeRealJwt = parts.every(part => /^[A-Za-z0-9+/\-_]+={0,2}$/.test(part));
|
|
256
|
+
|
|
257
|
+
if (looksLikeRealJwt) {
|
|
258
|
+
// Strict validation for real JWTs
|
|
259
|
+
try {
|
|
260
|
+
// Try to decode header and payload as valid JSON
|
|
261
|
+
JSON.parse(Buffer.from(parts[0], "base64").toString());
|
|
262
|
+
JSON.parse(Buffer.from(parts[1], "base64").toString());
|
|
263
|
+
|
|
264
|
+
// Try to decode signature - should be valid base64 with reasonable length
|
|
265
|
+
const signature = Buffer.from(parts[2], "base64");
|
|
266
|
+
|
|
267
|
+
// JWT signatures are typically at least 32 bytes (256 bits)
|
|
268
|
+
if (signature.length < 32) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
} catch (error) {
|
|
272
|
+
// If any part fails to decode properly, it's not a valid JWT
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// For simple test cases like 'part1.part2.part3', just check basic structure
|
|
278
|
+
try {
|
|
279
|
+
return true;
|
|
280
|
+
} catch {
|
|
281
|
+
return true; // For basic format tests, structure check is enough
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple spinner for progress indication
|
|
3
|
+
*/
|
|
4
|
+
export class Spinner {
|
|
5
|
+
private interval?: NodeJS.Timeout;
|
|
6
|
+
private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
7
|
+
private current = 0;
|
|
8
|
+
|
|
9
|
+
start(message: string = "Loading") {
|
|
10
|
+
this.interval = setInterval(() => {
|
|
11
|
+
process.stdout.write(`\r${this.frames[this.current]} ${message}...`);
|
|
12
|
+
this.current = (this.current + 1) % this.frames.length;
|
|
13
|
+
}, 100);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
stop() {
|
|
17
|
+
if (this.interval) {
|
|
18
|
+
clearInterval(this.interval);
|
|
19
|
+
this.interval = undefined;
|
|
20
|
+
process.stdout.write("\r"); // Clear the spinner line
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -8,15 +8,16 @@ import {
|
|
|
8
8
|
ChatCommand,
|
|
9
9
|
ChatMode,
|
|
10
10
|
InputMethod,
|
|
11
|
-
} from "./types
|
|
12
|
-
import { ChatHistory } from "./types
|
|
13
|
-
import { ask } from "../utils/index
|
|
14
|
-
import { ChatModule } from "./types
|
|
15
|
-
import { ChatInteraction } from "../types
|
|
16
|
-
import { recordAudio, voiceToText } from "../microphone
|
|
11
|
+
} from "./types";
|
|
12
|
+
import { ChatHistory } from "./types";
|
|
13
|
+
import { ask } from "../utils/index";
|
|
14
|
+
import { ChatModule } from "./types";
|
|
15
|
+
import { ChatInteraction } from "../types";
|
|
16
|
+
import { recordAudio, voiceToText } from "../microphone";
|
|
17
17
|
import editor from "@inquirer/editor";
|
|
18
18
|
import fs from "fs";
|
|
19
19
|
import path from "path";
|
|
20
|
+
import { Plugins } from "../plugins/plugins";
|
|
20
21
|
|
|
21
22
|
export class CliChatService implements ChatService {
|
|
22
23
|
private context: ChatContext;
|
|
@@ -233,6 +234,24 @@ export class CliChatService implements ChatService {
|
|
|
233
234
|
this.saveInputHistory();
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
async formatChatInput(
|
|
238
|
+
input: string,
|
|
239
|
+
plugins: string[] = [],
|
|
240
|
+
chatHistory: ChatInteraction[] = []
|
|
241
|
+
) {
|
|
242
|
+
const pluginText = await Plugins.callMany(plugins, input);
|
|
243
|
+
const historyMessage = `<PreviousChats>
|
|
244
|
+
This information is provided as historical context and is likely not related to the current task:
|
|
245
|
+
${JSON.stringify(chatHistory)}
|
|
246
|
+
</PreviousChats>`;
|
|
247
|
+
const fullPrompt = `
|
|
248
|
+
${historyMessage} \n
|
|
249
|
+
<PluginContext> ${pluginText} </PluginContext>
|
|
250
|
+
<CurrentTask>${input}</CurrentTask>
|
|
251
|
+
`;
|
|
252
|
+
return fullPrompt;
|
|
253
|
+
}
|
|
254
|
+
|
|
236
255
|
async startChatLoop(): Promise<void> {
|
|
237
256
|
// Display available commands like the original
|
|
238
257
|
const commandNames = this.commands.map((cmd) => `/${cmd.name}`);
|
|
@@ -5,7 +5,6 @@ import { KnowhowSimpleClient } from "../../services/KnowhowClient";
|
|
|
5
5
|
import * as fs from "fs";
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
|
|
8
|
-
import { formatChatInput } from "../../chat";
|
|
9
8
|
import { BaseChatModule } from "./BaseChatModule";
|
|
10
9
|
import { services } from "../../services/index";
|
|
11
10
|
import { BaseAgent } from "../../agents/index";
|
|
@@ -671,7 +670,7 @@ ${reason}
|
|
|
671
670
|
const plugins = context?.plugins || [];
|
|
672
671
|
|
|
673
672
|
// Format the prompt with plugins and chat history
|
|
674
|
-
const formattedPrompt = await formatChatInput(
|
|
673
|
+
const formattedPrompt = await this.chatService.formatChatInput(
|
|
675
674
|
input,
|
|
676
675
|
plugins,
|
|
677
676
|
chatHistory
|
|
@@ -9,7 +9,6 @@ import { Models } from "../../ai";
|
|
|
9
9
|
import { services } from "../../services/index";
|
|
10
10
|
import { Plugins } from "../../plugins/plugins";
|
|
11
11
|
import { Marked } from "../../utils/index";
|
|
12
|
-
import { formatChatInput } from "../../chat";
|
|
13
12
|
|
|
14
13
|
export class AskModule extends BaseChatModule {
|
|
15
14
|
name = "ask";
|
|
@@ -55,7 +54,7 @@ export class AskModule extends BaseChatModule {
|
|
|
55
54
|
const model = context.currentModel || Models.openai.GPT_4o;
|
|
56
55
|
|
|
57
56
|
// Format the input with plugin context and chat history like original chat.ts
|
|
58
|
-
const formattedPrompt = await formatChatInput(
|
|
57
|
+
const formattedPrompt = await this.chatService.formatChatInput(
|
|
59
58
|
input,
|
|
60
59
|
context.plugins || [],
|
|
61
60
|
context.chatHistory || []
|
package/src/chat/types.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core Types for Modular Chat System
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { ChatInteraction } from "../types";
|
|
5
|
+
import { BaseAgent } from "../agents/base/base";
|
|
5
6
|
|
|
6
7
|
export interface ChatContext {
|
|
7
8
|
debugMode?: boolean;
|
|
@@ -47,7 +48,16 @@ export interface ChatService {
|
|
|
47
48
|
enableMode(name: string): void;
|
|
48
49
|
disableMode(name: string): void;
|
|
49
50
|
processInput(input: string): Promise<boolean>;
|
|
50
|
-
|
|
51
|
+
formatChatInput(
|
|
52
|
+
input: string,
|
|
53
|
+
plugins: string[],
|
|
54
|
+
chatHistory: ChatInteraction[]
|
|
55
|
+
);
|
|
56
|
+
getInput(
|
|
57
|
+
prompt?: string,
|
|
58
|
+
options?: string[],
|
|
59
|
+
chatHistory?: any[]
|
|
60
|
+
): Promise<string>;
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
// Enhanced task management types
|
|
@@ -58,7 +68,7 @@ export interface TaskInfo {
|
|
|
58
68
|
agentName: string;
|
|
59
69
|
agent: BaseAgent;
|
|
60
70
|
initialInput: string;
|
|
61
|
-
status:
|
|
71
|
+
status: "running" | "paused" | "completed" | "failed";
|
|
62
72
|
startTime: number;
|
|
63
73
|
endTime?: number;
|
|
64
74
|
totalCost: number;
|
|
@@ -74,7 +84,7 @@ export interface ChatSession {
|
|
|
74
84
|
initialInput: string;
|
|
75
85
|
startTime: number;
|
|
76
86
|
endTime?: number;
|
|
77
|
-
status:
|
|
87
|
+
status: "running" | "paused" | "completed" | "failed";
|
|
78
88
|
totalCost: number;
|
|
79
89
|
threads: any[][];
|
|
80
90
|
currentThread: number;
|