@jupyterlite/ai 0.9.1 → 0.11.0
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/README.md +5 -214
- package/lib/agent.d.ts +58 -66
- package/lib/agent.js +291 -310
- package/lib/approval-buttons.d.ts +19 -82
- package/lib/approval-buttons.js +36 -289
- package/lib/chat-model-registry.d.ts +6 -0
- package/lib/chat-model-registry.js +4 -1
- package/lib/chat-model.d.ts +26 -54
- package/lib/chat-model.js +277 -303
- package/lib/components/clear-button.d.ts +6 -1
- package/lib/components/clear-button.js +10 -6
- package/lib/components/completion-status.d.ts +5 -0
- package/lib/components/completion-status.js +5 -4
- package/lib/components/model-select.d.ts +6 -1
- package/lib/components/model-select.js +13 -16
- package/lib/components/stop-button.d.ts +6 -1
- package/lib/components/stop-button.js +12 -8
- package/lib/components/token-usage-display.d.ts +5 -0
- package/lib/components/token-usage-display.js +2 -2
- package/lib/components/tool-select.d.ts +6 -1
- package/lib/components/tool-select.js +10 -9
- package/lib/index.d.ts +1 -0
- package/lib/index.js +61 -81
- package/lib/models/settings-model.d.ts +1 -1
- package/lib/models/settings-model.js +40 -26
- package/lib/providers/built-in-providers.js +38 -19
- package/lib/providers/models.d.ts +3 -3
- package/lib/providers/provider-registry.d.ts +3 -4
- package/lib/providers/provider-registry.js +1 -4
- package/lib/tokens.d.ts +5 -6
- package/lib/tools/commands.d.ts +2 -1
- package/lib/tools/commands.js +36 -49
- package/lib/widgets/ai-settings.d.ts +6 -0
- package/lib/widgets/ai-settings.js +72 -71
- package/lib/widgets/main-area-chat.d.ts +2 -0
- package/lib/widgets/main-area-chat.js +5 -2
- package/lib/widgets/provider-config-dialog.d.ts +2 -0
- package/lib/widgets/provider-config-dialog.js +34 -34
- package/package.json +13 -13
- package/schema/settings-model.json +3 -2
- package/src/agent.ts +360 -372
- package/src/approval-buttons.ts +43 -389
- package/src/chat-model-registry.ts +9 -1
- package/src/chat-model.ts +399 -370
- package/src/completion/completion-provider.ts +2 -3
- package/src/components/clear-button.tsx +18 -6
- package/src/components/completion-status.tsx +18 -4
- package/src/components/model-select.tsx +25 -16
- package/src/components/stop-button.tsx +22 -9
- package/src/components/token-usage-display.tsx +14 -2
- package/src/components/tool-select.tsx +27 -9
- package/src/index.ts +78 -134
- package/src/models/settings-model.ts +41 -27
- package/src/providers/built-in-providers.ts +38 -19
- package/src/providers/models.ts +3 -3
- package/src/providers/provider-registry.ts +4 -8
- package/src/tokens.ts +5 -6
- package/src/tools/commands.ts +40 -53
- package/src/widgets/ai-settings.tsx +153 -84
- package/src/widgets/main-area-chat.ts +8 -2
- package/src/widgets/provider-config-dialog.tsx +54 -41
- package/style/base.css +24 -73
- package/lib/mcp/browser.d.ts +0 -68
- package/lib/mcp/browser.js +0 -138
- package/lib/tools/file.d.ts +0 -36
- package/lib/tools/file.js +0 -351
- package/lib/tools/notebook.d.ts +0 -40
- package/lib/tools/notebook.js +0 -779
- package/src/mcp/browser.ts +0 -220
- package/src/tools/file.ts +0 -438
- package/src/tools/notebook.ts +0 -986
package/src/mcp/browser.ts
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
-
/**
|
|
3
|
-
* Browser-compatible MCP Server implementation
|
|
4
|
-
*
|
|
5
|
-
* This is a custom implementation that works around the limitation in
|
|
6
|
-
* @openai/agents where MCPServerStreamableHttp doesn't work in browsers
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// Type definitions matching openai/agents MCPServer interface
|
|
10
|
-
interface MCPServer {
|
|
11
|
-
cacheToolsList: boolean;
|
|
12
|
-
toolFilter?: any;
|
|
13
|
-
connect(): Promise<void>;
|
|
14
|
-
readonly name: string;
|
|
15
|
-
close(): Promise<void>;
|
|
16
|
-
listTools(): Promise<MCPTool[]>;
|
|
17
|
-
callTool(
|
|
18
|
-
toolName: string,
|
|
19
|
-
args: Record<string, unknown> | null
|
|
20
|
-
): Promise<CallToolResultContent>;
|
|
21
|
-
invalidateToolsCache(): Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface MCPTool {
|
|
25
|
-
name: string;
|
|
26
|
-
description?: string;
|
|
27
|
-
inputSchema: {
|
|
28
|
-
type: 'object';
|
|
29
|
-
properties: Record<string, any>;
|
|
30
|
-
required: string[];
|
|
31
|
-
additionalProperties: boolean;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// CallToolResultContent is an array of content items
|
|
36
|
-
type CallToolResultContent = Array<{ type: string; text: string }>;
|
|
37
|
-
|
|
38
|
-
interface MCPServerStreamableHttpOptions {
|
|
39
|
-
url: string;
|
|
40
|
-
cacheToolsList?: boolean;
|
|
41
|
-
clientSessionTimeoutSeconds?: number;
|
|
42
|
-
name?: string;
|
|
43
|
-
logger?: any;
|
|
44
|
-
toolFilter?: any;
|
|
45
|
-
timeout?: number;
|
|
46
|
-
authProvider?: any;
|
|
47
|
-
requestInit?: any;
|
|
48
|
-
fetch?: any;
|
|
49
|
-
reconnectionOptions?: any;
|
|
50
|
-
sessionId?: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Browser-compatible MCP Server implementation that works around limitations
|
|
55
|
-
* in @openai/agents where MCPServerStreamableHttp doesn't work in browsers.
|
|
56
|
-
*
|
|
57
|
-
* This class provides a streamable HTTP client transport for MCP (Model Context Protocol)
|
|
58
|
-
* servers that can be used in browser environments.
|
|
59
|
-
*/
|
|
60
|
-
export class BrowserMCPServerStreamableHttp implements MCPServer {
|
|
61
|
-
readonly name: string;
|
|
62
|
-
readonly cacheToolsList: boolean;
|
|
63
|
-
readonly toolFilter: any = undefined;
|
|
64
|
-
|
|
65
|
-
constructor(options: MCPServerStreamableHttpOptions) {
|
|
66
|
-
this._options = options;
|
|
67
|
-
this.name = options.name || `browser-mcp-server: ${options.url}`;
|
|
68
|
-
this.cacheToolsList = options.cacheToolsList ?? false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async connect(): Promise<void> {
|
|
72
|
-
try {
|
|
73
|
-
// Dynamic import to handle cases where MCP SDK isn't available
|
|
74
|
-
const { StreamableHTTPClientTransport } = await import(
|
|
75
|
-
'@modelcontextprotocol/sdk/client/streamableHttp.js'
|
|
76
|
-
);
|
|
77
|
-
const { Client } = await import(
|
|
78
|
-
'@modelcontextprotocol/sdk/client/index.js'
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Merge CORS-enabled requestInit with user options
|
|
82
|
-
const corsRequestInit = {
|
|
83
|
-
mode: 'cors' as RequestMode,
|
|
84
|
-
credentials: 'omit' as RequestCredentials,
|
|
85
|
-
...this._options.requestInit
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
this._transport = new StreamableHTTPClientTransport(
|
|
89
|
-
new URL(this._options.url),
|
|
90
|
-
{
|
|
91
|
-
authProvider: this._options.authProvider,
|
|
92
|
-
requestInit: corsRequestInit,
|
|
93
|
-
fetch: this._options.fetch || fetch,
|
|
94
|
-
reconnectionOptions: this._options.reconnectionOptions,
|
|
95
|
-
sessionId: this._options.sessionId
|
|
96
|
-
}
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
this._session = new Client({
|
|
100
|
-
name: this.name,
|
|
101
|
-
version: '1.0.0'
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
await this._session.connect(this._transport);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
console.error('Error initializing MCP server:', error);
|
|
107
|
-
await this.close();
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async close(): Promise<void> {
|
|
113
|
-
if (this._session) {
|
|
114
|
-
try {
|
|
115
|
-
await this._session.close();
|
|
116
|
-
} catch (error) {
|
|
117
|
-
console.error('Error closing MCP server session:', error);
|
|
118
|
-
}
|
|
119
|
-
this._session = null;
|
|
120
|
-
}
|
|
121
|
-
if (this._transport) {
|
|
122
|
-
try {
|
|
123
|
-
await this._transport.close();
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.error('Error closing MCP server transport:', error);
|
|
126
|
-
}
|
|
127
|
-
this._transport = null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async listTools(): Promise<MCPTool[]> {
|
|
132
|
-
if (!this._session) {
|
|
133
|
-
throw new Error('Server not initialized. Call connect() first.');
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
this.cacheToolsList &&
|
|
138
|
-
!this._cacheDirty &&
|
|
139
|
-
this._toolsList.length > 0
|
|
140
|
-
) {
|
|
141
|
-
return this._toolsList;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
const { ListToolsResultSchema } = await import(
|
|
146
|
-
'@modelcontextprotocol/sdk/types.js'
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const response = await this._session.listTools();
|
|
150
|
-
|
|
151
|
-
const parsedResponse = ListToolsResultSchema.parse(response);
|
|
152
|
-
|
|
153
|
-
// Map to openai/agents MCPTool type
|
|
154
|
-
this._toolsList = parsedResponse.tools.map((tool: any) => ({
|
|
155
|
-
name: tool.name,
|
|
156
|
-
description: tool.description,
|
|
157
|
-
inputSchema: {
|
|
158
|
-
type: 'object' as const,
|
|
159
|
-
properties: tool.inputSchema?.properties || {},
|
|
160
|
-
required: tool.inputSchema?.required || [],
|
|
161
|
-
additionalProperties: tool.inputSchema?.additionalProperties ?? false
|
|
162
|
-
}
|
|
163
|
-
}));
|
|
164
|
-
|
|
165
|
-
this._cacheDirty = false;
|
|
166
|
-
|
|
167
|
-
return this._toolsList;
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error(`Error listing tools from ${this.name}:`, error);
|
|
170
|
-
throw error;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async callTool(
|
|
175
|
-
toolName: string,
|
|
176
|
-
args: Record<string, unknown> | null
|
|
177
|
-
): Promise<CallToolResultContent> {
|
|
178
|
-
if (!this._session) {
|
|
179
|
-
throw new Error('Server not initialized. Call connect() first.');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const { CallToolResultSchema } = await import(
|
|
184
|
-
'@modelcontextprotocol/sdk/types.js'
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
const response = await this._session.callTool(
|
|
188
|
-
{
|
|
189
|
-
name: toolName,
|
|
190
|
-
arguments: args ?? {}
|
|
191
|
-
},
|
|
192
|
-
undefined,
|
|
193
|
-
{
|
|
194
|
-
timeout: this._options.timeout ?? 30000
|
|
195
|
-
}
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
// Parse and validate using MCP SDK schema
|
|
199
|
-
const parsed = CallToolResultSchema.parse(response);
|
|
200
|
-
const result = parsed.content;
|
|
201
|
-
|
|
202
|
-
// Return the content array as expected by openai/agents
|
|
203
|
-
// CallToolResultContent is { type: string; text: string }[]
|
|
204
|
-
return result as CallToolResultContent;
|
|
205
|
-
} catch (error) {
|
|
206
|
-
console.error(`Error calling tool ${toolName}:`, error);
|
|
207
|
-
throw error;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async invalidateToolsCache(): Promise<void> {
|
|
212
|
-
this._cacheDirty = true;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private _session: any | null = null;
|
|
216
|
-
private _toolsList: MCPTool[] = [];
|
|
217
|
-
private _cacheDirty = true;
|
|
218
|
-
private _transport: any = null;
|
|
219
|
-
private _options: MCPServerStreamableHttpOptions;
|
|
220
|
-
}
|
package/src/tools/file.ts
DELETED
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
import { PathExt } from '@jupyterlab/coreutils';
|
|
2
|
-
import { CommandRegistry } from '@lumino/commands';
|
|
3
|
-
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
4
|
-
import { IDocumentWidget } from '@jupyterlab/docregistry';
|
|
5
|
-
import { IEditorTracker } from '@jupyterlab/fileeditor';
|
|
6
|
-
|
|
7
|
-
import { tool } from '@openai/agents';
|
|
8
|
-
|
|
9
|
-
import { z } from 'zod';
|
|
10
|
-
|
|
11
|
-
import { IDiffManager, ITool } from '../tokens';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Create a tool for creating new files of various types
|
|
15
|
-
*/
|
|
16
|
-
export function createNewFileTool(docManager: IDocumentManager): ITool {
|
|
17
|
-
return tool({
|
|
18
|
-
name: 'create_file',
|
|
19
|
-
description:
|
|
20
|
-
'Create a new file of specified type (text, python, markdown, json, etc.)',
|
|
21
|
-
parameters: z.object({
|
|
22
|
-
fileName: z.string().describe('Name of the file to create'),
|
|
23
|
-
fileType: z
|
|
24
|
-
.string()
|
|
25
|
-
.default('text')
|
|
26
|
-
.describe(
|
|
27
|
-
'Type of file to create. Common examples: text, python, markdown, json, javascript, typescript, yaml, julia, r, csv'
|
|
28
|
-
),
|
|
29
|
-
content: z
|
|
30
|
-
.string()
|
|
31
|
-
.optional()
|
|
32
|
-
.nullable()
|
|
33
|
-
.describe('Initial content for the file (optional)'),
|
|
34
|
-
cwd: z
|
|
35
|
-
.string()
|
|
36
|
-
.optional()
|
|
37
|
-
.nullable()
|
|
38
|
-
.describe('Directory where to create the file (optional)')
|
|
39
|
-
}),
|
|
40
|
-
errorFunction: (context, error) => {
|
|
41
|
-
return JSON.stringify({
|
|
42
|
-
success: false,
|
|
43
|
-
error: `Failed to create file: ${error instanceof Error ? error.message : String(error)}`
|
|
44
|
-
});
|
|
45
|
-
},
|
|
46
|
-
execute: async (input: {
|
|
47
|
-
fileName: string;
|
|
48
|
-
fileType?: string;
|
|
49
|
-
content?: string | null;
|
|
50
|
-
cwd?: string | null;
|
|
51
|
-
}) => {
|
|
52
|
-
const { fileName, content = '', cwd, fileType = 'text' } = input;
|
|
53
|
-
|
|
54
|
-
const registeredFileType = docManager.registry.getFileType(fileType);
|
|
55
|
-
const ext = registeredFileType?.extensions[0] || '.txt';
|
|
56
|
-
|
|
57
|
-
const existingExt = PathExt.extname(fileName);
|
|
58
|
-
const fullFileName = existingExt ? fileName : `${fileName}${ext}`;
|
|
59
|
-
|
|
60
|
-
const fullPath = cwd ? `${cwd}/${fullFileName}` : fullFileName;
|
|
61
|
-
|
|
62
|
-
const model = await docManager.services.contents.newUntitled({
|
|
63
|
-
path: cwd || '',
|
|
64
|
-
type: 'file',
|
|
65
|
-
ext
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
let finalPath = model.path;
|
|
69
|
-
if (model.name !== fullFileName) {
|
|
70
|
-
const renamed = await docManager.services.contents.rename(
|
|
71
|
-
model.path,
|
|
72
|
-
fullPath
|
|
73
|
-
);
|
|
74
|
-
finalPath = renamed.path;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (content) {
|
|
78
|
-
await docManager.services.contents.save(finalPath, {
|
|
79
|
-
type: 'file',
|
|
80
|
-
format: 'text',
|
|
81
|
-
content
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let opened = false;
|
|
86
|
-
if (!docManager.findWidget(finalPath)) {
|
|
87
|
-
docManager.openOrReveal(finalPath);
|
|
88
|
-
opened = true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
success: true,
|
|
93
|
-
message: `${fileType} file '${fullFileName}' created and opened successfully`,
|
|
94
|
-
fileName: fullFileName,
|
|
95
|
-
filePath: finalPath,
|
|
96
|
-
fileType,
|
|
97
|
-
hasContent: !!content,
|
|
98
|
-
opened
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Create a tool for opening files
|
|
106
|
-
*/
|
|
107
|
-
export function createOpenFileTool(docManager: IDocumentManager): ITool {
|
|
108
|
-
return tool({
|
|
109
|
-
name: 'open_file',
|
|
110
|
-
description: 'Open a file in the editor',
|
|
111
|
-
parameters: z.object({
|
|
112
|
-
filePath: z.string().describe('Path to the file to open')
|
|
113
|
-
}),
|
|
114
|
-
errorFunction: (context, error) => {
|
|
115
|
-
return JSON.stringify({
|
|
116
|
-
success: false,
|
|
117
|
-
error: `Failed to open file: ${error instanceof Error ? error.message : String(error)}`
|
|
118
|
-
});
|
|
119
|
-
},
|
|
120
|
-
execute: async (input: { filePath: string }) => {
|
|
121
|
-
const { filePath } = input;
|
|
122
|
-
|
|
123
|
-
const widget = docManager.openOrReveal(filePath);
|
|
124
|
-
|
|
125
|
-
if (!widget) {
|
|
126
|
-
throw new Error(`Could not open file: ${filePath}`);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
success: true,
|
|
131
|
-
message: `File '${filePath}' opened successfully`,
|
|
132
|
-
filePath,
|
|
133
|
-
widgetId: widget.id
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Create a tool for deleting files
|
|
141
|
-
*/
|
|
142
|
-
export function createDeleteFileTool(docManager: IDocumentManager): ITool {
|
|
143
|
-
return tool({
|
|
144
|
-
name: 'delete_file',
|
|
145
|
-
description: 'Delete a file from the file system',
|
|
146
|
-
parameters: z.object({
|
|
147
|
-
filePath: z.string().describe('Path to the file to delete')
|
|
148
|
-
}),
|
|
149
|
-
errorFunction: (context, error) => {
|
|
150
|
-
return JSON.stringify({
|
|
151
|
-
success: false,
|
|
152
|
-
error: `Failed to delete file: ${error instanceof Error ? error.message : String(error)}`
|
|
153
|
-
});
|
|
154
|
-
},
|
|
155
|
-
execute: async (input: { filePath: string }) => {
|
|
156
|
-
const { filePath } = input;
|
|
157
|
-
|
|
158
|
-
await docManager.services.contents.delete(filePath);
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
success: true,
|
|
162
|
-
message: `File '${filePath}' deleted successfully`,
|
|
163
|
-
filePath
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Create a tool for renaming files
|
|
171
|
-
*/
|
|
172
|
-
export function createRenameFileTool(docManager: IDocumentManager): ITool {
|
|
173
|
-
return tool({
|
|
174
|
-
name: 'rename_file',
|
|
175
|
-
description: 'Rename a file or move it to a different location',
|
|
176
|
-
parameters: z.object({
|
|
177
|
-
oldPath: z.string().describe('Current path of the file'),
|
|
178
|
-
newPath: z.string().describe('New path/name for the file')
|
|
179
|
-
}),
|
|
180
|
-
errorFunction: (context, error) => {
|
|
181
|
-
return JSON.stringify({
|
|
182
|
-
success: false,
|
|
183
|
-
error: `Failed to rename file: ${error instanceof Error ? error.message : String(error)}`
|
|
184
|
-
});
|
|
185
|
-
},
|
|
186
|
-
execute: async (input: { oldPath: string; newPath: string }) => {
|
|
187
|
-
const { oldPath, newPath } = input;
|
|
188
|
-
|
|
189
|
-
await docManager.services.contents.rename(oldPath, newPath);
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
success: true,
|
|
193
|
-
message: `File renamed from '${oldPath}' to '${newPath}' successfully`,
|
|
194
|
-
oldPath,
|
|
195
|
-
newPath
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Create a tool for copying files
|
|
203
|
-
*/
|
|
204
|
-
export function createCopyFileTool(docManager: IDocumentManager): ITool {
|
|
205
|
-
return tool({
|
|
206
|
-
name: 'copy_file',
|
|
207
|
-
description: 'Copy a file to a new location',
|
|
208
|
-
parameters: z.object({
|
|
209
|
-
sourcePath: z.string().describe('Path of the file to copy'),
|
|
210
|
-
destinationPath: z
|
|
211
|
-
.string()
|
|
212
|
-
.describe('Destination path for the copied file')
|
|
213
|
-
}),
|
|
214
|
-
errorFunction: (context, error) => {
|
|
215
|
-
return JSON.stringify({
|
|
216
|
-
success: false,
|
|
217
|
-
error: `Failed to copy file: ${error instanceof Error ? error.message : String(error)}`
|
|
218
|
-
});
|
|
219
|
-
},
|
|
220
|
-
execute: async (input: { sourcePath: string; destinationPath: string }) => {
|
|
221
|
-
const { sourcePath, destinationPath } = input;
|
|
222
|
-
|
|
223
|
-
await docManager.services.contents.copy(sourcePath, destinationPath);
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
success: true,
|
|
227
|
-
message: `File copied from '${sourcePath}' to '${destinationPath}' successfully`,
|
|
228
|
-
sourcePath,
|
|
229
|
-
destinationPath
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Create a tool for navigating to directories in the file browser
|
|
237
|
-
*/
|
|
238
|
-
export function createNavigateToDirectoryTool(
|
|
239
|
-
commands: CommandRegistry
|
|
240
|
-
): ITool {
|
|
241
|
-
return tool({
|
|
242
|
-
name: 'navigate_to_directory',
|
|
243
|
-
description: 'Navigate to a specific directory in the file browser',
|
|
244
|
-
parameters: z.object({
|
|
245
|
-
directoryPath: z.string().describe('Path to the directory to navigate to')
|
|
246
|
-
}),
|
|
247
|
-
errorFunction: (context, error) => {
|
|
248
|
-
return JSON.stringify({
|
|
249
|
-
success: false,
|
|
250
|
-
error: `Failed to navigate to directory: ${error instanceof Error ? error.message : String(error)}`
|
|
251
|
-
});
|
|
252
|
-
},
|
|
253
|
-
execute: async (input: { directoryPath: string }) => {
|
|
254
|
-
const { directoryPath } = input;
|
|
255
|
-
|
|
256
|
-
await commands.execute('filebrowser:go-to-path', {
|
|
257
|
-
path: directoryPath
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
return {
|
|
261
|
-
success: true,
|
|
262
|
-
message: `Navigated to directory '${directoryPath}' successfully`,
|
|
263
|
-
directoryPath
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Create a tool for getting file information and content
|
|
271
|
-
*/
|
|
272
|
-
export function createGetFileInfoTool(
|
|
273
|
-
docManager: IDocumentManager,
|
|
274
|
-
editorTracker?: IEditorTracker
|
|
275
|
-
): ITool {
|
|
276
|
-
return tool({
|
|
277
|
-
name: 'get_file_info',
|
|
278
|
-
description:
|
|
279
|
-
'Get information about a file including its path, name, extension, and content. Works with text-based files like Python files, markdown, JSON, etc. For Jupyter notebooks, use dedicated notebook tools instead. If no file path is provided, returns information about the currently active file in the editor.',
|
|
280
|
-
parameters: z.object({
|
|
281
|
-
filePath: z
|
|
282
|
-
.string()
|
|
283
|
-
.optional()
|
|
284
|
-
.nullable()
|
|
285
|
-
.describe(
|
|
286
|
-
'Path to the file to read (e.g., "script.py", "README.md", "config.json"). If not provided, uses the currently active file in the editor.'
|
|
287
|
-
)
|
|
288
|
-
}),
|
|
289
|
-
errorFunction: (context, error) => {
|
|
290
|
-
return JSON.stringify({
|
|
291
|
-
success: false,
|
|
292
|
-
error: `Failed to get file info: ${error instanceof Error ? error.message : String(error)}`
|
|
293
|
-
});
|
|
294
|
-
},
|
|
295
|
-
execute: async (input: { filePath?: string | null }) => {
|
|
296
|
-
const { filePath } = input;
|
|
297
|
-
|
|
298
|
-
let widget: IDocumentWidget | null = null;
|
|
299
|
-
|
|
300
|
-
if (filePath) {
|
|
301
|
-
widget =
|
|
302
|
-
docManager.findWidget(filePath) ??
|
|
303
|
-
docManager.openOrReveal(filePath) ??
|
|
304
|
-
null;
|
|
305
|
-
|
|
306
|
-
if (!widget) {
|
|
307
|
-
throw new Error(`Failed to open file at path: ${filePath}`);
|
|
308
|
-
}
|
|
309
|
-
} else {
|
|
310
|
-
widget = editorTracker?.currentWidget ?? null;
|
|
311
|
-
|
|
312
|
-
if (!widget) {
|
|
313
|
-
throw new Error(
|
|
314
|
-
'No active file in the editor and no file path provided'
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (!widget.context) {
|
|
320
|
-
throw new Error('Widget is not a document');
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
await widget.context.ready;
|
|
324
|
-
|
|
325
|
-
const model = widget.context.model;
|
|
326
|
-
|
|
327
|
-
if (!model) {
|
|
328
|
-
throw new Error('File model not available');
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const sharedModel = model.sharedModel;
|
|
332
|
-
const content = sharedModel.getSource();
|
|
333
|
-
const resolvedFilePath = widget.context.path;
|
|
334
|
-
const fileName = widget.title.label;
|
|
335
|
-
const fileExtension = PathExt.extname(resolvedFilePath) || 'unknown';
|
|
336
|
-
|
|
337
|
-
return JSON.stringify({
|
|
338
|
-
success: true,
|
|
339
|
-
filePath: resolvedFilePath,
|
|
340
|
-
fileName,
|
|
341
|
-
fileExtension,
|
|
342
|
-
content,
|
|
343
|
-
isDirty: model.dirty,
|
|
344
|
-
readOnly: model.readOnly,
|
|
345
|
-
widgetType: widget.constructor.name
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Create a tool for setting the content of a file
|
|
353
|
-
*/
|
|
354
|
-
export function createSetFileContentTool(
|
|
355
|
-
docManager: IDocumentManager,
|
|
356
|
-
diffManager?: IDiffManager
|
|
357
|
-
): ITool {
|
|
358
|
-
return tool({
|
|
359
|
-
name: 'set_file_content',
|
|
360
|
-
description:
|
|
361
|
-
'Set or update the content of an existing file. This will replace the entire content of the file. For Jupyter notebooks, use dedicated notebook tools instead.',
|
|
362
|
-
parameters: z.object({
|
|
363
|
-
filePath: z
|
|
364
|
-
.string()
|
|
365
|
-
.describe(
|
|
366
|
-
'Path to the file to update (e.g., "script.py", "README.md", "config.json")'
|
|
367
|
-
),
|
|
368
|
-
content: z.string().describe('The new content to set for the file'),
|
|
369
|
-
save: z
|
|
370
|
-
.boolean()
|
|
371
|
-
.optional()
|
|
372
|
-
.default(true)
|
|
373
|
-
.describe('Whether to save the file after updating (default: true)')
|
|
374
|
-
}),
|
|
375
|
-
errorFunction: (context, error) => {
|
|
376
|
-
return JSON.stringify({
|
|
377
|
-
success: false,
|
|
378
|
-
error: `Failed to set file content: ${error instanceof Error ? error.message : String(error)}`
|
|
379
|
-
});
|
|
380
|
-
},
|
|
381
|
-
execute: async (input: {
|
|
382
|
-
filePath: string;
|
|
383
|
-
content: string;
|
|
384
|
-
save?: boolean;
|
|
385
|
-
}) => {
|
|
386
|
-
const { filePath, content, save = true } = input;
|
|
387
|
-
|
|
388
|
-
let widget = docManager.findWidget(filePath);
|
|
389
|
-
|
|
390
|
-
if (!widget) {
|
|
391
|
-
widget = docManager.openOrReveal(filePath);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (!widget) {
|
|
395
|
-
throw new Error(`Failed to open file at path: ${filePath}`);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
await widget.context.ready;
|
|
399
|
-
|
|
400
|
-
const model = widget.context.model;
|
|
401
|
-
|
|
402
|
-
if (!model) {
|
|
403
|
-
throw new Error('File model not available');
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (model.readOnly) {
|
|
407
|
-
throw new Error('File is read-only and cannot be modified');
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const sharedModel = model.sharedModel;
|
|
411
|
-
const originalContent = sharedModel.getSource();
|
|
412
|
-
|
|
413
|
-
sharedModel.setSource(content);
|
|
414
|
-
|
|
415
|
-
// Show the file diff using the diff manager if available
|
|
416
|
-
if (diffManager) {
|
|
417
|
-
await diffManager.showFileDiff({
|
|
418
|
-
original: String(originalContent),
|
|
419
|
-
modified: content,
|
|
420
|
-
filePath
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (save) {
|
|
425
|
-
await widget.context.save();
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
return JSON.stringify({
|
|
429
|
-
success: true,
|
|
430
|
-
filePath,
|
|
431
|
-
fileName: widget.title.label,
|
|
432
|
-
contentLength: content.length,
|
|
433
|
-
saved: save,
|
|
434
|
-
isDirty: model.dirty
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
}
|