jupyterlab_claude_code_extension 1.0.21 → 1.0.26
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/lib/icons.d.ts +1 -0
- package/lib/icons.js +7 -0
- package/lib/index.js +3 -0
- package/lib/types.d.ts +1 -0
- package/lib/widget.d.ts +8 -5
- package/lib/widget.js +29 -11
- package/package.json +1 -1
- package/src/icons.ts +9 -0
- package/src/index.ts +3 -0
- package/src/types.ts +1 -0
- package/src/widget.ts +48 -13
package/lib/icons.d.ts
CHANGED
package/lib/icons.js
CHANGED
|
@@ -42,3 +42,10 @@ export const removeIcon = new LabIcon({
|
|
|
42
42
|
name: 'jupyterlab_claude_code_extension:remove',
|
|
43
43
|
svgstr: removeSvgStr
|
|
44
44
|
});
|
|
45
|
+
const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
|
|
46
|
+
<path class="jp-icon3" fill="#616161" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/>
|
|
47
|
+
</svg>`;
|
|
48
|
+
export const shieldIcon = new LabIcon({
|
|
49
|
+
name: 'jupyterlab_claude_code_extension:shield',
|
|
50
|
+
svgstr: shieldSvgStr
|
|
51
|
+
});
|
package/lib/index.js
CHANGED
|
@@ -38,6 +38,9 @@ const plugin = {
|
|
|
38
38
|
if (typeof limit === 'number') {
|
|
39
39
|
widget.setRecentLimit(limit);
|
|
40
40
|
}
|
|
41
|
+
const dangerous = settings.get('dangerouslySkipPermissions')
|
|
42
|
+
.composite;
|
|
43
|
+
widget.setDangerouslySkipPermissions(!!dangerous);
|
|
41
44
|
};
|
|
42
45
|
apply();
|
|
43
46
|
settings.changed.connect(apply);
|
package/lib/types.d.ts
CHANGED
package/lib/widget.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
9
9
|
setResolveSessionNames(on: boolean): void;
|
|
10
10
|
/** Set how many rows the Recent section displays. */
|
|
11
11
|
setRecentLimit(n: number): void;
|
|
12
|
+
/** Toggle the --dangerously-skip-permissions flag on launched sessions. */
|
|
13
|
+
setDangerouslySkipPermissions(on: boolean): void;
|
|
12
14
|
protected onAfterShow(_msg: Message): void;
|
|
13
15
|
protected onBeforeHide(_msg: Message): void;
|
|
14
16
|
protected onCloseRequest(msg: Message): void;
|
|
@@ -19,11 +21,11 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
19
21
|
* compare equal as "foobar".
|
|
20
22
|
*/
|
|
21
23
|
private _normalize;
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
/** Fuzzy match at a 95% threshold: substring on normalised strings,
|
|
25
|
+
* with up to 5% Levenshtein tolerance. For short queries the budget
|
|
26
|
+
* still rounds to zero so behaviour is substring-only there - the
|
|
27
|
+
* relaxation only kicks in for queries long enough that 5% reaches a
|
|
28
|
+
* full edit (10+ chars).
|
|
27
29
|
*/
|
|
28
30
|
private _fuzzyMatch;
|
|
29
31
|
private _levenshtein;
|
|
@@ -73,6 +75,7 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
73
75
|
private readonly _rootDir;
|
|
74
76
|
private _resolveNames;
|
|
75
77
|
private _recentLimit;
|
|
78
|
+
private _dangerouslySkip;
|
|
76
79
|
private _displayNames;
|
|
77
80
|
private _filter;
|
|
78
81
|
}
|
package/lib/widget.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CommandRegistry } from '@lumino/commands';
|
|
2
2
|
import { Menu, Widget } from '@lumino/widgets';
|
|
3
3
|
import { requestAPI } from './request';
|
|
4
|
-
import { claudeIcon, refreshIcon, removeIcon, starFilledIcon } from './icons';
|
|
4
|
+
import { claudeIcon, refreshIcon, removeIcon, shieldIcon, starFilledIcon } from './icons';
|
|
5
5
|
const POLL_INTERVAL_MS = 10000;
|
|
6
6
|
const DEFAULT_RECENT_LIMIT = 10;
|
|
7
7
|
const EXPANDED_STORAGE_KEY = 'jupyterlab_claude_code_extension:expanded';
|
|
@@ -66,6 +66,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
66
66
|
this._pendingByPath = new Map();
|
|
67
67
|
this._resolveNames = true;
|
|
68
68
|
this._recentLimit = DEFAULT_RECENT_LIMIT;
|
|
69
|
+
this._dangerouslySkip = false;
|
|
69
70
|
this._displayNames = new Map();
|
|
70
71
|
this._filter = '';
|
|
71
72
|
this._app = app;
|
|
@@ -103,6 +104,10 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
103
104
|
this._recentLimit = clamped;
|
|
104
105
|
this._render();
|
|
105
106
|
}
|
|
107
|
+
/** Toggle the --dangerously-skip-permissions flag on launched sessions. */
|
|
108
|
+
setDangerouslySkipPermissions(on) {
|
|
109
|
+
this._dangerouslySkip = !!on;
|
|
110
|
+
}
|
|
106
111
|
onAfterShow(_msg) {
|
|
107
112
|
this.refresh();
|
|
108
113
|
this._startPolling();
|
|
@@ -163,11 +168,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
163
168
|
.toLowerCase()
|
|
164
169
|
.replace(/[\s\-_./]+/g, '');
|
|
165
170
|
}
|
|
166
|
-
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
+
/** Fuzzy match at a 95% threshold: substring on normalised strings,
|
|
172
|
+
* with up to 5% Levenshtein tolerance. For short queries the budget
|
|
173
|
+
* still rounds to zero so behaviour is substring-only there - the
|
|
174
|
+
* relaxation only kicks in for queries long enough that 5% reaches a
|
|
175
|
+
* full edit (10+ chars).
|
|
171
176
|
*/
|
|
172
177
|
_fuzzyMatch(haystack, needle) {
|
|
173
178
|
if (!needle) {
|
|
@@ -181,7 +186,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
181
186
|
if (h.includes(n)) {
|
|
182
187
|
return true;
|
|
183
188
|
}
|
|
184
|
-
const tol = Math.
|
|
189
|
+
const tol = Math.round(n.length * 0.05);
|
|
185
190
|
if (tol === 0) {
|
|
186
191
|
return false;
|
|
187
192
|
}
|
|
@@ -288,20 +293,20 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
288
293
|
}
|
|
289
294
|
}
|
|
290
295
|
// -------------------------------------------------------------- terminal
|
|
291
|
-
async _resumeInTerminal(session) {
|
|
296
|
+
async _resumeInTerminal(session, forceDangerous = false) {
|
|
292
297
|
// Coalesce concurrent clicks on the same row - subsequent clicks attach
|
|
293
298
|
// to the in-flight promise instead of creating their own terminal.
|
|
294
299
|
const inFlight = this._pendingByPath.get(session.project_path);
|
|
295
300
|
if (inFlight) {
|
|
296
301
|
return inFlight;
|
|
297
302
|
}
|
|
298
|
-
const promise = this._doResumeInTerminal(session).finally(() => {
|
|
303
|
+
const promise = this._doResumeInTerminal(session, forceDangerous).finally(() => {
|
|
299
304
|
this._pendingByPath.delete(session.project_path);
|
|
300
305
|
});
|
|
301
306
|
this._pendingByPath.set(session.project_path, promise);
|
|
302
307
|
return promise;
|
|
303
308
|
}
|
|
304
|
-
async _doResumeInTerminal(session) {
|
|
309
|
+
async _doResumeInTerminal(session, forceDangerous) {
|
|
305
310
|
try {
|
|
306
311
|
// 1. In-memory microcache - covers rapid-click and post-creation reuse
|
|
307
312
|
// before the new widget propagates fully through the tracker. Cleared
|
|
@@ -330,7 +335,8 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
330
335
|
method: 'POST',
|
|
331
336
|
body: JSON.stringify({
|
|
332
337
|
project_path: session.project_path,
|
|
333
|
-
session_id: session.session_id
|
|
338
|
+
session_id: session.session_id,
|
|
339
|
+
dangerously_skip_permissions: forceDangerous || this._dangerouslySkip
|
|
334
340
|
})
|
|
335
341
|
});
|
|
336
342
|
const widget = await this._app.commands.execute('terminal:open', {
|
|
@@ -679,6 +685,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
679
685
|
}
|
|
680
686
|
}
|
|
681
687
|
});
|
|
688
|
+
this._commands.addCommand('claude-code-sessions:resume-dangerous', {
|
|
689
|
+
label: 'Resume (Skip Permissions)',
|
|
690
|
+
icon: shieldIcon,
|
|
691
|
+
execute: () => {
|
|
692
|
+
if (this._activeSession) {
|
|
693
|
+
void this._resumeInTerminal(this._activeSession, true);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
});
|
|
682
697
|
this._commands.addCommand('claude-code-sessions:remove', {
|
|
683
698
|
label: 'Remove from Claude',
|
|
684
699
|
icon: removeIcon,
|
|
@@ -691,6 +706,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
691
706
|
this._contextMenu = new Menu({ commands: this._commands });
|
|
692
707
|
this._contextMenu.addClass('jp-ClaudeSessionsContextMenu');
|
|
693
708
|
this._contextMenu.addItem({ command: 'claude-code-sessions:resume' });
|
|
709
|
+
this._contextMenu.addItem({
|
|
710
|
+
command: 'claude-code-sessions:resume-dangerous'
|
|
711
|
+
});
|
|
694
712
|
this._contextMenu.addItem({
|
|
695
713
|
command: 'claude-code-sessions:toggle-favourite'
|
|
696
714
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab_claude_code_extension",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "Browse, resume, and manage your Claude Code CLI sessions from a JupyterLab side panel. One click reactivates the right terminal - no duplicate tabs, live remote-control indicator, and favourites for the projects you keep coming back to.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
package/src/icons.ts
CHANGED
|
@@ -50,3 +50,12 @@ export const removeIcon = new LabIcon({
|
|
|
50
50
|
name: 'jupyterlab_claude_code_extension:remove',
|
|
51
51
|
svgstr: removeSvgStr
|
|
52
52
|
});
|
|
53
|
+
|
|
54
|
+
const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
|
|
55
|
+
<path class="jp-icon3" fill="#616161" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/>
|
|
56
|
+
</svg>`;
|
|
57
|
+
|
|
58
|
+
export const shieldIcon = new LabIcon({
|
|
59
|
+
name: 'jupyterlab_claude_code_extension:shield',
|
|
60
|
+
svgstr: shieldSvgStr
|
|
61
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -66,6 +66,9 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
|
|
66
66
|
if (typeof limit === 'number') {
|
|
67
67
|
widget.setRecentLimit(limit);
|
|
68
68
|
}
|
|
69
|
+
const dangerous = settings.get('dangerouslySkipPermissions')
|
|
70
|
+
.composite as boolean;
|
|
71
|
+
widget.setDangerouslySkipPermissions(!!dangerous);
|
|
69
72
|
};
|
|
70
73
|
apply();
|
|
71
74
|
settings.changed.connect(apply);
|
package/src/types.ts
CHANGED
package/src/widget.ts
CHANGED
|
@@ -6,7 +6,13 @@ import { Menu, Widget } from '@lumino/widgets';
|
|
|
6
6
|
import { Message } from '@lumino/messaging';
|
|
7
7
|
|
|
8
8
|
import { requestAPI } from './request';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
claudeIcon,
|
|
11
|
+
refreshIcon,
|
|
12
|
+
removeIcon,
|
|
13
|
+
shieldIcon,
|
|
14
|
+
starFilledIcon
|
|
15
|
+
} from './icons';
|
|
10
16
|
import {
|
|
11
17
|
IFavouriteResponse,
|
|
12
18
|
ILaunchTerminalResponse,
|
|
@@ -125,6 +131,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
125
131
|
this._render();
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
/** Toggle the --dangerously-skip-permissions flag on launched sessions. */
|
|
135
|
+
setDangerouslySkipPermissions(on: boolean): void {
|
|
136
|
+
this._dangerouslySkip = !!on;
|
|
137
|
+
}
|
|
138
|
+
|
|
128
139
|
protected onAfterShow(_msg: Message): void {
|
|
129
140
|
this.refresh();
|
|
130
141
|
this._startPolling();
|
|
@@ -199,11 +210,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
199
210
|
.replace(/[\s\-_./]+/g, '');
|
|
200
211
|
}
|
|
201
212
|
|
|
202
|
-
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
213
|
+
/** Fuzzy match at a 95% threshold: substring on normalised strings,
|
|
214
|
+
* with up to 5% Levenshtein tolerance. For short queries the budget
|
|
215
|
+
* still rounds to zero so behaviour is substring-only there - the
|
|
216
|
+
* relaxation only kicks in for queries long enough that 5% reaches a
|
|
217
|
+
* full edit (10+ chars).
|
|
207
218
|
*/
|
|
208
219
|
private _fuzzyMatch(haystack: string, needle: string): boolean {
|
|
209
220
|
if (!needle) {
|
|
@@ -217,7 +228,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
217
228
|
if (h.includes(n)) {
|
|
218
229
|
return true;
|
|
219
230
|
}
|
|
220
|
-
const tol = Math.
|
|
231
|
+
const tol = Math.round(n.length * 0.05);
|
|
221
232
|
if (tol === 0) {
|
|
222
233
|
return false;
|
|
223
234
|
}
|
|
@@ -344,21 +355,29 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
344
355
|
|
|
345
356
|
// -------------------------------------------------------------- terminal
|
|
346
357
|
|
|
347
|
-
private async _resumeInTerminal(
|
|
358
|
+
private async _resumeInTerminal(
|
|
359
|
+
session: ISession,
|
|
360
|
+
forceDangerous: boolean = false
|
|
361
|
+
): Promise<void> {
|
|
348
362
|
// Coalesce concurrent clicks on the same row - subsequent clicks attach
|
|
349
363
|
// to the in-flight promise instead of creating their own terminal.
|
|
350
364
|
const inFlight = this._pendingByPath.get(session.project_path);
|
|
351
365
|
if (inFlight) {
|
|
352
366
|
return inFlight;
|
|
353
367
|
}
|
|
354
|
-
const promise = this._doResumeInTerminal(session).finally(
|
|
355
|
-
|
|
356
|
-
|
|
368
|
+
const promise = this._doResumeInTerminal(session, forceDangerous).finally(
|
|
369
|
+
() => {
|
|
370
|
+
this._pendingByPath.delete(session.project_path);
|
|
371
|
+
}
|
|
372
|
+
);
|
|
357
373
|
this._pendingByPath.set(session.project_path, promise);
|
|
358
374
|
return promise;
|
|
359
375
|
}
|
|
360
376
|
|
|
361
|
-
private async _doResumeInTerminal(
|
|
377
|
+
private async _doResumeInTerminal(
|
|
378
|
+
session: ISession,
|
|
379
|
+
forceDangerous: boolean
|
|
380
|
+
): Promise<void> {
|
|
362
381
|
try {
|
|
363
382
|
// 1. In-memory microcache - covers rapid-click and post-creation reuse
|
|
364
383
|
// before the new widget propagates fully through the tracker. Cleared
|
|
@@ -392,7 +411,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
392
411
|
method: 'POST',
|
|
393
412
|
body: JSON.stringify({
|
|
394
413
|
project_path: session.project_path,
|
|
395
|
-
session_id: session.session_id
|
|
414
|
+
session_id: session.session_id,
|
|
415
|
+
dangerously_skip_permissions:
|
|
416
|
+
forceDangerous || this._dangerouslySkip
|
|
396
417
|
})
|
|
397
418
|
}
|
|
398
419
|
);
|
|
@@ -779,6 +800,16 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
779
800
|
}
|
|
780
801
|
});
|
|
781
802
|
|
|
803
|
+
this._commands.addCommand('claude-code-sessions:resume-dangerous', {
|
|
804
|
+
label: 'Resume (Skip Permissions)',
|
|
805
|
+
icon: shieldIcon,
|
|
806
|
+
execute: () => {
|
|
807
|
+
if (this._activeSession) {
|
|
808
|
+
void this._resumeInTerminal(this._activeSession, true);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
|
|
782
813
|
this._commands.addCommand('claude-code-sessions:remove', {
|
|
783
814
|
label: 'Remove from Claude',
|
|
784
815
|
icon: removeIcon,
|
|
@@ -792,6 +823,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
792
823
|
this._contextMenu = new Menu({ commands: this._commands });
|
|
793
824
|
this._contextMenu.addClass('jp-ClaudeSessionsContextMenu');
|
|
794
825
|
this._contextMenu.addItem({ command: 'claude-code-sessions:resume' });
|
|
826
|
+
this._contextMenu.addItem({
|
|
827
|
+
command: 'claude-code-sessions:resume-dangerous'
|
|
828
|
+
});
|
|
795
829
|
this._contextMenu.addItem({
|
|
796
830
|
command: 'claude-code-sessions:toggle-favourite'
|
|
797
831
|
});
|
|
@@ -848,6 +882,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
848
882
|
private readonly _rootDir: string;
|
|
849
883
|
private _resolveNames: boolean = true;
|
|
850
884
|
private _recentLimit: number = DEFAULT_RECENT_LIMIT;
|
|
885
|
+
private _dangerouslySkip: boolean = false;
|
|
851
886
|
private _displayNames: Map<string, string> = new Map();
|
|
852
887
|
private _filter: string = '';
|
|
853
888
|
}
|