@notebook-intelligence/notebook-intelligence 2.3.1 → 2.4.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 +1 -1
- package/lib/api.d.ts +5 -1
- package/lib/api.js +42 -7
- package/lib/chat-sidebar.d.ts +2 -0
- package/lib/chat-sidebar.js +32 -5
- package/lib/index.js +105 -0
- package/lib/tokens.d.ts +2 -1
- package/lib/tokens.js +1 -0
- package/package.json +1 -1
- package/src/api.ts +47 -9
- package/src/chat-sidebar.tsx +73 -6
- package/src/index.ts +132 -1
- package/src/tokens.ts +2 -1
- package/style/base.css +6 -1
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ for the frontend extension.
|
|
|
62
62
|
Notebook Intelligence can remember your GitHub Copilot login so that you don't need to re-login after a JupyterLab or system restart. Please be aware of the security implications of using this feature.
|
|
63
63
|
|
|
64
64
|
> [!CAUTION]
|
|
65
|
-
> If you configure NBI to remember your GitHub Copilot login, it will encrypt the token and store into a data file at `~/.jupyter/nbi-data.json`. You should never share this file with others as they can access your tokens.
|
|
65
|
+
> If you configure NBI to remember your GitHub Copilot login, it will encrypt the token and store into a data file at `~/.jupyter/nbi/user-data.json`. You should never share this file with others as they can access your tokens.
|
|
66
66
|
> Even though the token is encrypted, it is done so by using a default password and that's why it can be decrypted by others. In order to prevent that you can specify a custom password using the environment variable `NBI_GH_ACCESS_TOKEN_PASSWORD`.
|
|
67
67
|
|
|
68
68
|
```bash
|
package/lib/api.d.ts
CHANGED
|
@@ -12,10 +12,11 @@ export interface IDeviceVerificationInfo {
|
|
|
12
12
|
}
|
|
13
13
|
export declare class NBIConfig {
|
|
14
14
|
get userHomeDir(): string;
|
|
15
|
-
get
|
|
15
|
+
get userConfigDir(): string;
|
|
16
16
|
get llmProviders(): [any];
|
|
17
17
|
get chatModels(): [any];
|
|
18
18
|
get inlineCompletionModels(): [any];
|
|
19
|
+
get defaultChatMode(): string;
|
|
19
20
|
get chatModel(): any;
|
|
20
21
|
get inlineCompletionModel(): any;
|
|
21
22
|
get usingGitHubCopilotModel(): boolean;
|
|
@@ -32,6 +33,7 @@ export declare class NBIAPI {
|
|
|
32
33
|
static _messageReceived: Signal<unknown, any>;
|
|
33
34
|
static config: NBIConfig;
|
|
34
35
|
static configChanged: Signal<NBIConfig, void>;
|
|
36
|
+
static githubLoginStatusChanged: Signal<unknown, void>;
|
|
35
37
|
static initialize(): Promise<void>;
|
|
36
38
|
static initializeWebsocket(): Promise<void>;
|
|
37
39
|
static getLoginStatus(): GitHubCopilotLoginStatus;
|
|
@@ -43,6 +45,8 @@ export declare class NBIAPI {
|
|
|
43
45
|
static setConfig(config: any): Promise<void>;
|
|
44
46
|
static updateOllamaModelList(): Promise<void>;
|
|
45
47
|
static reloadMCPServerList(): Promise<any>;
|
|
48
|
+
static getMCPConfigFile(): Promise<any>;
|
|
49
|
+
static setMCPConfigFile(config: any): Promise<any>;
|
|
46
50
|
static chatRequest(messageId: string, chatId: string, prompt: string, language: string, filename: string, additionalContext: IContextItem[], chatMode: string, toolSelections: IToolSelections, responseEmitter: IChatCompletionResponseEmitter): Promise<void>;
|
|
47
51
|
static generateCode(chatId: string, prompt: string, prefix: string, suffix: string, existingCode: string, language: string, filename: string, responseEmitter: IChatCompletionResponseEmitter): Promise<void>;
|
|
48
52
|
static sendChatUserInput(messageId: string, data: any): Promise<void>;
|
package/lib/api.js
CHANGED
|
@@ -5,8 +5,7 @@ import { requestAPI } from './handler';
|
|
|
5
5
|
import { URLExt } from '@jupyterlab/coreutils';
|
|
6
6
|
import { UUID } from '@lumino/coreutils';
|
|
7
7
|
import { Signal } from '@lumino/signaling';
|
|
8
|
-
import { GITHUB_COPILOT_PROVIDER_ID, RequestDataType } from './tokens';
|
|
9
|
-
const LOGIN_STATUS_UPDATE_INTERVAL = 2000;
|
|
8
|
+
import { GITHUB_COPILOT_PROVIDER_ID, RequestDataType, BackendMessageType } from './tokens';
|
|
10
9
|
export var GitHubCopilotLoginStatus;
|
|
11
10
|
(function (GitHubCopilotLoginStatus) {
|
|
12
11
|
GitHubCopilotLoginStatus["NotLoggedIn"] = "NOT_LOGGED_IN";
|
|
@@ -23,8 +22,8 @@ export class NBIConfig {
|
|
|
23
22
|
get userHomeDir() {
|
|
24
23
|
return this.capabilities.user_home_dir;
|
|
25
24
|
}
|
|
26
|
-
get
|
|
27
|
-
return this.capabilities.
|
|
25
|
+
get userConfigDir() {
|
|
26
|
+
return this.capabilities.nbi_user_config_dir;
|
|
28
27
|
}
|
|
29
28
|
get llmProviders() {
|
|
30
29
|
return this.capabilities.llm_providers;
|
|
@@ -35,6 +34,9 @@ export class NBIConfig {
|
|
|
35
34
|
get inlineCompletionModels() {
|
|
36
35
|
return this.capabilities.inline_completion_models;
|
|
37
36
|
}
|
|
37
|
+
get defaultChatMode() {
|
|
38
|
+
return this.capabilities.default_chat_mode;
|
|
39
|
+
}
|
|
38
40
|
get chatModel() {
|
|
39
41
|
return this.capabilities.chat_model;
|
|
40
42
|
}
|
|
@@ -56,10 +58,15 @@ class NBIAPI {
|
|
|
56
58
|
static async initialize() {
|
|
57
59
|
await this.fetchCapabilities();
|
|
58
60
|
this.updateGitHubLoginStatus();
|
|
59
|
-
setInterval(() => {
|
|
60
|
-
this.updateGitHubLoginStatus();
|
|
61
|
-
}, LOGIN_STATUS_UPDATE_INTERVAL);
|
|
62
61
|
NBIAPI.initializeWebsocket();
|
|
62
|
+
this._messageReceived.connect((_, msg) => {
|
|
63
|
+
msg = JSON.parse(msg);
|
|
64
|
+
if (msg.type === BackendMessageType.GitHubCopilotLoginStatusChange) {
|
|
65
|
+
this.updateGitHubLoginStatus().then(() => {
|
|
66
|
+
this.githubLoginStatusChanged.emit();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
63
70
|
}
|
|
64
71
|
static async initializeWebsocket() {
|
|
65
72
|
const serverSettings = ServerConnection.makeSettings();
|
|
@@ -189,6 +196,33 @@ class NBIAPI {
|
|
|
189
196
|
});
|
|
190
197
|
});
|
|
191
198
|
}
|
|
199
|
+
static async getMCPConfigFile() {
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
requestAPI('mcp-config-file', { method: 'GET' })
|
|
202
|
+
.then(async (data) => {
|
|
203
|
+
resolve(data);
|
|
204
|
+
})
|
|
205
|
+
.catch(reason => {
|
|
206
|
+
console.error(`Failed to get MCP config file.\n${reason}`);
|
|
207
|
+
reject(reason);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
static async setMCPConfigFile(config) {
|
|
212
|
+
return new Promise((resolve, reject) => {
|
|
213
|
+
requestAPI('mcp-config-file', {
|
|
214
|
+
method: 'POST',
|
|
215
|
+
body: JSON.stringify(config)
|
|
216
|
+
})
|
|
217
|
+
.then(async (data) => {
|
|
218
|
+
resolve(data);
|
|
219
|
+
})
|
|
220
|
+
.catch(reason => {
|
|
221
|
+
console.error(`Failed to set MCP config file.\n${reason}`);
|
|
222
|
+
reject(reason);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
192
226
|
static async chatRequest(messageId, chatId, prompt, language, filename, additionalContext, chatMode, toolSelections, responseEmitter) {
|
|
193
227
|
this._messageReceived.connect((_, msg) => {
|
|
194
228
|
msg = JSON.parse(msg);
|
|
@@ -286,4 +320,5 @@ NBIAPI._deviceVerificationInfo = {
|
|
|
286
320
|
NBIAPI._messageReceived = new Signal(_a);
|
|
287
321
|
NBIAPI.config = new NBIConfig();
|
|
288
322
|
NBIAPI.configChanged = _a.config.changed;
|
|
323
|
+
NBIAPI.githubLoginStatusChanged = new Signal(_a);
|
|
289
324
|
export { NBIAPI };
|
package/lib/chat-sidebar.d.ts
CHANGED
|
@@ -76,7 +76,9 @@ export declare class GitHubCopilotLoginDialogBody extends ReactWidget {
|
|
|
76
76
|
export declare class ConfigurationDialogBody extends ReactWidget {
|
|
77
77
|
constructor(options: {
|
|
78
78
|
onSave: () => void;
|
|
79
|
+
onEditMCPConfigClicked: () => void;
|
|
79
80
|
});
|
|
80
81
|
render(): JSX.Element;
|
|
82
|
+
private _onEditMCPConfigClicked;
|
|
81
83
|
private _onSave;
|
|
82
84
|
}
|
package/lib/chat-sidebar.js
CHANGED
|
@@ -13,6 +13,7 @@ import copilotWarningSvgstr from '../style/icons/copilot-warning.svg';
|
|
|
13
13
|
import { VscSend, VscStopCircle, VscEye, VscEyeClosed, VscTriangleRight, VscTriangleDown, VscWarning, VscSettingsGear, VscPassFilled, VscTools, VscTrash } from 'react-icons/vsc';
|
|
14
14
|
import { MdOutlineCheckBoxOutlineBlank, MdCheckBox } from 'react-icons/md';
|
|
15
15
|
import { extractLLMGeneratedCode, isDarkTheme } from './utils';
|
|
16
|
+
import * as path from 'path';
|
|
16
17
|
const OPENAI_COMPATIBLE_CHAT_MODEL_ID = 'openai-compatible-chat-model';
|
|
17
18
|
const LITELLM_COMPATIBLE_CHAT_MODEL_ID = 'litellm-compatible-chat-model';
|
|
18
19
|
const OPENAI_COMPATIBLE_INLINE_COMPLETION_MODEL_ID = 'openai-compatible-inline-completion-model';
|
|
@@ -130,10 +131,11 @@ export class GitHubCopilotLoginDialogBody extends ReactWidget {
|
|
|
130
131
|
export class ConfigurationDialogBody extends ReactWidget {
|
|
131
132
|
constructor(options) {
|
|
132
133
|
super();
|
|
134
|
+
this._onEditMCPConfigClicked = options.onEditMCPConfigClicked;
|
|
133
135
|
this._onSave = options.onSave;
|
|
134
136
|
}
|
|
135
137
|
render() {
|
|
136
|
-
return React.createElement(ConfigurationDialogBodyComponent, { onSave: this._onSave });
|
|
138
|
+
return (React.createElement(ConfigurationDialogBodyComponent, { onEditMCPConfigClicked: this._onEditMCPConfigClicked, onSave: this._onSave }));
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
const answeredForms = new Map();
|
|
@@ -358,7 +360,7 @@ function SidebarComponent(props) {
|
|
|
358
360
|
const [activeDocumentInfo, setActiveDocumentInfo] = useState(null);
|
|
359
361
|
const [currentFileContextTitle, setCurrentFileContextTitle] = useState('');
|
|
360
362
|
const telemetryEmitter = props.getTelemetryEmitter();
|
|
361
|
-
const [chatMode, setChatMode] = useState(
|
|
363
|
+
const [chatMode, setChatMode] = useState(NBIAPI.config.defaultChatMode);
|
|
362
364
|
const [toolSelectionTitle, setToolSelectionTitle] = useState('Tool selection');
|
|
363
365
|
const [selectedToolCount, setSelectedToolCount] = useState(0);
|
|
364
366
|
const [notebookExecuteToolSelected, setNotebookExecuteToolSelected] = useState(false);
|
|
@@ -1508,6 +1510,7 @@ function ConfigurationDialogBodyComponent(props) {
|
|
|
1508
1510
|
const [mcpServerNames, setMcpServerNames] = useState(((_a = nbiConfig.toolConfig.mcpServers) === null || _a === void 0 ? void 0 : _a.map((server) => server.id)) || []);
|
|
1509
1511
|
const handleSaveClick = async () => {
|
|
1510
1512
|
const config = {
|
|
1513
|
+
default_chat_mode: defaultChatMode,
|
|
1511
1514
|
chat_model: {
|
|
1512
1515
|
provider: chatModelProvider,
|
|
1513
1516
|
model: chatModel,
|
|
@@ -1532,6 +1535,7 @@ function ConfigurationDialogBodyComponent(props) {
|
|
|
1532
1535
|
};
|
|
1533
1536
|
const [chatModelProvider, setChatModelProvider] = useState(nbiConfig.chatModel.provider || 'none');
|
|
1534
1537
|
const [inlineCompletionModelProvider, setInlineCompletionModelProvider] = useState(nbiConfig.inlineCompletionModel.provider || 'none');
|
|
1538
|
+
const [defaultChatMode, setDefaultChatMode] = useState(nbiConfig.defaultChatMode);
|
|
1535
1539
|
const [chatModel, setChatModel] = useState(nbiConfig.chatModel.model);
|
|
1536
1540
|
const [chatModelProperties, setChatModelProperties] = useState([]);
|
|
1537
1541
|
const [inlineCompletionModelProperties, setInlineCompletionModelProperties] = useState([]);
|
|
@@ -1608,6 +1612,16 @@ function ConfigurationDialogBodyComponent(props) {
|
|
|
1608
1612
|
}, []);
|
|
1609
1613
|
return (React.createElement("div", { className: "config-dialog" },
|
|
1610
1614
|
React.createElement("div", { className: "config-dialog-body" },
|
|
1615
|
+
React.createElement("div", { className: "model-config-section" },
|
|
1616
|
+
React.createElement("div", { className: "model-config-section-header" }, "Default chat mode"),
|
|
1617
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
1618
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
1619
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
1620
|
+
React.createElement("div", null,
|
|
1621
|
+
React.createElement("select", { className: "jp-mod-styled", value: defaultChatMode, onChange: event => setDefaultChatMode(event.target.value) },
|
|
1622
|
+
React.createElement("option", { value: "ask" }, "Ask"),
|
|
1623
|
+
React.createElement("option", { value: "agent" }, "Agent")))),
|
|
1624
|
+
React.createElement("div", { className: "model-config-section-column" }, " ")))),
|
|
1611
1625
|
React.createElement("div", { className: "model-config-section" },
|
|
1612
1626
|
React.createElement("div", { className: "model-config-section-header" }, "Chat model"),
|
|
1613
1627
|
React.createElement("div", { className: "model-config-section-body" },
|
|
@@ -1689,8 +1703,10 @@ function ConfigurationDialogBodyComponent(props) {
|
|
|
1689
1703
|
"Remember my GitHub Copilot access token")))))),
|
|
1690
1704
|
React.createElement("div", { className: "model-config-section" },
|
|
1691
1705
|
React.createElement("div", { className: "model-config-section-header" },
|
|
1692
|
-
"MCP Servers
|
|
1706
|
+
"MCP Servers (",
|
|
1693
1707
|
mcpServerNames.length,
|
|
1708
|
+
") [",
|
|
1709
|
+
React.createElement("a", { href: "javascript:void(0)", onClick: props.onEditMCPConfigClicked }, "edit"),
|
|
1694
1710
|
"]"),
|
|
1695
1711
|
React.createElement("div", { className: "model-config-section-body" },
|
|
1696
1712
|
React.createElement("div", { className: "model-config-section-row" },
|
|
@@ -1706,10 +1722,21 @@ function ConfigurationDialogBodyComponent(props) {
|
|
|
1706
1722
|
React.createElement("div", { className: "model-config-section-row" },
|
|
1707
1723
|
React.createElement("div", { className: "model-config-section-column" },
|
|
1708
1724
|
React.createElement("span", { className: "user-code-span", onClick: () => {
|
|
1709
|
-
navigator.clipboard.writeText(NBIAPI.config.
|
|
1725
|
+
navigator.clipboard.writeText(path.join(NBIAPI.config.userConfigDir, 'config.json'));
|
|
1726
|
+
return true;
|
|
1727
|
+
} },
|
|
1728
|
+
path.join(NBIAPI.config.userConfigDir, 'config.json'),
|
|
1729
|
+
' ',
|
|
1730
|
+
React.createElement("span", { className: "copy-icon", dangerouslySetInnerHTML: { __html: copySvgstr } }))))),
|
|
1731
|
+
React.createElement("div", { className: "model-config-section-header" }, "MCP config file path"),
|
|
1732
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
1733
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
1734
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
1735
|
+
React.createElement("span", { className: "user-code-span", onClick: () => {
|
|
1736
|
+
navigator.clipboard.writeText(path.join(NBIAPI.config.userConfigDir, 'mcp.json'));
|
|
1710
1737
|
return true;
|
|
1711
1738
|
} },
|
|
1712
|
-
NBIAPI.config.
|
|
1739
|
+
path.join(NBIAPI.config.userConfigDir, 'mcp.json'),
|
|
1713
1740
|
' ',
|
|
1714
1741
|
React.createElement("span", { className: "copy-icon", dangerouslySetInnerHTML: { __html: copySvgstr } }))))))),
|
|
1715
1742
|
React.createElement("div", { className: "config-dialog-footer" },
|
package/lib/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import sparklesSvgstr from '../style/icons/sparkles.svg';
|
|
|
21
21
|
import copilotSvgstr from '../style/icons/copilot.svg';
|
|
22
22
|
import { applyCodeToSelectionInEditor, cellOutputAsText, compareSelections, extractLLMGeneratedCode, getSelectionInEditor, getTokenCount, getWholeNotebookContent, isSelectionEmpty, markdownToComment, waitForDuration } from './utils';
|
|
23
23
|
import { UUID } from '@lumino/coreutils';
|
|
24
|
+
import * as path from 'path';
|
|
24
25
|
var CommandIDs;
|
|
25
26
|
(function (CommandIDs) {
|
|
26
27
|
CommandIDs.chatuserInput = 'notebook-intelligence:chat-user-input';
|
|
@@ -28,6 +29,7 @@ var CommandIDs;
|
|
|
28
29
|
CommandIDs.addCodeAsNewCell = 'notebook-intelligence:add-code-as-new-cell';
|
|
29
30
|
CommandIDs.createNewFile = 'notebook-intelligence:create-new-file';
|
|
30
31
|
CommandIDs.createNewNotebookFromPython = 'notebook-intelligence:create-new-notebook-from-py';
|
|
32
|
+
CommandIDs.renameNotebook = 'notebook-intelligence:rename-notebook';
|
|
31
33
|
CommandIDs.addCodeCellToNotebook = 'notebook-intelligence:add-code-cell-to-notebook';
|
|
32
34
|
CommandIDs.addMarkdownCellToNotebook = 'notebook-intelligence:add-markdown-cell-to-notebook';
|
|
33
35
|
CommandIDs.editorGenerateCode = 'notebook-intelligence:editor-generate-code';
|
|
@@ -48,6 +50,7 @@ var CommandIDs;
|
|
|
48
50
|
CommandIDs.runCellAtIndex = 'notebook-intelligence:run-cell-at-index';
|
|
49
51
|
CommandIDs.getCurrentFileContent = 'notebook-intelligence:get-current-file-content';
|
|
50
52
|
CommandIDs.setCurrentFileContent = 'notebook-intelligence:set-current-file-content';
|
|
53
|
+
CommandIDs.openMCPConfigEditor = 'notebook-intelligence:open-mcp-config-editor';
|
|
51
54
|
})(CommandIDs || (CommandIDs = {}));
|
|
52
55
|
const DOCUMENT_WATCH_INTERVAL = 1000;
|
|
53
56
|
const MAX_TOKENS = 4096;
|
|
@@ -354,6 +357,67 @@ class TelemetryEmitter {
|
|
|
354
357
|
});
|
|
355
358
|
}
|
|
356
359
|
}
|
|
360
|
+
class MCPConfigEditor {
|
|
361
|
+
constructor(docManager) {
|
|
362
|
+
this._docWidget = null;
|
|
363
|
+
this._tmpMCPConfigFilename = 'nbi.mcp.temp.json';
|
|
364
|
+
this._isOpen = false;
|
|
365
|
+
this._docManager = docManager;
|
|
366
|
+
}
|
|
367
|
+
async open() {
|
|
368
|
+
const contents = new ContentsManager();
|
|
369
|
+
const newJSONFile = await contents.newUntitled({
|
|
370
|
+
ext: '.json'
|
|
371
|
+
});
|
|
372
|
+
const mcpConfig = await NBIAPI.getMCPConfigFile();
|
|
373
|
+
try {
|
|
374
|
+
await contents.delete(this._tmpMCPConfigFilename);
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
// ignore
|
|
378
|
+
}
|
|
379
|
+
await contents.save(newJSONFile.path, {
|
|
380
|
+
content: JSON.stringify(mcpConfig, null, 2),
|
|
381
|
+
format: 'text',
|
|
382
|
+
type: 'file'
|
|
383
|
+
});
|
|
384
|
+
await contents.rename(newJSONFile.path, this._tmpMCPConfigFilename);
|
|
385
|
+
this._docWidget = this._docManager.openOrReveal(this._tmpMCPConfigFilename, 'Editor');
|
|
386
|
+
this._addListeners();
|
|
387
|
+
// tab closed
|
|
388
|
+
this._docWidget.disposed.connect((_, args) => {
|
|
389
|
+
this._removeListeners();
|
|
390
|
+
contents.delete(this._tmpMCPConfigFilename);
|
|
391
|
+
});
|
|
392
|
+
this._isOpen = true;
|
|
393
|
+
}
|
|
394
|
+
close() {
|
|
395
|
+
if (!this._isOpen) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
this._isOpen = false;
|
|
399
|
+
this._docWidget.dispose();
|
|
400
|
+
this._docWidget = null;
|
|
401
|
+
}
|
|
402
|
+
get isOpen() {
|
|
403
|
+
return this._isOpen;
|
|
404
|
+
}
|
|
405
|
+
_addListeners() {
|
|
406
|
+
this._docWidget.context.model.stateChanged.connect(this._onStateChanged, this);
|
|
407
|
+
}
|
|
408
|
+
_removeListeners() {
|
|
409
|
+
this._docWidget.context.model.stateChanged.disconnect(this._onStateChanged, this);
|
|
410
|
+
}
|
|
411
|
+
_onStateChanged(model, args) {
|
|
412
|
+
if (args.name === 'dirty' && args.newValue === false) {
|
|
413
|
+
this._onSave();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async _onSave() {
|
|
417
|
+
const mcpConfig = this._docWidget.context.model.toJSON();
|
|
418
|
+
await NBIAPI.setMCPConfigFile(mcpConfig);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
357
421
|
/**
|
|
358
422
|
* Initialization data for the @notebook-intelligence/notebook-intelligence extension.
|
|
359
423
|
*/
|
|
@@ -390,6 +454,7 @@ const plugin = {
|
|
|
390
454
|
};
|
|
391
455
|
await NBIAPI.initialize();
|
|
392
456
|
let openPopover = null;
|
|
457
|
+
let mcpConfigEditor = null;
|
|
393
458
|
completionManager.registerInlineProvider(new NBIInlineCompletionProvider(telemetryEmitter));
|
|
394
459
|
if (settingRegistry) {
|
|
395
460
|
settingRegistry
|
|
@@ -577,6 +642,32 @@ const plugin = {
|
|
|
577
642
|
return newNBFile;
|
|
578
643
|
}
|
|
579
644
|
});
|
|
645
|
+
app.commands.addCommand(CommandIDs.renameNotebook, {
|
|
646
|
+
execute: async (args) => {
|
|
647
|
+
const activeWidget = app.shell.currentWidget;
|
|
648
|
+
if (activeWidget instanceof NotebookPanel) {
|
|
649
|
+
const oldPath = activeWidget.context.path;
|
|
650
|
+
const oldParentPath = path.dirname(oldPath);
|
|
651
|
+
let newPath = path.join(oldParentPath, args.newName);
|
|
652
|
+
if (path.extname(newPath) !== '.ipynb') {
|
|
653
|
+
newPath += '.ipynb';
|
|
654
|
+
}
|
|
655
|
+
if (path.dirname(newPath) !== oldParentPath) {
|
|
656
|
+
return 'Failed to rename notebook. New path is outside the old parent directory';
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
await app.serviceManager.contents.rename(oldPath, newPath);
|
|
660
|
+
return 'Successfully renamed notebook';
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
return `Failed to rename notebook: ${error}`;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
return 'Cannot rename non notebook files';
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
});
|
|
580
671
|
const isNewEmptyNotebook = (model) => {
|
|
581
672
|
return (model.cells.length === 1 &&
|
|
582
673
|
model.cells[0].cell_type === 'code' &&
|
|
@@ -838,6 +929,10 @@ const plugin = {
|
|
|
838
929
|
onSave: () => {
|
|
839
930
|
dialog === null || dialog === void 0 ? void 0 : dialog.dispose();
|
|
840
931
|
NBIAPI.fetchCapabilities();
|
|
932
|
+
},
|
|
933
|
+
onEditMCPConfigClicked: () => {
|
|
934
|
+
dialog === null || dialog === void 0 ? void 0 : dialog.dispose();
|
|
935
|
+
app.commands.execute('notebook-intelligence:open-mcp-config-editor');
|
|
841
936
|
}
|
|
842
937
|
});
|
|
843
938
|
dialog = new Dialog({
|
|
@@ -850,6 +945,16 @@ const plugin = {
|
|
|
850
945
|
dialog.launch();
|
|
851
946
|
}
|
|
852
947
|
});
|
|
948
|
+
app.commands.addCommand(CommandIDs.openMCPConfigEditor, {
|
|
949
|
+
label: 'Open MCP Config Editor',
|
|
950
|
+
execute: args => {
|
|
951
|
+
if (mcpConfigEditor && mcpConfigEditor.isOpen) {
|
|
952
|
+
mcpConfigEditor.close();
|
|
953
|
+
}
|
|
954
|
+
mcpConfigEditor = new MCPConfigEditor(docManager);
|
|
955
|
+
mcpConfigEditor.open();
|
|
956
|
+
}
|
|
957
|
+
});
|
|
853
958
|
palette.addItem({
|
|
854
959
|
command: CommandIDs.openConfigurationDialog,
|
|
855
960
|
category: 'Notebook Intelligence'
|
package/lib/tokens.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ export declare enum RequestDataType {
|
|
|
25
25
|
export declare enum BackendMessageType {
|
|
26
26
|
StreamMessage = "stream-message",
|
|
27
27
|
StreamEnd = "stream-end",
|
|
28
|
-
RunUICommand = "run-ui-command"
|
|
28
|
+
RunUICommand = "run-ui-command",
|
|
29
|
+
GitHubCopilotLoginStatusChange = "github-copilot-login-status-change"
|
|
29
30
|
}
|
|
30
31
|
export declare enum ResponseStreamDataType {
|
|
31
32
|
LLMRaw = "llm-raw",
|
package/lib/tokens.js
CHANGED
|
@@ -16,6 +16,7 @@ export var BackendMessageType;
|
|
|
16
16
|
BackendMessageType["StreamMessage"] = "stream-message";
|
|
17
17
|
BackendMessageType["StreamEnd"] = "stream-end";
|
|
18
18
|
BackendMessageType["RunUICommand"] = "run-ui-command";
|
|
19
|
+
BackendMessageType["GitHubCopilotLoginStatusChange"] = "github-copilot-login-status-change";
|
|
19
20
|
})(BackendMessageType || (BackendMessageType = {}));
|
|
20
21
|
export var ResponseStreamDataType;
|
|
21
22
|
(function (ResponseStreamDataType) {
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -12,11 +12,10 @@ import {
|
|
|
12
12
|
IContextItem,
|
|
13
13
|
ITelemetryEvent,
|
|
14
14
|
IToolSelections,
|
|
15
|
-
RequestDataType
|
|
15
|
+
RequestDataType,
|
|
16
|
+
BackendMessageType
|
|
16
17
|
} from './tokens';
|
|
17
18
|
|
|
18
|
-
const LOGIN_STATUS_UPDATE_INTERVAL = 2000;
|
|
19
|
-
|
|
20
19
|
export enum GitHubCopilotLoginStatus {
|
|
21
20
|
NotLoggedIn = 'NOT_LOGGED_IN',
|
|
22
21
|
ActivatingDevice = 'ACTIVATING_DEVICE',
|
|
@@ -34,8 +33,8 @@ export class NBIConfig {
|
|
|
34
33
|
return this.capabilities.user_home_dir;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
get
|
|
38
|
-
return this.capabilities.
|
|
36
|
+
get userConfigDir(): string {
|
|
37
|
+
return this.capabilities.nbi_user_config_dir;
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
get llmProviders(): [any] {
|
|
@@ -50,6 +49,10 @@ export class NBIConfig {
|
|
|
50
49
|
return this.capabilities.inline_completion_models;
|
|
51
50
|
}
|
|
52
51
|
|
|
52
|
+
get defaultChatMode(): string {
|
|
53
|
+
return this.capabilities.default_chat_mode;
|
|
54
|
+
}
|
|
55
|
+
|
|
53
56
|
get chatModel(): any {
|
|
54
57
|
return this.capabilities.chat_model;
|
|
55
58
|
}
|
|
@@ -89,16 +92,22 @@ export class NBIAPI {
|
|
|
89
92
|
static _messageReceived = new Signal<unknown, any>(this);
|
|
90
93
|
static config = new NBIConfig();
|
|
91
94
|
static configChanged = this.config.changed;
|
|
95
|
+
static githubLoginStatusChanged = new Signal<unknown, void>(this);
|
|
92
96
|
|
|
93
97
|
static async initialize() {
|
|
94
98
|
await this.fetchCapabilities();
|
|
95
99
|
this.updateGitHubLoginStatus();
|
|
96
100
|
|
|
97
|
-
setInterval(() => {
|
|
98
|
-
this.updateGitHubLoginStatus();
|
|
99
|
-
}, LOGIN_STATUS_UPDATE_INTERVAL);
|
|
100
|
-
|
|
101
101
|
NBIAPI.initializeWebsocket();
|
|
102
|
+
|
|
103
|
+
this._messageReceived.connect((_, msg) => {
|
|
104
|
+
msg = JSON.parse(msg);
|
|
105
|
+
if (msg.type === BackendMessageType.GitHubCopilotLoginStatusChange) {
|
|
106
|
+
this.updateGitHubLoginStatus().then(() => {
|
|
107
|
+
this.githubLoginStatusChanged.emit();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
});
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
static async initializeWebsocket() {
|
|
@@ -250,6 +259,35 @@ export class NBIAPI {
|
|
|
250
259
|
});
|
|
251
260
|
}
|
|
252
261
|
|
|
262
|
+
static async getMCPConfigFile(): Promise<any> {
|
|
263
|
+
return new Promise<any>((resolve, reject) => {
|
|
264
|
+
requestAPI<any>('mcp-config-file', { method: 'GET' })
|
|
265
|
+
.then(async data => {
|
|
266
|
+
resolve(data);
|
|
267
|
+
})
|
|
268
|
+
.catch(reason => {
|
|
269
|
+
console.error(`Failed to get MCP config file.\n${reason}`);
|
|
270
|
+
reject(reason);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static async setMCPConfigFile(config: any): Promise<any> {
|
|
276
|
+
return new Promise<any>((resolve, reject) => {
|
|
277
|
+
requestAPI<any>('mcp-config-file', {
|
|
278
|
+
method: 'POST',
|
|
279
|
+
body: JSON.stringify(config)
|
|
280
|
+
})
|
|
281
|
+
.then(async data => {
|
|
282
|
+
resolve(data);
|
|
283
|
+
})
|
|
284
|
+
.catch(reason => {
|
|
285
|
+
console.error(`Failed to set MCP config file.\n${reason}`);
|
|
286
|
+
reject(reason);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
253
291
|
static async chatRequest(
|
|
254
292
|
messageId: string,
|
|
255
293
|
chatId: string,
|
package/src/chat-sidebar.tsx
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
import { MdOutlineCheckBoxOutlineBlank, MdCheckBox } from 'react-icons/md';
|
|
57
57
|
|
|
58
58
|
import { extractLLMGeneratedCode, isDarkTheme } from './utils';
|
|
59
|
+
import * as path from 'path';
|
|
59
60
|
|
|
60
61
|
const OPENAI_COMPATIBLE_CHAT_MODEL_ID = 'openai-compatible-chat-model';
|
|
61
62
|
const LITELLM_COMPATIBLE_CHAT_MODEL_ID = 'litellm-compatible-chat-model';
|
|
@@ -264,16 +265,26 @@ export class GitHubCopilotLoginDialogBody extends ReactWidget {
|
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
export class ConfigurationDialogBody extends ReactWidget {
|
|
267
|
-
constructor(options: {
|
|
268
|
+
constructor(options: {
|
|
269
|
+
onSave: () => void;
|
|
270
|
+
onEditMCPConfigClicked: () => void;
|
|
271
|
+
}) {
|
|
268
272
|
super();
|
|
269
273
|
|
|
274
|
+
this._onEditMCPConfigClicked = options.onEditMCPConfigClicked;
|
|
270
275
|
this._onSave = options.onSave;
|
|
271
276
|
}
|
|
272
277
|
|
|
273
278
|
render(): JSX.Element {
|
|
274
|
-
return
|
|
279
|
+
return (
|
|
280
|
+
<ConfigurationDialogBodyComponent
|
|
281
|
+
onEditMCPConfigClicked={this._onEditMCPConfigClicked}
|
|
282
|
+
onSave={this._onSave}
|
|
283
|
+
/>
|
|
284
|
+
);
|
|
275
285
|
}
|
|
276
286
|
|
|
287
|
+
private _onEditMCPConfigClicked: () => void;
|
|
277
288
|
private _onSave: () => void;
|
|
278
289
|
}
|
|
279
290
|
|
|
@@ -725,7 +736,8 @@ function SidebarComponent(props: any) {
|
|
|
725
736
|
useState<IActiveDocumentInfo | null>(null);
|
|
726
737
|
const [currentFileContextTitle, setCurrentFileContextTitle] = useState('');
|
|
727
738
|
const telemetryEmitter: ITelemetryEmitter = props.getTelemetryEmitter();
|
|
728
|
-
const [chatMode, setChatMode] = useState(
|
|
739
|
+
const [chatMode, setChatMode] = useState(NBIAPI.config.defaultChatMode);
|
|
740
|
+
|
|
729
741
|
const [toolSelectionTitle, setToolSelectionTitle] =
|
|
730
742
|
useState('Tool selection');
|
|
731
743
|
const [selectedToolCount, setSelectedToolCount] = useState(0);
|
|
@@ -2497,6 +2509,7 @@ function ConfigurationDialogBodyComponent(props: any) {
|
|
|
2497
2509
|
|
|
2498
2510
|
const handleSaveClick = async () => {
|
|
2499
2511
|
const config: any = {
|
|
2512
|
+
default_chat_mode: defaultChatMode,
|
|
2500
2513
|
chat_model: {
|
|
2501
2514
|
provider: chatModelProvider,
|
|
2502
2515
|
model: chatModel,
|
|
@@ -2531,6 +2544,9 @@ function ConfigurationDialogBodyComponent(props: any) {
|
|
|
2531
2544
|
);
|
|
2532
2545
|
const [inlineCompletionModelProvider, setInlineCompletionModelProvider] =
|
|
2533
2546
|
useState(nbiConfig.inlineCompletionModel.provider || 'none');
|
|
2547
|
+
const [defaultChatMode, setDefaultChatMode] = useState<string>(
|
|
2548
|
+
nbiConfig.defaultChatMode
|
|
2549
|
+
);
|
|
2534
2550
|
const [chatModel, setChatModel] = useState<string>(nbiConfig.chatModel.model);
|
|
2535
2551
|
const [chatModelProperties, setChatModelProperties] = useState<any[]>([]);
|
|
2536
2552
|
const [inlineCompletionModelProperties, setInlineCompletionModelProperties] =
|
|
@@ -2629,6 +2645,27 @@ function ConfigurationDialogBodyComponent(props: any) {
|
|
|
2629
2645
|
return (
|
|
2630
2646
|
<div className="config-dialog">
|
|
2631
2647
|
<div className="config-dialog-body">
|
|
2648
|
+
<div className="model-config-section">
|
|
2649
|
+
<div className="model-config-section-header">Default chat mode</div>
|
|
2650
|
+
<div className="model-config-section-body">
|
|
2651
|
+
<div className="model-config-section-row">
|
|
2652
|
+
<div className="model-config-section-column">
|
|
2653
|
+
<div>
|
|
2654
|
+
<select
|
|
2655
|
+
className="jp-mod-styled"
|
|
2656
|
+
value={defaultChatMode}
|
|
2657
|
+
onChange={event => setDefaultChatMode(event.target.value)}
|
|
2658
|
+
>
|
|
2659
|
+
<option value="ask">Ask</option>
|
|
2660
|
+
<option value="agent">Agent</option>
|
|
2661
|
+
</select>
|
|
2662
|
+
</div>
|
|
2663
|
+
</div>
|
|
2664
|
+
<div className="model-config-section-column"> </div>
|
|
2665
|
+
</div>
|
|
2666
|
+
</div>
|
|
2667
|
+
</div>
|
|
2668
|
+
|
|
2632
2669
|
<div className="model-config-section">
|
|
2633
2670
|
<div className="model-config-section-header">Chat model</div>
|
|
2634
2671
|
<div className="model-config-section-body">
|
|
@@ -2889,7 +2926,11 @@ function ConfigurationDialogBodyComponent(props: any) {
|
|
|
2889
2926
|
|
|
2890
2927
|
<div className="model-config-section">
|
|
2891
2928
|
<div className="model-config-section-header">
|
|
2892
|
-
MCP Servers
|
|
2929
|
+
MCP Servers ({mcpServerNames.length}) [
|
|
2930
|
+
<a href="javascript:void(0)" onClick={props.onEditMCPConfigClicked}>
|
|
2931
|
+
edit
|
|
2932
|
+
</a>
|
|
2933
|
+
]
|
|
2893
2934
|
</div>
|
|
2894
2935
|
<div className="model-config-section-body">
|
|
2895
2936
|
<div className="model-config-section-row">
|
|
@@ -2927,11 +2968,37 @@ function ConfigurationDialogBodyComponent(props: any) {
|
|
|
2927
2968
|
<span
|
|
2928
2969
|
className="user-code-span"
|
|
2929
2970
|
onClick={() => {
|
|
2930
|
-
navigator.clipboard.writeText(
|
|
2971
|
+
navigator.clipboard.writeText(
|
|
2972
|
+
path.join(NBIAPI.config.userConfigDir, 'config.json')
|
|
2973
|
+
);
|
|
2974
|
+
return true;
|
|
2975
|
+
}}
|
|
2976
|
+
>
|
|
2977
|
+
{path.join(NBIAPI.config.userConfigDir, 'config.json')}{' '}
|
|
2978
|
+
<span
|
|
2979
|
+
className="copy-icon"
|
|
2980
|
+
dangerouslySetInnerHTML={{ __html: copySvgstr }}
|
|
2981
|
+
></span>
|
|
2982
|
+
</span>
|
|
2983
|
+
</div>
|
|
2984
|
+
</div>
|
|
2985
|
+
</div>
|
|
2986
|
+
<div className="model-config-section-header">
|
|
2987
|
+
MCP config file path
|
|
2988
|
+
</div>
|
|
2989
|
+
<div className="model-config-section-body">
|
|
2990
|
+
<div className="model-config-section-row">
|
|
2991
|
+
<div className="model-config-section-column">
|
|
2992
|
+
<span
|
|
2993
|
+
className="user-code-span"
|
|
2994
|
+
onClick={() => {
|
|
2995
|
+
navigator.clipboard.writeText(
|
|
2996
|
+
path.join(NBIAPI.config.userConfigDir, 'mcp.json')
|
|
2997
|
+
);
|
|
2931
2998
|
return true;
|
|
2932
2999
|
}}
|
|
2933
3000
|
>
|
|
2934
|
-
{NBIAPI.config.
|
|
3001
|
+
{path.join(NBIAPI.config.userConfigDir, 'mcp.json')}{' '}
|
|
2935
3002
|
<span
|
|
2936
3003
|
className="copy-icon"
|
|
2937
3004
|
dangerouslySetInnerHTML={{ __html: copySvgstr }}
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '@jupyterlab/application';
|
|
8
8
|
|
|
9
9
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
10
|
-
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
10
|
+
import { DocumentWidget, IDocumentWidget } from '@jupyterlab/docregistry';
|
|
11
11
|
|
|
12
12
|
import { Dialog, ICommandPalette } from '@jupyterlab/apputils';
|
|
13
13
|
import { IMainMenu } from '@jupyterlab/mainmenu';
|
|
@@ -80,6 +80,8 @@ import {
|
|
|
80
80
|
} from './utils';
|
|
81
81
|
import { UUID } from '@lumino/coreutils';
|
|
82
82
|
|
|
83
|
+
import * as path from 'path';
|
|
84
|
+
|
|
83
85
|
namespace CommandIDs {
|
|
84
86
|
export const chatuserInput = 'notebook-intelligence:chat-user-input';
|
|
85
87
|
export const insertAtCursor = 'notebook-intelligence:insert-at-cursor';
|
|
@@ -87,6 +89,7 @@ namespace CommandIDs {
|
|
|
87
89
|
export const createNewFile = 'notebook-intelligence:create-new-file';
|
|
88
90
|
export const createNewNotebookFromPython =
|
|
89
91
|
'notebook-intelligence:create-new-notebook-from-py';
|
|
92
|
+
export const renameNotebook = 'notebook-intelligence:rename-notebook';
|
|
90
93
|
export const addCodeCellToNotebook =
|
|
91
94
|
'notebook-intelligence:add-code-cell-to-notebook';
|
|
92
95
|
export const addMarkdownCellToNotebook =
|
|
@@ -121,6 +124,8 @@ namespace CommandIDs {
|
|
|
121
124
|
'notebook-intelligence:get-current-file-content';
|
|
122
125
|
export const setCurrentFileContent =
|
|
123
126
|
'notebook-intelligence:set-current-file-content';
|
|
127
|
+
export const openMCPConfigEditor =
|
|
128
|
+
'notebook-intelligence:open-mcp-config-editor';
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
const DOCUMENT_WATCH_INTERVAL = 1000;
|
|
@@ -511,6 +516,87 @@ class TelemetryEmitter implements ITelemetryEmitter {
|
|
|
511
516
|
private _listeners: Set<ITelemetryListener> = new Set<ITelemetryListener>();
|
|
512
517
|
}
|
|
513
518
|
|
|
519
|
+
class MCPConfigEditor {
|
|
520
|
+
constructor(docManager: IDocumentManager) {
|
|
521
|
+
this._docManager = docManager;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async open() {
|
|
525
|
+
const contents = new ContentsManager();
|
|
526
|
+
const newJSONFile = await contents.newUntitled({
|
|
527
|
+
ext: '.json'
|
|
528
|
+
});
|
|
529
|
+
const mcpConfig = await NBIAPI.getMCPConfigFile();
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
await contents.delete(this._tmpMCPConfigFilename);
|
|
533
|
+
} catch (error) {
|
|
534
|
+
// ignore
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
await contents.save(newJSONFile.path, {
|
|
538
|
+
content: JSON.stringify(mcpConfig, null, 2),
|
|
539
|
+
format: 'text',
|
|
540
|
+
type: 'file'
|
|
541
|
+
});
|
|
542
|
+
await contents.rename(newJSONFile.path, this._tmpMCPConfigFilename);
|
|
543
|
+
this._docWidget = this._docManager.openOrReveal(
|
|
544
|
+
this._tmpMCPConfigFilename,
|
|
545
|
+
'Editor'
|
|
546
|
+
);
|
|
547
|
+
this._addListeners();
|
|
548
|
+
// tab closed
|
|
549
|
+
this._docWidget.disposed.connect((_, args) => {
|
|
550
|
+
this._removeListeners();
|
|
551
|
+
contents.delete(this._tmpMCPConfigFilename);
|
|
552
|
+
});
|
|
553
|
+
this._isOpen = true;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
close() {
|
|
557
|
+
if (!this._isOpen) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
this._isOpen = false;
|
|
561
|
+
this._docWidget.dispose();
|
|
562
|
+
this._docWidget = null;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
get isOpen(): boolean {
|
|
566
|
+
return this._isOpen;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private _addListeners() {
|
|
570
|
+
this._docWidget.context.model.stateChanged.connect(
|
|
571
|
+
this._onStateChanged,
|
|
572
|
+
this
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private _removeListeners() {
|
|
577
|
+
this._docWidget.context.model.stateChanged.disconnect(
|
|
578
|
+
this._onStateChanged,
|
|
579
|
+
this
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
private _onStateChanged(model: any, args: any) {
|
|
584
|
+
if (args.name === 'dirty' && args.newValue === false) {
|
|
585
|
+
this._onSave();
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private async _onSave() {
|
|
590
|
+
const mcpConfig = this._docWidget.context.model.toJSON();
|
|
591
|
+
await NBIAPI.setMCPConfigFile(mcpConfig);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
private _docManager: IDocumentManager;
|
|
595
|
+
private _docWidget: IDocumentWidget = null;
|
|
596
|
+
private _tmpMCPConfigFilename = 'nbi.mcp.temp.json';
|
|
597
|
+
private _isOpen = false;
|
|
598
|
+
}
|
|
599
|
+
|
|
514
600
|
/**
|
|
515
601
|
* Initialization data for the @notebook-intelligence/notebook-intelligence extension.
|
|
516
602
|
*/
|
|
@@ -564,6 +650,7 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
564
650
|
await NBIAPI.initialize();
|
|
565
651
|
|
|
566
652
|
let openPopover: InlinePromptWidget | null = null;
|
|
653
|
+
let mcpConfigEditor: MCPConfigEditor | null = null;
|
|
567
654
|
|
|
568
655
|
completionManager.registerInlineProvider(
|
|
569
656
|
new NBIInlineCompletionProvider(telemetryEmitter)
|
|
@@ -786,6 +873,33 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
786
873
|
}
|
|
787
874
|
});
|
|
788
875
|
|
|
876
|
+
app.commands.addCommand(CommandIDs.renameNotebook, {
|
|
877
|
+
execute: async args => {
|
|
878
|
+
const activeWidget = app.shell.currentWidget;
|
|
879
|
+
if (activeWidget instanceof NotebookPanel) {
|
|
880
|
+
const oldPath = activeWidget.context.path;
|
|
881
|
+
const oldParentPath = path.dirname(oldPath);
|
|
882
|
+
let newPath = path.join(oldParentPath, args.newName as string);
|
|
883
|
+
if (path.extname(newPath) !== '.ipynb') {
|
|
884
|
+
newPath += '.ipynb';
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (path.dirname(newPath) !== oldParentPath) {
|
|
888
|
+
return 'Failed to rename notebook. New path is outside the old parent directory';
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
try {
|
|
892
|
+
await app.serviceManager.contents.rename(oldPath, newPath);
|
|
893
|
+
return 'Successfully renamed notebook';
|
|
894
|
+
} catch (error) {
|
|
895
|
+
return `Failed to rename notebook: ${error}`;
|
|
896
|
+
}
|
|
897
|
+
} else {
|
|
898
|
+
return 'Cannot rename non notebook files';
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
|
|
789
903
|
const isNewEmptyNotebook = (model: ISharedNotebook) => {
|
|
790
904
|
return (
|
|
791
905
|
model.cells.length === 1 &&
|
|
@@ -1122,6 +1236,12 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
1122
1236
|
onSave: () => {
|
|
1123
1237
|
dialog?.dispose();
|
|
1124
1238
|
NBIAPI.fetchCapabilities();
|
|
1239
|
+
},
|
|
1240
|
+
onEditMCPConfigClicked: () => {
|
|
1241
|
+
dialog?.dispose();
|
|
1242
|
+
app.commands.execute(
|
|
1243
|
+
'notebook-intelligence:open-mcp-config-editor'
|
|
1244
|
+
);
|
|
1125
1245
|
}
|
|
1126
1246
|
});
|
|
1127
1247
|
dialog = new Dialog({
|
|
@@ -1136,6 +1256,17 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
|
|
|
1136
1256
|
}
|
|
1137
1257
|
});
|
|
1138
1258
|
|
|
1259
|
+
app.commands.addCommand(CommandIDs.openMCPConfigEditor, {
|
|
1260
|
+
label: 'Open MCP Config Editor',
|
|
1261
|
+
execute: args => {
|
|
1262
|
+
if (mcpConfigEditor && mcpConfigEditor.isOpen) {
|
|
1263
|
+
mcpConfigEditor.close();
|
|
1264
|
+
}
|
|
1265
|
+
mcpConfigEditor = new MCPConfigEditor(docManager);
|
|
1266
|
+
mcpConfigEditor.open();
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1139
1270
|
palette.addItem({
|
|
1140
1271
|
command: CommandIDs.openConfigurationDialog,
|
|
1141
1272
|
category: 'Notebook Intelligence'
|
package/src/tokens.ts
CHANGED
|
@@ -31,7 +31,8 @@ export enum RequestDataType {
|
|
|
31
31
|
export enum BackendMessageType {
|
|
32
32
|
StreamMessage = 'stream-message',
|
|
33
33
|
StreamEnd = 'stream-end',
|
|
34
|
-
RunUICommand = 'run-ui-command'
|
|
34
|
+
RunUICommand = 'run-ui-command',
|
|
35
|
+
GitHubCopilotLoginStatusChange = 'github-copilot-login-status-change'
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export enum ResponseStreamDataType {
|
package/style/base.css
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.sidebar-user-input {
|
|
33
|
-
height:
|
|
33
|
+
height: auto;
|
|
34
34
|
padding: 5px;
|
|
35
35
|
display: flex;
|
|
36
36
|
flex-direction: column;
|
|
@@ -67,6 +67,11 @@
|
|
|
67
67
|
border: none;
|
|
68
68
|
resize: none;
|
|
69
69
|
background: none;
|
|
70
|
+
/* stylelint-disable */
|
|
71
|
+
field-sizing: content;
|
|
72
|
+
/* stylelint-enable */
|
|
73
|
+
min-height: 36px;
|
|
74
|
+
max-height: 200px;
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
.sidebar-user-input .user-input-context-row {
|