jupyterlab-codex-sidebar 0.1.4 → 0.1.6

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.
Files changed (153) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/.github/workflows/unit-tests.yml +27 -0
  3. package/.jupyterlab-playwright.log +0 -0
  4. package/README.md +83 -9
  5. package/docs/images/codex-sidebar-screenshot.png +0 -0
  6. package/jupyterlab_codex/handlers.py +938 -297
  7. package/jupyterlab_codex/labextension/package.json +13 -3
  8. package/jupyterlab_codex/labextension/static/525.224526d045c727069de6.js +2 -0
  9. package/jupyterlab_codex/labextension/static/737.e7de3ad9dd6ded798340.js +1 -0
  10. package/jupyterlab_codex/labextension/static/remoteEntry.6ef5e7167763a316c000.js +1 -0
  11. package/jupyterlab_codex/protocol.py +297 -0
  12. package/jupyterlab_codex/runner.py +58 -15
  13. package/jupyterlab_codex/sessions.py +582 -97
  14. package/lib/codexChat.d.ts +13 -0
  15. package/lib/codexChat.js +2506 -0
  16. package/lib/codexChat.js.map +1 -0
  17. package/lib/codexChatAttachmentDedup.d.ts +10 -0
  18. package/lib/codexChatAttachmentDedup.js +35 -0
  19. package/lib/codexChatAttachmentDedup.js.map +1 -0
  20. package/lib/codexChatAttachmentLimit.d.ts +18 -0
  21. package/lib/codexChatAttachmentLimit.js +50 -0
  22. package/lib/codexChatAttachmentLimit.js.map +1 -0
  23. package/lib/codexChatAttachmentState.d.ts +15 -0
  24. package/lib/codexChatAttachmentState.js +16 -0
  25. package/lib/codexChatAttachmentState.js.map +1 -0
  26. package/lib/codexChatDocumentUtils.d.ts +70 -0
  27. package/lib/codexChatDocumentUtils.js +506 -0
  28. package/lib/codexChatDocumentUtils.js.map +1 -0
  29. package/lib/codexChatFormatting.d.ts +11 -0
  30. package/lib/codexChatFormatting.js +83 -0
  31. package/lib/codexChatFormatting.js.map +1 -0
  32. package/lib/codexChatNotice.d.ts +3 -0
  33. package/lib/codexChatNotice.js +74 -0
  34. package/lib/codexChatNotice.js.map +1 -0
  35. package/lib/codexChatPersistence.d.ts +35 -0
  36. package/lib/codexChatPersistence.js +158 -0
  37. package/lib/codexChatPersistence.js.map +1 -0
  38. package/lib/codexChatPrimitives.d.ts +44 -0
  39. package/lib/codexChatPrimitives.js +156 -0
  40. package/lib/codexChatPrimitives.js.map +1 -0
  41. package/lib/codexChatRender.d.ts +24 -0
  42. package/lib/codexChatRender.js +293 -0
  43. package/lib/codexChatRender.js.map +1 -0
  44. package/lib/codexChatSessionFactory.d.ts +15 -0
  45. package/lib/codexChatSessionFactory.js +45 -0
  46. package/lib/codexChatSessionFactory.js.map +1 -0
  47. package/lib/codexChatSessionKey.d.ts +3 -0
  48. package/lib/codexChatSessionKey.js +14 -0
  49. package/lib/codexChatSessionKey.js.map +1 -0
  50. package/lib/codexChatStorage.d.ts +4 -0
  51. package/lib/codexChatStorage.js +37 -0
  52. package/lib/codexChatStorage.js.map +1 -0
  53. package/lib/codexSessionResolver.d.ts +12 -0
  54. package/lib/codexSessionResolver.js +38 -0
  55. package/lib/codexSessionResolver.js.map +1 -0
  56. package/lib/handlers/activitySummarizer.d.ts +15 -0
  57. package/lib/handlers/activitySummarizer.js +327 -0
  58. package/lib/handlers/activitySummarizer.js.map +1 -0
  59. package/lib/handlers/codexMessageTypes.d.ts +30 -0
  60. package/lib/handlers/codexMessageTypes.js +2 -0
  61. package/lib/handlers/codexMessageTypes.js.map +1 -0
  62. package/lib/handlers/codexMessageUtils.d.ts +46 -0
  63. package/lib/handlers/codexMessageUtils.js +144 -0
  64. package/lib/handlers/codexMessageUtils.js.map +1 -0
  65. package/lib/handlers/handleCodexSocketMessage.d.ts +107 -0
  66. package/lib/handlers/handleCodexSocketMessage.js +78 -0
  67. package/lib/handlers/handleCodexSocketMessage.js.map +1 -0
  68. package/lib/handlers/sessionSyncHandler.d.ts +34 -0
  69. package/lib/handlers/sessionSyncHandler.js +181 -0
  70. package/lib/handlers/sessionSyncHandler.js.map +1 -0
  71. package/lib/hooks/useCodexSocket.d.ts +15 -0
  72. package/lib/hooks/useCodexSocket.js +84 -0
  73. package/lib/hooks/useCodexSocket.js.map +1 -0
  74. package/lib/index.js +1 -1
  75. package/lib/index.js.map +1 -1
  76. package/lib/panel.d.ts +1 -11
  77. package/lib/panel.js +1 -2815
  78. package/lib/panel.js.map +1 -1
  79. package/lib/protocol.d.ts +235 -0
  80. package/lib/protocol.js +278 -0
  81. package/lib/protocol.js.map +1 -0
  82. package/package.json +13 -3
  83. package/playwright.config.cjs +27 -0
  84. package/playwright.unit.config.cjs +19 -0
  85. package/pyproject.toml +1 -1
  86. package/release.sh +52 -14
  87. package/scripts/run_playwright_e2e.sh +96 -0
  88. package/scripts/run_playwright_freeze_repro.sh +58 -0
  89. package/scripts/run_playwright_queue_repro.sh +60 -0
  90. package/scripts/run_playwright_repro.sh +55 -0
  91. package/src/codexChat.tsx +3914 -0
  92. package/src/codexChatAttachmentDedup.ts +47 -0
  93. package/src/codexChatAttachmentLimit.ts +81 -0
  94. package/src/codexChatAttachmentState.ts +37 -0
  95. package/src/codexChatDocumentUtils.ts +644 -0
  96. package/src/codexChatFormatting.ts +94 -0
  97. package/src/codexChatNotice.ts +95 -0
  98. package/src/codexChatPersistence.ts +191 -0
  99. package/src/codexChatPrimitives.tsx +446 -0
  100. package/src/codexChatRender.tsx +376 -0
  101. package/src/codexChatSessionFactory.ts +79 -0
  102. package/src/codexChatSessionKey.ts +16 -0
  103. package/src/codexChatStorage.ts +36 -0
  104. package/src/codexSessionResolver.ts +56 -0
  105. package/src/handlers/activitySummarizer.ts +369 -0
  106. package/src/handlers/codexMessageTypes.ts +34 -0
  107. package/src/handlers/codexMessageUtils.ts +217 -0
  108. package/src/handlers/handleCodexSocketMessage.ts +204 -0
  109. package/src/handlers/sessionSyncHandler.ts +308 -0
  110. package/src/hooks/useCodexSocket.ts +109 -0
  111. package/src/index.ts +1 -1
  112. package/src/panel.tsx +1 -4184
  113. package/src/protocol.ts +582 -0
  114. package/style/index.css +480 -11
  115. package/test-results/.last-run.json +4 -0
  116. package/test.py +0 -0
  117. package/tests/e2e/cell-output-error-tail.spec.js +156 -0
  118. package/tests/e2e/codex-ui-test-helpers.js +138 -0
  119. package/tests/e2e/fixtures/notebooks/error-output-tail.ipynb +58 -0
  120. package/tests/e2e/fixtures/notebooks/error-output-tail.py +19 -0
  121. package/tests/e2e/fixtures/notebooks/tab1.ipynb +322 -0
  122. package/tests/e2e/fixtures/notebooks/tab1.py +272 -0
  123. package/tests/e2e/fixtures/notebooks/tab2.ipynb +252 -0
  124. package/tests/e2e/fixtures/notebooks/tab2.py +231 -0
  125. package/tests/e2e/fixtures/notebooks/tab3.ipynb +403 -0
  126. package/tests/e2e/fixtures/notebooks/tab3.py +331 -0
  127. package/tests/e2e/fixtures/notebooks/tab4.py +339 -0
  128. package/tests/e2e/freeze-notebook-tabs-repro.spec.js +295 -0
  129. package/tests/e2e/mock-codex-cli-flood.py +127 -0
  130. package/tests/e2e/mock-codex-cli-prompt-echo.py +88 -0
  131. package/tests/e2e/mock-codex-cli.py +95 -0
  132. package/tests/e2e/queue-multitab-repro.spec.js +189 -0
  133. package/tests/test_handlers.py +116 -0
  134. package/tests/test_protocol.py +169 -0
  135. package/tests/test_session_store_limits.py +50 -0
  136. package/tests/unit/codexChatAttachmentDedup.spec.ts +56 -0
  137. package/tests/unit/codexChatAttachmentLimit.spec.ts +57 -0
  138. package/tests/unit/codexChatAttachmentState.spec.ts +71 -0
  139. package/tests/unit/codexChatDocumentUtils.spec.ts +63 -0
  140. package/tests/unit/codexChatLimit.spec.ts +18 -0
  141. package/tests/unit/codexChatNotice.spec.ts +45 -0
  142. package/tests/unit/codexChatPersistence.spec.ts +199 -0
  143. package/tests/unit/codexChatSessionFactory.spec.ts +94 -0
  144. package/tests/unit/codexChatSessionKey.spec.ts +18 -0
  145. package/tests/unit/codexMessageUtils.spec.ts +89 -0
  146. package/tests/unit/codexSessionResolver.spec.ts +92 -0
  147. package/tests/unit/handleCodexSocketMessage.spec.ts +476 -0
  148. package/tsconfig.tsbuildinfo +1 -1
  149. package/webpack.config.js +6 -0
  150. package/jupyterlab_codex/labextension/static/504.335f3447c84ba3d74517.js +0 -2
  151. package/jupyterlab_codex/labextension/static/972.8e856719e40acc1ef4cb.js +0 -1
  152. package/jupyterlab_codex/labextension/static/remoteEntry.a2982f776a1f0f515640.js +0 -1
  153. /package/jupyterlab_codex/labextension/static/{504.335f3447c84ba3d74517.js.LICENSE.txt → 525.224526d045c727069de6.js.LICENSE.txt} +0 -0
@@ -0,0 +1,278 @@
1
+ export const PROTOCOL_VERSION = '1.0.0';
2
+ export function parseServerMessage(raw) {
3
+ if (typeof raw !== 'string' && (raw === null || typeof raw !== 'object')) {
4
+ return null;
5
+ }
6
+ const parsed = typeof raw === 'string' ? parseJson(raw) : raw;
7
+ if (parsed === null || typeof parsed !== 'object') {
8
+ return null;
9
+ }
10
+ const message = parsed;
11
+ const type = typeof message.type === 'string' ? message.type : '';
12
+ if (!type) {
13
+ return null;
14
+ }
15
+ switch (type) {
16
+ case 'cli_defaults':
17
+ const cliDefaultsMessage = {
18
+ type,
19
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined
20
+ };
21
+ if (Object.prototype.hasOwnProperty.call(message, 'model')) {
22
+ cliDefaultsMessage.model =
23
+ typeof message.model === 'string' || message.model === null ? message.model : null;
24
+ }
25
+ if (Object.prototype.hasOwnProperty.call(message, 'reasoningEffort')) {
26
+ cliDefaultsMessage.reasoningEffort =
27
+ typeof message.reasoningEffort === 'string' || message.reasoningEffort === null
28
+ ? message.reasoningEffort
29
+ : null;
30
+ }
31
+ if (Object.prototype.hasOwnProperty.call(message, 'availableModels')) {
32
+ cliDefaultsMessage.availableModels = parseModelCatalog(Array.isArray(message.availableModels) ? message.availableModels : []);
33
+ }
34
+ return cliDefaultsMessage;
35
+ case 'rate_limits':
36
+ return {
37
+ type,
38
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined,
39
+ snapshot: message.snapshot ?? null
40
+ };
41
+ case 'delete_all_sessions':
42
+ if (typeof message.ok !== 'boolean' ||
43
+ !Number.isFinite(Number(message.deletedCount)) ||
44
+ !Number.isFinite(Number(message.failedCount)) ||
45
+ typeof message.message !== 'string') {
46
+ return null;
47
+ }
48
+ return {
49
+ type,
50
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined,
51
+ ok: message.ok,
52
+ deletedCount: Number(message.deletedCount),
53
+ failedCount: Number(message.failedCount),
54
+ message: message.message
55
+ };
56
+ case 'status':
57
+ return {
58
+ type,
59
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined,
60
+ state: message.state === 'running' ? 'running' : 'ready',
61
+ runId: typeof message.runId === 'string' ? message.runId : undefined,
62
+ sessionId: typeof message.sessionId === 'string' ? message.sessionId : undefined,
63
+ sessionContextKey: typeof message.sessionContextKey === 'string' ? message.sessionContextKey : undefined,
64
+ notebookPath: typeof message.notebookPath === 'string' ? message.notebookPath : undefined,
65
+ runMode: message.runMode === 'fallback' ? 'fallback' : message.runMode === 'resume' ? 'resume' : undefined,
66
+ pairedOk: typeof message.pairedOk === 'boolean' ? message.pairedOk : undefined,
67
+ pairedPath: typeof message.pairedPath === 'string' ? message.pairedPath : undefined,
68
+ pairedOsPath: typeof message.pairedOsPath === 'string' ? message.pairedOsPath : undefined,
69
+ pairedMessage: typeof message.pairedMessage === 'string' ? message.pairedMessage : undefined,
70
+ notebookMode: typeof message.notebookMode === 'string' ? message.notebookMode : undefined,
71
+ sessionResolution: typeof message.sessionResolution === 'string' ? message.sessionResolution : undefined,
72
+ sessionResolutionNotice: typeof message.sessionResolutionNotice === 'string' ? message.sessionResolutionNotice : undefined,
73
+ history: Array.isArray(message.history) ? message.history : undefined,
74
+ effectiveSandbox: typeof message.effectiveSandbox === 'string' ? message.effectiveSandbox : undefined
75
+ };
76
+ case 'output':
77
+ if (typeof message.runId !== 'string' ||
78
+ typeof message.sessionId !== 'string' ||
79
+ typeof message.notebookPath !== 'string' ||
80
+ typeof message.text !== 'string') {
81
+ return null;
82
+ }
83
+ return {
84
+ type,
85
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined,
86
+ runId: message.runId,
87
+ sessionId: message.sessionId,
88
+ sessionContextKey: typeof message.sessionContextKey === 'string' ? message.sessionContextKey : undefined,
89
+ notebookPath: message.notebookPath,
90
+ role: typeof message.role === 'string' ? message.role : 'assistant',
91
+ text: message.text
92
+ };
93
+ case 'event':
94
+ if (typeof message.runId !== 'string' ||
95
+ typeof message.sessionId !== 'string' ||
96
+ typeof message.notebookPath !== 'string') {
97
+ return null;
98
+ }
99
+ return {
100
+ type,
101
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined,
102
+ runId: message.runId,
103
+ sessionId: message.sessionId,
104
+ sessionContextKey: typeof message.sessionContextKey === 'string' ? message.sessionContextKey : undefined,
105
+ notebookPath: message.notebookPath,
106
+ payload: message.payload
107
+ };
108
+ case 'done':
109
+ if (typeof message.runId !== 'string' ||
110
+ typeof message.sessionId !== 'string' ||
111
+ typeof message.notebookPath !== 'string') {
112
+ return null;
113
+ }
114
+ let exitCode = null;
115
+ if (message.exitCode === null) {
116
+ exitCode = null;
117
+ }
118
+ else if (typeof message.exitCode === 'number' && Number.isFinite(message.exitCode)) {
119
+ exitCode = message.exitCode;
120
+ }
121
+ else if (typeof message.exitCode === 'string') {
122
+ const parsedExitCode = Number(message.exitCode.trim());
123
+ exitCode = Number.isFinite(parsedExitCode) ? parsedExitCode : null;
124
+ }
125
+ return {
126
+ type,
127
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined,
128
+ runId: message.runId,
129
+ sessionId: message.sessionId,
130
+ sessionContextKey: typeof message.sessionContextKey === 'string' ? message.sessionContextKey : undefined,
131
+ notebookPath: message.notebookPath,
132
+ exitCode,
133
+ cancelled: typeof message.cancelled === 'boolean' ? message.cancelled : false,
134
+ fileChanged: typeof message.fileChanged === 'boolean' ? message.fileChanged : false,
135
+ runMode: message.runMode === 'fallback' ? 'fallback' : message.runMode === 'resume' ? 'resume' : undefined,
136
+ pairedOk: typeof message.pairedOk === 'boolean' ? message.pairedOk : undefined,
137
+ pairedPath: typeof message.pairedPath === 'string' ? message.pairedPath : undefined,
138
+ pairedOsPath: typeof message.pairedOsPath === 'string' ? message.pairedOsPath : undefined,
139
+ pairedMessage: typeof message.pairedMessage === 'string' ? message.pairedMessage : undefined,
140
+ notebookMode: typeof message.notebookMode === 'string' ? message.notebookMode : undefined
141
+ };
142
+ case 'error':
143
+ if (typeof message.message !== 'string') {
144
+ return null;
145
+ }
146
+ return {
147
+ type,
148
+ protocolVersion: typeof message.protocolVersion === 'string' ? message.protocolVersion : undefined,
149
+ runId: typeof message.runId === 'string' ? message.runId : undefined,
150
+ sessionId: typeof message.sessionId === 'string' ? message.sessionId : undefined,
151
+ sessionContextKey: typeof message.sessionContextKey === 'string' ? message.sessionContextKey : undefined,
152
+ notebookPath: typeof message.notebookPath === 'string' ? message.notebookPath : undefined,
153
+ message: message.message,
154
+ runMode: message.runMode === 'fallback' ? 'fallback' : message.runMode === 'resume' ? 'resume' : undefined,
155
+ suggestedCommandPath: typeof message.suggestedCommandPath === 'string' ? message.suggestedCommandPath : undefined,
156
+ pairedOk: typeof message.pairedOk === 'boolean' ? message.pairedOk : undefined,
157
+ pairedPath: typeof message.pairedPath === 'string' ? message.pairedPath : undefined,
158
+ pairedOsPath: typeof message.pairedOsPath === 'string' ? message.pairedOsPath : undefined,
159
+ pairedMessage: typeof message.pairedMessage === 'string' ? message.pairedMessage : undefined,
160
+ notebookMode: typeof message.notebookMode === 'string' ? message.notebookMode : undefined
161
+ };
162
+ }
163
+ return null;
164
+ }
165
+ export function parseModelCatalog(rawModels) {
166
+ if (!Array.isArray(rawModels)) {
167
+ return [];
168
+ }
169
+ const seen = new Set();
170
+ const catalog = [];
171
+ for (const item of rawModels) {
172
+ if (!item || typeof item !== 'object') {
173
+ continue;
174
+ }
175
+ const rawModel = item.model;
176
+ const rawDisplayName = item.displayName;
177
+ const rawReasoningEfforts = item.reasoningEfforts ??
178
+ item.supportedReasoningEfforts;
179
+ const rawDefaultReasoningEffort = item.defaultReasoningEffort;
180
+ if (typeof rawModel !== 'string') {
181
+ continue;
182
+ }
183
+ const model = rawModel.trim();
184
+ if (!model || seen.has(model)) {
185
+ continue;
186
+ }
187
+ seen.add(model);
188
+ const displayName = typeof rawDisplayName === 'string' && rawDisplayName.trim() ? rawDisplayName.trim() : model;
189
+ const defaultReasoningEffort = typeof rawDefaultReasoningEffort === 'string' && rawDefaultReasoningEffort.trim()
190
+ ? rawDefaultReasoningEffort.trim()
191
+ : '';
192
+ const reasoningEfforts = Array.isArray(rawReasoningEfforts)
193
+ ? rawReasoningEfforts.reduce((acc, effortCandidate) => {
194
+ const effort = coerceReasoningEffortEntry(effortCandidate);
195
+ if (!effort || acc.includes(effort)) {
196
+ return acc;
197
+ }
198
+ acc.push(effort);
199
+ return acc;
200
+ }, [])
201
+ : [];
202
+ catalog.push({
203
+ model,
204
+ displayName,
205
+ ...(reasoningEfforts.length > 0 ? { reasoningEfforts } : {}),
206
+ ...(defaultReasoningEffort ? { defaultReasoningEffort } : {})
207
+ });
208
+ }
209
+ return catalog;
210
+ }
211
+ export function buildStartSessionMessage(input) {
212
+ return {
213
+ type: 'start_session',
214
+ sessionId: input.sessionId,
215
+ sessionContextKey: input.sessionContextKey,
216
+ notebookPath: input.notebookPath,
217
+ forceNewThread: input.forceNewThread === true,
218
+ commandPath: input.commandPath ? input.commandPath.trim() : undefined
219
+ };
220
+ }
221
+ export function buildSendMessage(input) {
222
+ return {
223
+ type: 'send',
224
+ sessionId: input.sessionId,
225
+ sessionContextKey: input.sessionContextKey,
226
+ notebookPath: input.notebookPath,
227
+ ...(input.commandPath ? { commandPath: input.commandPath } : {}),
228
+ content: input.content,
229
+ model: input.model,
230
+ reasoningEffort: input.reasoningEffort,
231
+ sandbox: input.sandbox,
232
+ ...(input.selection ? { selection: input.selection } : {}),
233
+ ...(input.cellOutput ? { cellOutput: input.cellOutput } : {}),
234
+ ...(input.images ? { images: input.images } : {}),
235
+ ...(input.uiSelectionPreview ? { uiSelectionPreview: input.uiSelectionPreview } : {}),
236
+ ...(input.uiCellOutputPreview ? { uiCellOutputPreview: input.uiCellOutputPreview } : {}),
237
+ ...(input.selectionTruncated ? { selectionTruncated: true } : {}),
238
+ ...(input.cellOutputTruncated ? { cellOutputTruncated: true } : {})
239
+ };
240
+ }
241
+ export function buildCancelMessage(runId) {
242
+ return {
243
+ type: 'cancel',
244
+ runId
245
+ };
246
+ }
247
+ export function buildDeleteSessionMessage(sessionId) {
248
+ return {
249
+ type: 'delete_session',
250
+ sessionId
251
+ };
252
+ }
253
+ export function buildDeleteAllSessionsMessage() {
254
+ return {
255
+ type: 'delete_all_sessions'
256
+ };
257
+ }
258
+ function parseJson(raw) {
259
+ try {
260
+ return JSON.parse(raw);
261
+ }
262
+ catch {
263
+ return null;
264
+ }
265
+ }
266
+ function coerceReasoningEffortEntry(value) {
267
+ if (typeof value === 'string') {
268
+ return value.trim();
269
+ }
270
+ if (value &&
271
+ typeof value === 'object' &&
272
+ 'reasoningEffort' in value) {
273
+ const candidate = value.reasoningEffort;
274
+ return typeof candidate === 'string' ? candidate.trim() : '';
275
+ }
276
+ return '';
277
+ }
278
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab-codex-sidebar",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "author": "ILHO AHN",
5
5
  "description": "Codex sidebar for JupyterLab",
6
6
  "license": "BSD-3-Clause",
@@ -26,8 +26,16 @@
26
26
  "watch": "jlpm run watch:lib & jlpm run watch:labextension",
27
27
  "watch:lib": "tsc -w -p tsconfig.json",
28
28
  "watch:labextension": "jupyter labextension watch . --development --source-map",
29
- "clean": "rimraf lib tsconfig.tsbuildinfo"
29
+ "clean": "rimraf lib tsconfig.tsbuildinfo",
30
+ "test:unit": "playwright test --config playwright.unit.config.cjs",
31
+ "test:e2e": "bash scripts/run_playwright_e2e.sh",
32
+ "test:e2e:queue-repro": "bash scripts/run_playwright_queue_repro.sh",
33
+ "test:e2e:queue-repro:local": "bash scripts/run_playwright_repro.sh",
34
+ "test:e2e:headed": "bash scripts/run_playwright_e2e.sh --headed",
35
+ "test:e2e:install": "playwright install chromium",
36
+ "test:e2e:repro-local": "bash scripts/run_playwright_repro.sh"
30
37
  },
38
+ "packageManager": "yarn@3.5.0",
31
39
  "dependencies": {
32
40
  "@jupyterlab/application": "^4.0.0",
33
41
  "@jupyterlab/apputils": "^4.0.0",
@@ -45,6 +53,7 @@
45
53
  },
46
54
  "devDependencies": {
47
55
  "@jupyterlab/builder": "^4.0.0",
56
+ "@playwright/test": "^1.56.1",
48
57
  "@types/react": "^18.2.0",
49
58
  "@types/react-dom": "^18.2.0",
50
59
  "rimraf": "^5.0.0",
@@ -52,6 +61,7 @@
52
61
  },
53
62
  "jupyterlab": {
54
63
  "extension": true,
55
- "outputDir": "jupyterlab_codex/labextension"
64
+ "outputDir": "jupyterlab_codex/labextension",
65
+ "webpackConfig": "webpack.config.js"
56
66
  }
57
67
  }
@@ -0,0 +1,27 @@
1
+ const { defineConfig } = require('@playwright/test');
2
+
3
+ const baseURL =
4
+ process.env.JUPYTERLAB_URL ||
5
+ process.env.PLAYWRIGHT_BASE_URL ||
6
+ 'http://127.0.0.1:8888/lab';
7
+ const channel = process.env.PLAYWRIGHT_BROWSER_CHANNEL || undefined;
8
+ const video = process.env.PLAYWRIGHT_VIDEO || 'retain-on-failure';
9
+
10
+ module.exports = defineConfig({
11
+ testDir: './tests/e2e',
12
+ timeout: 120000,
13
+ expect: {
14
+ timeout: 15000
15
+ },
16
+ fullyParallel: false,
17
+ retries: 0,
18
+ reporter: [['list']],
19
+ use: {
20
+ baseURL,
21
+ ...(channel ? { channel } : {}),
22
+ headless: process.env.PW_HEADLESS !== '0',
23
+ trace: 'retain-on-failure',
24
+ screenshot: 'only-on-failure',
25
+ video
26
+ }
27
+ });
@@ -0,0 +1,19 @@
1
+ const { defineConfig } = require('@playwright/test');
2
+
3
+ module.exports = defineConfig({
4
+ testDir: './tests/unit',
5
+ testMatch: /.*\.spec\.(js|ts)$/,
6
+ timeout: 120000,
7
+ expect: {
8
+ timeout: 15000
9
+ },
10
+ fullyParallel: false,
11
+ retries: 0,
12
+ reporter: [['list']],
13
+ use: {
14
+ headless: process.env.PW_HEADLESS !== '0',
15
+ trace: 'off',
16
+ screenshot: 'off',
17
+ video: 'off'
18
+ }
19
+ });
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jupyterlab-codex-sidebar"
7
- version = "0.1.4"
7
+ version = "0.1.6"
8
8
  description = "JupyterLab Codex sidebar with server extension"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
package/release.sh CHANGED
@@ -15,9 +15,9 @@ Examples:
15
15
  ./release.sh --skip-npm
16
16
 
17
17
  Description:
18
- - Uses current versions in package.json and pyproject.toml (no version bump)
19
- - Optionally validates that [version] matches current version
20
- - Runs jlpm install and jlpm build
18
+ - If [version] is provided, updates package.json and pyproject.toml to that version
19
+ - If [version] is omitted, uses the current synced version from manifests
20
+ - Runs jlpm install --immutable and jlpm build
21
21
  - Builds Python distribution (dist/*)
22
22
  - Uploads to PyPI using twine (unless --skip-pypi)
23
23
  - Publishes to npm (unless --skip-npm)
@@ -118,18 +118,48 @@ print(match.group(1))
118
118
  PY
119
119
  )"
120
120
 
121
- if [[ "$CURRENT_PACKAGE_VERSION" != "$CURRENT_PYTHON_VERSION" ]]; then
122
- echo "ERROR: Version mismatch between package.json ($CURRENT_PACKAGE_VERSION) and pyproject.toml ($CURRENT_PYTHON_VERSION)." >&2
123
- echo " Sync versions first, then run release.sh again." >&2
124
- exit 1
125
- fi
121
+ if [[ -n "$EXPECTED_VERSION" ]]; then
122
+ RELEASE_VERSION="$EXPECTED_VERSION"
123
+
124
+ if [[ "$CURRENT_PACKAGE_VERSION" != "$RELEASE_VERSION" ]]; then
125
+ node - <<'JS' "$RELEASE_VERSION"
126
+ const fs = require('fs');
127
+ const version = process.argv[2];
128
+ const pkgPath = 'package.json';
129
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
130
+ pkg.version = version;
131
+ fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
132
+ JS
133
+ fi
126
134
 
127
- RELEASE_VERSION="$CURRENT_PACKAGE_VERSION"
135
+ if [[ "$CURRENT_PYTHON_VERSION" != "$RELEASE_VERSION" ]]; then
136
+ "$PYTHON_BIN" - <<'PY' "$RELEASE_VERSION"
137
+ import pathlib
138
+ import re
139
+ import sys
128
140
 
129
- if [[ -n "$EXPECTED_VERSION" && "$EXPECTED_VERSION" != "$RELEASE_VERSION" ]]; then
130
- echo "ERROR: Requested version '$EXPECTED_VERSION' does not match current version '$RELEASE_VERSION'." >&2
131
- echo " This script does not change versions; update files first if needed." >&2
132
- exit 1
141
+ version = sys.argv[1]
142
+ pyproject = pathlib.Path("pyproject.toml")
143
+ text = pyproject.read_text()
144
+ updated = re.sub(
145
+ r'(^version\s*=\s*")([^"]+)(")',
146
+ rf'\g<1>{version}\g<3>',
147
+ text,
148
+ count=1,
149
+ flags=re.M,
150
+ )
151
+ if updated == text:
152
+ raise SystemExit("ERROR: version not found in pyproject.toml")
153
+ pyproject.write_text(updated)
154
+ PY
155
+ fi
156
+ else
157
+ if [[ "$CURRENT_PACKAGE_VERSION" != "$CURRENT_PYTHON_VERSION" ]]; then
158
+ echo "ERROR: Version mismatch between package.json ($CURRENT_PACKAGE_VERSION) and pyproject.toml ($CURRENT_PYTHON_VERSION)." >&2
159
+ echo " Sync versions first, then run release.sh again." >&2
160
+ exit 1
161
+ fi
162
+ RELEASE_VERSION="$CURRENT_PACKAGE_VERSION"
133
163
  fi
134
164
 
135
165
  if [[ "$RELEASE_VERSION" =~ -dev(\.[0-9A-Za-z-]+)*$ ]]; then
@@ -172,7 +202,7 @@ echo "[2/5] Cleaning previous artifacts"
172
202
  rm -rf dist
173
203
 
174
204
  echo "[3/5] Installing JS dependencies and building frontend"
175
- jlpm install
205
+ jlpm install --immutable
176
206
  jlpm run build
177
207
 
178
208
  echo "[4/5] Building Python distributions"
@@ -194,6 +224,14 @@ else
194
224
  fi
195
225
 
196
226
  if [[ "$SKIP_NPM" -eq 0 ]]; then
227
+ echo "[extra] Ensuring npm authentication"
228
+ if ! npm whoami >/dev/null 2>&1; then
229
+ echo " - npm login required. Starting interactive login..."
230
+ npm login
231
+ else
232
+ echo " - npm login already active."
233
+ fi
234
+
197
235
  echo "[extra] Publishing to npm"
198
236
  npm publish --access public
199
237
  else
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if [ -z "${BASH_VERSION:-}" ]; then
4
+ exec bash "$0" "$@"
5
+ fi
6
+
7
+ set -euo pipefail
8
+
9
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
10
+ cd "$ROOT_DIR"
11
+
12
+ HOST="${JUPYTERLAB_HOST:-127.0.0.1}"
13
+ DEFAULT_PORT="${JUPYTERLAB_PORT:-8888}"
14
+ FALLBACK_PORTS_RAW="${JUPYTERLAB_FALLBACK_PORTS:-$DEFAULT_PORT 8889 8890 8891 8892 8893}"
15
+ if [[ "${PLAYWRIGHT_BASE_URL:-}" != "" ]]; then
16
+ BASE_URL_CANDIDATES=("$PLAYWRIGHT_BASE_URL")
17
+ else
18
+ # shellcheck disable=SC2206
19
+ FALLBACK_PORTS=(${FALLBACK_PORTS_RAW})
20
+ BASE_URL_CANDIDATES=()
21
+ for port in "${FALLBACK_PORTS[@]}"; do
22
+ BASE_URL_CANDIDATES+=("http://${HOST}:${port}/lab")
23
+ done
24
+ fi
25
+
26
+ BASE_URL=""
27
+ JUPYTER_PID=""
28
+ LOG_FILE="${PLAYWRIGHT_JUPYTER_LOG:-$ROOT_DIR/.jupyterlab-playwright.log}"
29
+
30
+ cleanup() {
31
+ if [ -n "${JUPYTER_PID}" ] && kill -0 "$JUPYTER_PID" >/dev/null 2>&1; then
32
+ kill "$JUPYTER_PID" >/dev/null 2>&1 || true
33
+ wait "$JUPYTER_PID" >/dev/null 2>&1 || true
34
+ fi
35
+ }
36
+ trap cleanup EXIT
37
+
38
+ for candidate in "${BASE_URL_CANDIDATES[@]}"; do
39
+ if curl -fsS "$candidate" >/dev/null 2>&1; then
40
+ BASE_URL="$candidate"
41
+ break
42
+ fi
43
+ done
44
+
45
+ if [[ -z "$BASE_URL" ]]; then
46
+ if [[ "${PLAYWRIGHT_BASE_URL:-}" != "" ]]; then
47
+ echo "[playwright] JupyterLab is not reachable."
48
+ echo "[playwright] Tried: $PLAYWRIGHT_BASE_URL"
49
+ echo "[playwright] Start JupyterLab first and retry."
50
+ exit 1
51
+ fi
52
+
53
+ echo "[playwright] JupyterLab is not reachable. launching automatically..."
54
+ for port in ${FALLBACK_PORTS_RAW}; do
55
+ BASE_URL_CANDIDATE="http://${HOST}:${port}/lab"
56
+ echo "[playwright] launching JupyterLab on ${BASE_URL_CANDIDATE}"
57
+
58
+ jupyter lab \
59
+ --no-browser \
60
+ --ServerApp.open_browser=False \
61
+ --ServerApp.port="$port" \
62
+ --ServerApp.ip="$HOST" \
63
+ --IdentityProvider.token='' \
64
+ --ServerApp.token='' \
65
+ --ServerApp.password='' \
66
+ >"$LOG_FILE" 2>&1 &
67
+ JUPYTER_PID=$!
68
+
69
+ for _ in $(seq 1 60); do
70
+ if curl -fsS "$BASE_URL_CANDIDATE" >/dev/null 2>&1; then
71
+ BASE_URL="$BASE_URL_CANDIDATE"
72
+ break 2
73
+ fi
74
+ sleep 1
75
+ done
76
+
77
+ if [ -n "$JUPYTER_PID" ] && kill -0 "$JUPYTER_PID" >/dev/null 2>&1; then
78
+ kill "$JUPYTER_PID" >/dev/null 2>&1 || true
79
+ wait "$JUPYTER_PID" >/dev/null 2>&1 || true
80
+ fi
81
+ JUPYTER_PID=""
82
+ done
83
+
84
+ if [[ -z "$BASE_URL" ]]; then
85
+ echo "[playwright] JupyterLab failed to start. Log: $LOG_FILE"
86
+ exit 1
87
+ fi
88
+ fi
89
+
90
+ if [ -x "$ROOT_DIR/node_modules/.bin/playwright" ]; then
91
+ env PLAYWRIGHT_BASE_URL="$BASE_URL" \
92
+ "$ROOT_DIR/node_modules/.bin/playwright" test "$@"
93
+ else
94
+ env PLAYWRIGHT_BASE_URL="$BASE_URL" \
95
+ npx playwright test "$@"
96
+ fi
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if [ -z "${BASH_VERSION:-}" ]; then
4
+ exec bash "$0" "$@"
5
+ fi
6
+
7
+ set -euo pipefail
8
+
9
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
10
+ cd "$ROOT_DIR"
11
+
12
+ PORT="${JUPYTERLAB_PORT:-8888}"
13
+ HOST="${JUPYTERLAB_HOST:-127.0.0.1}"
14
+ BASE_URL="${PLAYWRIGHT_BASE_URL:-http://${HOST}:${PORT}/lab}"
15
+ MOCK_CODEX="${PLAYWRIGHT_CODEX_COMMAND:-$ROOT_DIR/tests/e2e/mock-codex-cli-flood.py}"
16
+ LOG_FILE="${PLAYWRIGHT_JUPYTER_LOG:-$ROOT_DIR/.jupyterlab-playwright-freeze.log}"
17
+ MOCK_CODEX_EVENT_DELAY_MS_DEFAULT="${MOCK_CODEX_EVENT_DELAY_MS:-120}"
18
+
19
+ cleanup() {
20
+ if [ -n "${JUPYTER_PID:-}" ] && kill -0 "$JUPYTER_PID" >/dev/null 2>&1; then
21
+ kill "$JUPYTER_PID" >/dev/null 2>&1 || true
22
+ wait "$JUPYTER_PID" >/dev/null 2>&1 || true
23
+ fi
24
+ }
25
+ trap cleanup EXIT
26
+
27
+ echo "[playwright] launching JupyterLab on ${BASE_URL}"
28
+ jupyter lab \
29
+ --no-browser \
30
+ --ServerApp.open_browser=False \
31
+ --ServerApp.port="$PORT" \
32
+ --ServerApp.ip="$HOST" \
33
+ --IdentityProvider.token='' \
34
+ --ServerApp.token='' \
35
+ --ServerApp.password='' \
36
+ >"$LOG_FILE" 2>&1 &
37
+ JUPYTER_PID=$!
38
+
39
+ echo "[playwright] waiting for JupyterLab to be ready..."
40
+ for _ in $(seq 1 60); do
41
+ if curl -fsS "$BASE_URL" >/dev/null 2>&1; then
42
+ break
43
+ fi
44
+ sleep 1
45
+ done
46
+
47
+ if ! curl -fsS "$BASE_URL" >/dev/null 2>&1; then
48
+ echo "[playwright] JupyterLab failed to start. Log: $LOG_FILE"
49
+ exit 1
50
+ fi
51
+
52
+ echo "[playwright] running notebook-tab freeze reproduction e2e"
53
+ PLAYWRIGHT_BASE_URL="$BASE_URL" \
54
+ PLAYWRIGHT_CODEX_COMMAND="$MOCK_CODEX" \
55
+ MOCK_CODEX_EVENT_COUNT="${MOCK_CODEX_EVENT_COUNT:-360}" \
56
+ MOCK_CODEX_EVENT_DELAY_MS="${MOCK_CODEX_EVENT_DELAY_MS_DEFAULT}" \
57
+ MOCK_CODEX_CHUNK_WORDS="${MOCK_CODEX_CHUNK_WORDS:-14}" \
58
+ npx playwright test tests/e2e/freeze-notebook-tabs-repro.spec.js "$@"