jupyterlab_commands_extension 1.1.4 → 1.1.7

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 CHANGED
@@ -61,11 +61,13 @@ jupyterlab-commands activity
61
61
  jupyterlab-commands execute file:download --args '{"path": "data/report.csv"}'
62
62
  ```
63
63
 
64
+ The optional `--purpose` flag names the purpose of an execution in at most 25 words, and the optional `--owner` flag names the agent or project that asked for it. The CLI treats both as optional; the agent skill requires them. An empty value, or a purpose over 25 words, is a usage error.
65
+
64
66
  Exit codes: 0 success, 2 discovery or usage error, 3 no browser client, 4 command not allowed, 5 execution error.
65
67
 
66
68
  The browser tab is the executor: the command runs in the lab, not in the terminal.
67
69
 
68
- Every execute request reaches the JupyterLab server log: the outcome, the command ID, the arguments as compact JSON truncated to 500 characters, the elapsed milliseconds and the caller's remote IP, at INFO on success and WARNING on refusal or failure.
70
+ Every execute request reaches the JupyterLab server log: the outcome, the command ID, the arguments as compact JSON truncated to 500 characters, the elapsed milliseconds, the caller's remote IP, and the purpose and owner when given, at INFO on success and WARNING on refusal or failure.
69
71
 
70
72
  ## Allowlist
71
73
 
package/lib/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';
2
2
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
3
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
3
4
  import { textEditorIcon } from '@jupyterlab/ui-components';
5
+ // eslint-disable-next-line jupyter/prefer-lazy-imports -- the relay must start with the plugin: the CLI reaches this tab only through it
4
6
  import { RelayClient } from './relay';
5
- import { CommandsReferenceWidget } from './widget';
6
7
  /**
7
8
  * Command IDs for the extension
8
9
  */
@@ -30,8 +31,9 @@ const plugin = {
30
31
  id: PLUGIN_ID,
31
32
  description: 'Display all available JupyterLab commands with their full reference IDs and arguments in a dedicated tab',
32
33
  autoStart: true,
33
- requires: [ICommandPalette, ISettingRegistry],
34
- activate: async (app, palette, settingRegistry) => {
34
+ requires: [ICommandPalette, ISettingRegistry, ITranslator],
35
+ activate: async (app, palette, settingRegistry, translator) => {
36
+ const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab_commands_extension');
35
37
  console.log('JupyterLab extension jupyterlab_commands_extension is activated!');
36
38
  let settings = null;
37
39
  try {
@@ -42,15 +44,17 @@ const plugin = {
42
44
  }
43
45
  // Register the command
44
46
  app.commands.addCommand(CommandIDs.open, {
45
- label: 'Show Commands Reference',
46
- caption: 'Display all available JupyterLab commands with their IDs and arguments',
47
- execute: () => {
47
+ label: trans.__('Show Commands Reference'),
48
+ caption: trans.__('Display all available JupyterLab commands with their IDs and arguments'),
49
+ describedBy: () => ({}),
50
+ execute: async () => {
48
51
  // Create widget if it doesn't exist or was disposed
49
52
  if (!widget || widget.isDisposed) {
50
- const content = new CommandsReferenceWidget(app.commands, settings, settingRegistry);
53
+ const { CommandsReferenceWidget } = await import('./widget');
54
+ const content = new CommandsReferenceWidget(app.commands, settings, settingRegistry, trans);
51
55
  widget = new MainAreaWidget({ content });
52
56
  widget.id = 'jp-commands-reference';
53
- widget.title.label = 'Commands Reference';
57
+ widget.title.label = trans.__('Commands Reference');
54
58
  widget.title.icon = textEditorIcon;
55
59
  widget.title.closable = true;
56
60
  // Clear reference when widget is disposed
@@ -69,13 +73,13 @@ const plugin = {
69
73
  // Add command to palette under Help category
70
74
  palette.addItem({
71
75
  command: CommandIDs.open,
72
- category: 'Help'
76
+ category: trans.__('Help')
73
77
  });
74
78
  // Download a file the contents API serves to the computer that runs
75
79
  // the browser tab.
76
80
  app.commands.addCommand(CommandIDs.download, {
77
- label: 'Download File',
78
- caption: 'Download a file from the server to the local computer',
81
+ label: trans.__('Download File'),
82
+ caption: trans.__('Download a file from the server to the local computer'),
79
83
  describedBy: () => ({
80
84
  args: {
81
85
  type: 'object',
package/lib/relay.js CHANGED
@@ -1,4 +1,4 @@
1
- import { PageConfig, URLExt } from '@jupyterlab/coreutils';
1
+ import { URLExt } from '@jupyterlab/coreutils';
2
2
  import { ServerConnection } from '@jupyterlab/services';
3
3
  import { getAllowedCommands } from './allowlist';
4
4
  import { collectCommandDescriptors } from './commands';
@@ -228,7 +228,7 @@ export class RelayClient {
228
228
  * Build the URL of one relay endpoint.
229
229
  */
230
230
  _url(endpoint) {
231
- return URLExt.join(PageConfig.getBaseUrl(), API_NAMESPACE, 'api', endpoint);
231
+ return URLExt.join(this._serverSettings.baseUrl, API_NAMESPACE, 'api', endpoint);
232
232
  }
233
233
  _get(endpoint) {
234
234
  const url = this._url(endpoint) + '?client_id=' + encodeURIComponent(this._clientId);
package/lib/widget.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Widget } from '@lumino/widgets';
2
2
  import { CommandRegistry } from '@lumino/commands';
3
3
  import type { ISettingRegistry } from '@jupyterlab/settingregistry';
4
+ import type { TranslationBundle } from '@jupyterlab/translation';
4
5
  /**
5
6
  * A widget that displays all registered JupyterLab commands
6
7
  */
@@ -13,7 +14,8 @@ export declare class CommandsReferenceWidget extends Widget {
13
14
  private _tbody;
14
15
  private _commandsCache;
15
16
  private _argsCache;
16
- constructor(commands: CommandRegistry, settings?: ISettingRegistry.ISettings | null, registry?: ISettingRegistry | null);
17
+ private _trans;
18
+ constructor(commands: CommandRegistry, settings?: ISettingRegistry.ISettings | null, registry?: ISettingRegistry | null, trans?: TranslationBundle);
17
19
  /**
18
20
  * Dispose the widget and drop the settings listener.
19
21
  */
package/lib/widget.js CHANGED
@@ -1,17 +1,19 @@
1
1
  import { Widget } from '@lumino/widgets';
2
+ import { nullTranslator } from '@jupyterlab/translation';
2
3
  import { addAllowedCommand, getAllowedCommands } from './allowlist';
3
4
  import { collectCommands, describeCommand } from './commands';
4
5
  /**
5
6
  * A widget that displays all registered JupyterLab commands
6
7
  */
7
8
  export class CommandsReferenceWidget extends Widget {
8
- constructor(commands, settings = null, registry = null) {
9
+ constructor(commands, settings = null, registry = null, trans = nullTranslator.load('jupyterlab_commands_extension')) {
9
10
  super();
10
11
  this._commandsCache = [];
11
12
  this._argsCache = new Map();
12
13
  this._commands = commands;
13
14
  this._settings = settings;
14
15
  this._registry = registry;
16
+ this._trans = trans;
15
17
  this.addClass('jp-CommandsReferenceWidget');
16
18
  if (this._settings) {
17
19
  this._settings.changed.connect(this._onSettingsChanged, this);
@@ -46,7 +48,7 @@ export class CommandsReferenceWidget extends Widget {
46
48
  // Search input
47
49
  this._searchInput = document.createElement('input');
48
50
  this._searchInput.type = 'text';
49
- this._searchInput.placeholder = 'Filter commands...';
51
+ this._searchInput.placeholder = this._trans.__('Filter commands...');
50
52
  this._searchInput.className =
51
53
  'jp-CommandsReferenceWidget-search jp-mod-styled';
52
54
  this._searchInput.addEventListener('input', () => {
@@ -71,11 +73,11 @@ export class CommandsReferenceWidget extends Widget {
71
73
  const thead = document.createElement('thead');
72
74
  const headerRow = document.createElement('tr');
73
75
  const headers = [
74
- 'Command ID',
75
- 'Label',
76
- 'Description',
77
- 'Arguments',
78
- 'Command Executor'
76
+ this._trans.__('Command ID'),
77
+ this._trans.__('Label'),
78
+ this._trans.__('Description'),
79
+ this._trans.__('Arguments'),
80
+ this._trans.__('Command Executor')
79
81
  ];
80
82
  for (const headerText of headers) {
81
83
  const th = document.createElement('th');
@@ -187,11 +189,11 @@ export class CommandsReferenceWidget extends Widget {
187
189
  void this._addToExecutor(cmd.id, button);
188
190
  });
189
191
  if (this._isAllowed(cmd.id)) {
190
- button.textContent = 'Added';
192
+ button.textContent = this._trans.__('Added');
191
193
  button.disabled = true;
192
194
  }
193
195
  else {
194
- button.textContent = 'Add to command executor';
196
+ button.textContent = this._trans.__('Add to command executor');
195
197
  button.disabled = !this._settings || !this._registry;
196
198
  }
197
199
  executorCell.appendChild(button);
@@ -233,11 +235,11 @@ export class CommandsReferenceWidget extends Widget {
233
235
  button.disabled = true;
234
236
  try {
235
237
  await addAllowedCommand(this._settings, this._registry, commandId);
236
- button.textContent = 'Added';
238
+ button.textContent = this._trans.__('Added');
237
239
  }
238
240
  catch (error) {
239
241
  console.error('Commands Reference: cannot update allowedCommands', error);
240
- button.textContent = 'Add to command executor';
242
+ button.textContent = this._trans.__('Add to command executor');
241
243
  button.disabled = false;
242
244
  }
243
245
  }
@@ -249,7 +251,9 @@ export class CommandsReferenceWidget extends Widget {
249
251
  for (const button of Array.from(buttons)) {
250
252
  const commandId = button.dataset.commandId || '';
251
253
  const allowed = this._isAllowed(commandId);
252
- button.textContent = allowed ? 'Added' : 'Add to command executor';
254
+ button.textContent = allowed
255
+ ? this._trans.__('Added')
256
+ : this._trans.__('Add to command executor');
253
257
  button.disabled =
254
258
  allowed || this._settings === null || this._registry === null;
255
259
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_commands_extension",
3
- "version": "1.1.4",
3
+ "version": "1.1.7",
4
4
  "description": "Display all available JupyterLab commands with their full reference IDs and arguments in a dedicated tab",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -62,6 +62,7 @@
62
62
  "@jupyterlab/coreutils": "^6.6.0",
63
63
  "@jupyterlab/services": "^7.6.0",
64
64
  "@jupyterlab/settingregistry": "^4.6.0",
65
+ "@jupyterlab/translation": "^4.6.4",
65
66
  "@jupyterlab/ui-components": "^4.6.0"
66
67
  },
67
68
  "devDependencies": {
@@ -22,7 +22,8 @@
22
22
  "terminal:create-new",
23
23
  "launcher:create",
24
24
  "jupyterlab-commands-reference:open",
25
- "file:download"
25
+ "file:download",
26
+ "workspace-ui:open"
26
27
  ]
27
28
  }
28
29
  },
package/src/index.ts CHANGED
@@ -7,10 +7,13 @@ import { ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';
7
7
 
8
8
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
9
9
 
10
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
11
+
10
12
  import { textEditorIcon } from '@jupyterlab/ui-components';
11
13
 
14
+ // eslint-disable-next-line jupyter/prefer-lazy-imports -- the relay must start with the plugin: the CLI reaches this tab only through it
12
15
  import { RelayClient } from './relay';
13
- import { CommandsReferenceWidget } from './widget';
16
+ import type { CommandsReferenceWidget } from './widget';
14
17
 
15
18
  /**
16
19
  * Command IDs for the extension
@@ -43,12 +46,17 @@ const plugin: JupyterFrontEndPlugin<void> = {
43
46
  description:
44
47
  'Display all available JupyterLab commands with their full reference IDs and arguments in a dedicated tab',
45
48
  autoStart: true,
46
- requires: [ICommandPalette, ISettingRegistry],
49
+ requires: [ICommandPalette, ISettingRegistry, ITranslator],
47
50
  activate: async (
48
51
  app: JupyterFrontEnd,
49
52
  palette: ICommandPalette,
50
- settingRegistry: ISettingRegistry
53
+ settingRegistry: ISettingRegistry,
54
+ translator: ITranslator | null
51
55
  ) => {
56
+ const trans = (translator ?? nullTranslator).load(
57
+ 'jupyterlab_commands_extension'
58
+ );
59
+
52
60
  console.log(
53
61
  'JupyterLab extension jupyterlab_commands_extension is activated!'
54
62
  );
@@ -65,20 +73,24 @@ const plugin: JupyterFrontEndPlugin<void> = {
65
73
 
66
74
  // Register the command
67
75
  app.commands.addCommand(CommandIDs.open, {
68
- label: 'Show Commands Reference',
69
- caption:
70
- 'Display all available JupyterLab commands with their IDs and arguments',
71
- execute: () => {
76
+ label: trans.__('Show Commands Reference'),
77
+ caption: trans.__(
78
+ 'Display all available JupyterLab commands with their IDs and arguments'
79
+ ),
80
+ describedBy: () => ({}),
81
+ execute: async () => {
72
82
  // Create widget if it doesn't exist or was disposed
73
83
  if (!widget || widget.isDisposed) {
84
+ const { CommandsReferenceWidget } = await import('./widget');
74
85
  const content = new CommandsReferenceWidget(
75
86
  app.commands,
76
87
  settings,
77
- settingRegistry
88
+ settingRegistry,
89
+ trans
78
90
  );
79
91
  widget = new MainAreaWidget({ content });
80
92
  widget.id = 'jp-commands-reference';
81
- widget.title.label = 'Commands Reference';
93
+ widget.title.label = trans.__('Commands Reference');
82
94
  widget.title.icon = textEditorIcon;
83
95
  widget.title.closable = true;
84
96
 
@@ -101,14 +113,16 @@ const plugin: JupyterFrontEndPlugin<void> = {
101
113
  // Add command to palette under Help category
102
114
  palette.addItem({
103
115
  command: CommandIDs.open,
104
- category: 'Help'
116
+ category: trans.__('Help')
105
117
  });
106
118
 
107
119
  // Download a file the contents API serves to the computer that runs
108
120
  // the browser tab.
109
121
  app.commands.addCommand(CommandIDs.download, {
110
- label: 'Download File',
111
- caption: 'Download a file from the server to the local computer',
122
+ label: trans.__('Download File'),
123
+ caption: trans.__(
124
+ 'Download a file from the server to the local computer'
125
+ ),
112
126
  describedBy: () => ({
113
127
  args: {
114
128
  type: 'object',
package/src/relay.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { JupyterFrontEnd } from '@jupyterlab/application';
2
- import { PageConfig, URLExt } from '@jupyterlab/coreutils';
2
+ import { URLExt } from '@jupyterlab/coreutils';
3
3
  import { ServerConnection } from '@jupyterlab/services';
4
4
  import type { ISettingRegistry } from '@jupyterlab/settingregistry';
5
5
  import type { CommandRegistry } from '@lumino/commands';
@@ -260,7 +260,12 @@ export class RelayClient {
260
260
  * Build the URL of one relay endpoint.
261
261
  */
262
262
  private _url(endpoint: string): string {
263
- return URLExt.join(PageConfig.getBaseUrl(), API_NAMESPACE, 'api', endpoint);
263
+ return URLExt.join(
264
+ this._serverSettings.baseUrl,
265
+ API_NAMESPACE,
266
+ 'api',
267
+ endpoint
268
+ );
264
269
  }
265
270
 
266
271
  private _get(endpoint: string): Promise<any> {
package/src/widget.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Widget } from '@lumino/widgets';
2
2
  import { CommandRegistry } from '@lumino/commands';
3
3
  import type { ISettingRegistry } from '@jupyterlab/settingregistry';
4
+ import { nullTranslator } from '@jupyterlab/translation';
5
+ import type { TranslationBundle } from '@jupyterlab/translation';
4
6
 
5
7
  import { addAllowedCommand, getAllowedCommands } from './allowlist';
6
8
  import { collectCommands, describeCommand, ICommandInfo } from './commands';
@@ -17,16 +19,21 @@ export class CommandsReferenceWidget extends Widget {
17
19
  private _tbody!: HTMLTableSectionElement;
18
20
  private _commandsCache: ICommandInfo[] = [];
19
21
  private _argsCache: Map<string, string> = new Map();
22
+ private _trans: TranslationBundle;
20
23
 
21
24
  constructor(
22
25
  commands: CommandRegistry,
23
26
  settings: ISettingRegistry.ISettings | null = null,
24
- registry: ISettingRegistry | null = null
27
+ registry: ISettingRegistry | null = null,
28
+ trans: TranslationBundle = nullTranslator.load(
29
+ 'jupyterlab_commands_extension'
30
+ )
25
31
  ) {
26
32
  super();
27
33
  this._commands = commands;
28
34
  this._settings = settings;
29
35
  this._registry = registry;
36
+ this._trans = trans;
30
37
  this.addClass('jp-CommandsReferenceWidget');
31
38
 
32
39
  if (this._settings) {
@@ -68,7 +75,7 @@ export class CommandsReferenceWidget extends Widget {
68
75
  // Search input
69
76
  this._searchInput = document.createElement('input');
70
77
  this._searchInput.type = 'text';
71
- this._searchInput.placeholder = 'Filter commands...';
78
+ this._searchInput.placeholder = this._trans.__('Filter commands...');
72
79
  this._searchInput.className =
73
80
  'jp-CommandsReferenceWidget-search jp-mod-styled';
74
81
  this._searchInput.addEventListener('input', () => {
@@ -98,11 +105,11 @@ export class CommandsReferenceWidget extends Widget {
98
105
  const thead = document.createElement('thead');
99
106
  const headerRow = document.createElement('tr');
100
107
  const headers = [
101
- 'Command ID',
102
- 'Label',
103
- 'Description',
104
- 'Arguments',
105
- 'Command Executor'
108
+ this._trans.__('Command ID'),
109
+ this._trans.__('Label'),
110
+ this._trans.__('Description'),
111
+ this._trans.__('Arguments'),
112
+ this._trans.__('Command Executor')
106
113
  ];
107
114
  for (const headerText of headers) {
108
115
  const th = document.createElement('th');
@@ -235,10 +242,10 @@ export class CommandsReferenceWidget extends Widget {
235
242
  void this._addToExecutor(cmd.id, button);
236
243
  });
237
244
  if (this._isAllowed(cmd.id)) {
238
- button.textContent = 'Added';
245
+ button.textContent = this._trans.__('Added');
239
246
  button.disabled = true;
240
247
  } else {
241
- button.textContent = 'Add to command executor';
248
+ button.textContent = this._trans.__('Add to command executor');
242
249
  button.disabled = !this._settings || !this._registry;
243
250
  }
244
251
  executorCell.appendChild(button);
@@ -295,10 +302,10 @@ export class CommandsReferenceWidget extends Widget {
295
302
  button.disabled = true;
296
303
  try {
297
304
  await addAllowedCommand(this._settings, this._registry, commandId);
298
- button.textContent = 'Added';
305
+ button.textContent = this._trans.__('Added');
299
306
  } catch (error) {
300
307
  console.error('Commands Reference: cannot update allowedCommands', error);
301
- button.textContent = 'Add to command executor';
308
+ button.textContent = this._trans.__('Add to command executor');
302
309
  button.disabled = false;
303
310
  }
304
311
  }
@@ -313,7 +320,9 @@ export class CommandsReferenceWidget extends Widget {
313
320
  for (const button of Array.from(buttons)) {
314
321
  const commandId = button.dataset.commandId || '';
315
322
  const allowed = this._isAllowed(commandId);
316
- button.textContent = allowed ? 'Added' : 'Add to command executor';
323
+ button.textContent = allowed
324
+ ? this._trans.__('Added')
325
+ : this._trans.__('Add to command executor');
317
326
  button.disabled =
318
327
  allowed || this._settings === null || this._registry === null;
319
328
  }