@nbakka/mcp-appium 2.0.93 → 2.0.95

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.
@@ -33,20 +33,11 @@
33
33
  padding-bottom: 10px;
34
34
  border-bottom: 2px solid;
35
35
  }
36
- .new {
37
- background-color: #e8f5e8;
38
- border-color: #4caf50;
39
- }
36
+ .new { background-color: #e8f5e8; border-color: #4caf50; }
40
37
  .new h2 { border-bottom-color: #4caf50; color: #2e7d32; }
41
- .modify {
42
- background-color: #fff8e1;
43
- border-color: #ff9800;
44
- }
38
+ .modify { background-color: #fff8e1; border-color: #ff9800; }
45
39
  .modify h2 { border-bottom-color: #ff9800; color: #f57c00; }
46
- .remove {
47
- background-color: #ffebee;
48
- border-color: #f44336;
49
- }
40
+ .remove { background-color: #ffebee; border-color: #f44336; }
50
41
  .remove h2 { border-bottom-color: #f44336; color: #c62828; }
51
42
  .test-case {
52
43
  margin: 15px 0;
@@ -75,35 +66,18 @@
75
66
  font-size: 0.85em;
76
67
  margin-left: 5px;
77
68
  }
78
- .edit-btn {
79
- background-color: #2196f3;
80
- color: white;
81
- }
82
- .save-btn {
83
- background-color: #4caf50;
84
- color: white;
85
- }
86
- .cancel-btn-inline {
87
- background-color: #757575;
88
- color: white;
89
- }
90
- .delete-btn {
91
- background-color: #f44336;
92
- color: white;
93
- }
94
- .delete-btn:hover {
95
- background-color: #d32f2f;
96
- }
69
+ .edit-btn { background-color: #2196f3; color: white; }
70
+ .save-btn { background-color: #4caf50; color: white; }
71
+ .cancel-btn-inline { background-color: #757575; color: white; }
72
+ .delete-btn { background-color: #f44336; color: white; }
73
+ .delete-btn:hover { background-color: #d32f2f; }
97
74
  .original-text {
98
75
  color: #999;
99
76
  text-decoration: line-through;
100
77
  font-style: italic;
101
78
  margin-bottom: 10px;
102
79
  }
103
- .modified-text {
104
- color: #000;
105
- font-weight: 500;
106
- }
80
+ .modified-text { color: #000; font-weight: 500; }
107
81
  .editable-area {
108
82
  width: 100%;
109
83
  min-height: 60px;
@@ -114,10 +88,7 @@
114
88
  font-size: 14px;
115
89
  resize: vertical;
116
90
  }
117
- .editable-area:focus {
118
- outline: none;
119
- border-color: #2196f3;
120
- }
91
+ .editable-area:focus { outline: none; border-color: #2196f3; }
121
92
  .button-container {
122
93
  text-align: center;
123
94
  margin-top: 40px;
@@ -136,22 +107,10 @@
136
107
  letter-spacing: 0.5px;
137
108
  transition: all 0.3s ease;
138
109
  }
139
- .approve {
140
- background-color: #4caf50;
141
- color: white;
142
- }
143
- .approve:hover {
144
- background-color: #45a049;
145
- transform: translateY(-2px);
146
- }
147
- .cancel {
148
- background-color: #f44336;
149
- color: white;
150
- }
151
- .cancel:hover {
152
- background-color: #da190b;
153
- transform: translateY(-2px);
154
- }
110
+ .approve { background-color: #4caf50; color: white; }
111
+ .approve:hover { background-color: #45a049; transform: translateY(-2px); }
112
+ .cancel { background-color: #f44336; color: white; }
113
+ .cancel:hover { background-color: #da190b; transform: translateY(-2px); }
155
114
  .status {
156
115
  margin-top: 20px;
157
116
  padding: 15px;
@@ -203,22 +162,18 @@
203
162
 
204
163
  <script>
205
164
  let sessionId;
206
- let testCases;
165
+ let testCases = { new: [], modify: [], remove: [] };
207
166
  let editingStates = {};
208
167
 
209
168
  async function loadTestCases() {
210
169
  try {
211
170
  const response = await fetch('/api/testcases');
212
- if (!response.ok) {
213
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
214
- }
171
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
215
172
  const data = await response.json();
216
173
  sessionId = data.sessionId;
217
174
  testCases = data.testCases;
218
-
219
175
  document.getElementById('loading').style.display = 'none';
220
176
  document.getElementById('content').style.display = 'block';
221
-
222
177
  renderTestCases();
223
178
  } catch (error) {
224
179
  document.getElementById('loading').innerHTML = `<div class="status error">Error loading test cases: ${error.message}</div>`;
@@ -297,7 +252,7 @@
297
252
  function renderRemoveCases() {
298
253
  const container = document.getElementById('remove-cases');
299
254
  if (testCases.remove && testCases.remove.length > 0) {
300
- container.innerHTML = testCases.remove.map((tc, index) => `
255
+ container.innerHTML = testCases.remove.map(tc => `
301
256
  <div class="test-case">
302
257
  <div class="test-case-header">
303
258
  <span class="test-case-id">ID: ${tc.id}</span>
@@ -318,8 +273,7 @@
318
273
  function toggleEdit(type, index) {
319
274
  const displayDiv = document.getElementById(`${type}-${index}-display`);
320
275
  const editDiv = document.getElementById(`${type}-${index}-edit`);
321
-
322
- if (displayDiv.style.display === 'block') {
276
+ if (displayDiv && editDiv && displayDiv.style.display === 'block') {
323
277
  displayDiv.style.display = 'none';
324
278
  editDiv.style.display = 'block';
325
279
  editingStates[`${type}-${index}`] = true;
@@ -329,16 +283,11 @@
329
283
  function saveEdit(type, index) {
330
284
  if (type === 'new') {
331
285
  const newDesc = document.getElementById(`new-${index}-desc`).value.trim();
332
- if (newDesc) {
333
- testCases.new[index].description = newDesc;
334
- }
286
+ if (newDesc) testCases.new[index].description = newDesc;
335
287
  } else if (type === 'modify') {
336
288
  const newMod = document.getElementById(`modify-${index}-mod`).value.trim();
337
- if (newMod) {
338
- testCases.modify[index].modified = newMod;
339
- }
289
+ if (newMod) testCases.modify[index].modified = newMod;
340
290
  }
341
-
342
291
  cancelEdit(type, index);
343
292
  renderTestCases();
344
293
  showStatus('Changes saved successfully!', 'success');
@@ -347,10 +296,11 @@
347
296
  function cancelEdit(type, index) {
348
297
  const displayDiv = document.getElementById(`${type}-${index}-display`);
349
298
  const editDiv = document.getElementById(`${type}-${index}-edit`);
350
-
351
- displayDiv.style.display = 'block';
352
- editDiv.style.display = 'none';
353
- delete editingStates[`${type}-${index}`];
299
+ if (displayDiv && editDiv) {
300
+ displayDiv.style.display = 'block';
301
+ editDiv.style.display = 'none';
302
+ delete editingStates[`${type}-${index}`];
303
+ }
354
304
  }
355
305
 
356
306
  function showStatus(message, type) {
@@ -358,10 +308,35 @@
358
308
  statusDiv.textContent = message;
359
309
  statusDiv.className = `status ${type}`;
360
310
  statusDiv.style.display = 'block';
311
+ setTimeout(() => { statusDiv.style.display = 'none'; }, 3000);
312
+ }
361
313
 
362
- setTimeout(() => {
363
- statusDiv.style.display = 'none';
364
- }, 3000);
314
+ async function deleteTestCase(type, index) {
315
+ if (!['new', 'modify'].includes(type)) return;
316
+ const confirmation = confirm("Are you sure you want to delete this test case?");
317
+ if (!confirmation) return;
318
+ const item = testCases[type][index];
319
+ if (!item) return;
320
+ try {
321
+ showStatus('Deleting test case...', 'info');
322
+ const response = await fetch(`/delete/${sessionId}`, {
323
+ method: 'POST',
324
+ headers: { 'Content-Type': 'application/json' },
325
+ body: JSON.stringify({ type, id: item.id })
326
+ });
327
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
328
+ const updated = await response.json();
329
+ if (updated.testCases) {
330
+ testCases = updated.testCases;
331
+ } else {
332
+ // fallback local update
333
+ testCases[type] = testCases[type].filter(tc => tc.id !== item.id);
334
+ }
335
+ renderTestCases();
336
+ showStatus('Test case deleted successfully!', 'success');
337
+ } catch (error) {
338
+ showStatus(`Error deleting test case: ${error.message}`, 'error');
339
+ }
365
340
  }
366
341
 
367
342
  async function approveTestCases() {
@@ -372,17 +347,10 @@
372
347
  headers: { 'Content-Type': 'application/json' },
373
348
  body: JSON.stringify(testCases)
374
349
  });
375
-
376
- if (!response.ok) {
377
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
378
- }
379
-
350
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
380
351
  const result = await response.json();
381
352
  showStatus(result.message || 'Test cases approved successfully!', 'success');
382
-
383
- setTimeout(() => {
384
- window.close();
385
- }, 2000);
353
+ setTimeout(() => window.close(), 1200);
386
354
  } catch (error) {
387
355
  showStatus(`Error approving test cases: ${error.message}`, 'error');
388
356
  }
@@ -391,58 +359,17 @@
391
359
  async function cancelReview() {
392
360
  try {
393
361
  showStatus('Cancelling review...', 'info');
394
- const response = await fetch(`/cancel/${sessionId}`, {
395
- method: 'POST'
396
- });
397
-
398
- if (!response.ok) {
399
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
400
- }
401
-
362
+ const response = await fetch(`/cancel/${sessionId}`, { method: 'POST' });
363
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
402
364
  const result = await response.json();
403
365
  showStatus(result.message || 'Review cancelled', 'info');
404
-
405
- setTimeout(() => {
406
- window.close();
407
- }, 2000);
366
+ setTimeout(() => window.close(), 1500);
408
367
  } catch (error) {
409
368
  showStatus(`Error cancelling review: ${error.message}`, 'error');
410
369
  }
411
370
  }
412
371
 
413
- async function deleteTestCase(type, index) {
414
- const confirmation = confirm("Are you sure you want to delete this test case?");
415
- if (!confirmation) return;
416
-
417
- try {
418
- showStatus('Deleting test case...', 'info');
419
- const response = await fetch(`/delete/${sessionId}`, {
420
- method: 'POST',
421
- headers: { 'Content-Type': 'application/json' },
422
- body: JSON.stringify({ type, index })
423
- });
424
-
425
- if (!response.ok) {
426
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
427
- }
428
-
429
- // Remove the deleted test case from the local state first
430
- if (type === 'new') {
431
- testCases.new.splice(index, 1);
432
- } else if (type === 'modify') {
433
- testCases.modify.splice(index, 1);
434
- }
435
-
436
- // Re-render the UI to update indexes and remove the element
437
- renderTestCases();
438
- showStatus('Test case deleted successfully!', 'success');
439
- } catch (error) {
440
- showStatus(`Error deleting test case: ${error.message}`, 'error');
441
- }
442
- }
443
-
444
- // Load test cases when page loads
445
372
  window.onload = loadTestCases;
446
373
  </script>
447
374
  </body>
448
- </html>
375
+ </html>
package/lib/server.js CHANGED
@@ -798,219 +798,177 @@ tool(
798
798
  let approvalSessions = new Map();
799
799
 
800
800
  tool(
801
- "review_testcases",
802
- "Open test cases in browser for manual approval.",
803
- {
804
- testCases: zod_1.z.array(zod_1.z.array(zod_1.z.string())).describe("test cases array generated by tool generate_testcases_from_ticket_data")
805
- },
806
- async ({ testCases }) => {
807
- try {
808
- const express = require('express');
809
- const path = require('path');
810
- const sessionId = Date.now().toString();
811
-
812
- // Add the improved parseTestCases function here
813
- function parseTestCases(testCasesArray) {
814
- const parsed = {
815
- new: [],
816
- modify: [],
817
- remove: []
818
- };
819
-
820
- testCasesArray.forEach(tc => {
821
- if (tc.length === 4 && tc[2] === 'Modify') {
822
- // Modify: [original, modified, "Modify", tcmsId]
823
- parsed.modify.push({
824
- id: tc[3],
825
- original: tc[0],
826
- modified: tc[1]
827
- });
828
- } else if (tc.length === 3 && tc[1] === 'Remove') {
829
- // Remove: [description, "Remove", tcmsId]
830
- parsed.remove.push({
831
- id: tc[2],
832
- description: tc[0]
833
- });
834
- } else if (tc.length === 2 && tc[1] === 'New') {
835
- // New: [description, "New"]
836
- parsed.new.push({
837
- description: tc[0],
838
- id: `NEW-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
839
- });
840
- }
841
- });
842
-
843
- return parsed;
844
- }
845
-
846
- // Use the improved parsing function
847
- const parsedTestCases = parseTestCases(testCases);
848
-
849
- // Remove the old parsing logic and console.log statements that cause JSON errors
850
- // Initialize session state
851
- approvalSessions.set(sessionId, {
852
- status: 'pending',
853
- testCases: parsedTestCases,
854
- originalTestCases: JSON.parse(JSON.stringify(parsedTestCases)),
855
- server: null,
856
- startTime: Date.now()
801
+ "review_testcases",
802
+ "Open test cases in browser for manual approval.",
803
+ {
804
+ testCases: zod_1.z
805
+ .array(zod_1.z.array(zod_1.z.string()))
806
+ .describe("test cases array generated by tool generate_testcases_from_ticket_data")
807
+ },
808
+ async ({ testCases }) => {
809
+ try {
810
+ const sessionId = Date.now().toString();
811
+
812
+ function parseTestCases(testCasesArray) {
813
+ const parsed = { new: [], modify: [], remove: [] };
814
+ testCasesArray.forEach(tc => {
815
+ if (tc.length === 4 && tc[2] === 'Modify') {
816
+ parsed.modify.push({ id: tc[3], original: tc[0], modified: tc[1] });
817
+ } else if (tc.length === 3 && tc[1] === 'Remove') {
818
+ parsed.remove.push({ id: tc[2], description: tc[0] });
819
+ } else if (tc.length === 2 && tc[1] === 'New') {
820
+ parsed.new.push({
821
+ description: tc[0],
822
+ id: `NEW-${Date.now()}-${Math.random().toString(36).slice(2)}`
857
823
  });
824
+ }
825
+ });
826
+ return parsed;
827
+ }
858
828
 
859
- const app = express();
860
- const port = 3001;
861
-
862
- app.use(express.json());
863
- app.use(express.static(path.join(__dirname, 'review-ui')));
864
-
865
- app.get("/", (req, res) => {
866
- res.sendFile(path.join(__dirname, 'review-ui', 'index.html'));
867
- });
829
+ const parsedTestCases = parseTestCases(testCases);
868
830
 
869
- app.get("/api/testcases", (req, res) => {
870
- const session = approvalSessions.get(sessionId);
871
- if (!session) {
872
- return res.status(404).json({ error: 'Session not found' });
873
- }
874
- res.json({
875
- sessionId: sessionId,
876
- testCases: session.testCases
877
- });
878
- });
879
-
880
- app.post(`/approve/${sessionId}`, (req, res) => {
881
- const session = approvalSessions.get(sessionId);
882
- if (session) {
883
- session.status = 'approved';
884
- session.finalTestCases = req.body;
885
- approvalSessions.set(sessionId, session);
886
- }
887
- res.json({ status: 'approved', message: 'Test cases approved successfully!' });
888
- });
831
+ approvalSessions.set(sessionId, {
832
+ status: 'pending',
833
+ testCases: parsedTestCases,
834
+ originalTestCases: JSON.parse(JSON.stringify(parsedTestCases)),
835
+ server: null,
836
+ startTime: Date.now()
837
+ });
889
838
 
890
- app.post(`/cancel/${sessionId}`, (req, res) => {
891
- const session = approvalSessions.get(sessionId);
892
- if (session) {
893
- session.status = 'cancelled';
894
- approvalSessions.set(sessionId, session);
895
- }
896
- res.json({ status: 'cancelled', message: 'Review cancelled' });
897
- });
839
+ const app = express();
840
+ const port = 3001;
898
841
 
899
- app.post(`/delete/${sessionId}`, (req, res) => {
900
- const session = approvalSessions.get(sessionId);
901
- if (!session) {
902
- return res.status(404).json({ error: 'Session not found' });
903
- }
842
+ app.use(express.json());
843
+ app.use(express.static(path.join(__dirname, 'review-ui')));
904
844
 
905
- const { type, index } = req.body;
906
-
907
- try {
908
- if (type === 'new' && session.testCases.new && index < session.testCases.new.length) {
909
- session.testCases.new.splice(index, 1);
910
- } else if (type === 'modify' && session.testCases.modify && index < session.testCases.modify.length) {
911
- session.testCases.modify.splice(index, 1);
912
- } else {
913
- return res.status(400).json({ error: 'Invalid type or index' });
914
- }
915
-
916
- approvalSessions.set(sessionId, session);
917
- res.json({ status: 'deleted', message: 'Test case deleted successfully' });
918
- } catch (error) {
919
- res.status(500).json({ error: 'Failed to delete test case' });
920
- }
921
- });
845
+ app.get("/", (_req, res) =>
846
+ res.sendFile(path.join(__dirname, 'review-ui', 'index.html'))
847
+ );
922
848
 
923
- const server = app.listen(port, async () => {
924
- // Remove console.log to prevent JSON parsing errors
925
- try {
926
- const { default: open } = await import('open');
927
- await open(`http://localhost:${port}`);
928
- } catch (openError) {
929
- // Silent fail for browser opening
930
- }
931
- });
849
+ app.get("/api/testcases", (_req, res) => {
850
+ const session = approvalSessions.get(sessionId);
851
+ if (!session) return res.status(404).json({ error: 'Session not found' });
852
+ res.json({ sessionId, testCases: session.testCases });
853
+ });
932
854
 
933
- const session = approvalSessions.get(sessionId);
934
- session.server = server;
935
- approvalSessions.set(sessionId, session);
936
-
937
- setTimeout(() => {
938
- const session = approvalSessions.get(sessionId);
939
- if (session && session.status === 'pending') {
940
- session.status = 'timeout';
941
- if (session.server) {
942
- session.server.close();
943
- }
944
- approvalSessions.set(sessionId, session);
945
- }
946
- }, 300000);
947
-
948
- // Return clean JSON response
949
- return JSON.stringify({
950
- status: "review_started",
951
- sessionId: sessionId,
952
- message: "Test case review interface opened in browser. Use check_approval_status tool to poll for approval.",
953
- testCasesCount: testCases.length,
954
- browserUrl: `http://localhost:${port}`,
955
- instructions: "Poll using check_approval_status tool continuously for max 10 times"
956
- });
855
+ app.post(`/delete/${sessionId}`, (req, res) => {
856
+ const session = approvalSessions.get(sessionId);
857
+ if (!session) return res.status(404).json({ error: 'Session not found' });
957
858
 
958
- } catch (err) {
959
- return JSON.stringify({
960
- status: "error",
961
- message: `Error setting up review interface: ${err.message}`
962
- });
859
+ const { type, id } = req.body;
860
+ if (!['new', 'modify'].includes(type) || !id) {
861
+ return res.status(400).json({ error: 'Invalid delete request' });
963
862
  }
964
- }
965
- );
966
-
967
- tool(
968
- "check_approval_status",
969
- "Check the approval status of test cases review session with 25 second wait",
970
- {
971
- sessionId: zod_1.z.string().describe("The session ID returned from review_testcases tool")
972
- },
973
- async ({ sessionId }) => {
974
- await new Promise(resolve => setTimeout(resolve, 25000));
863
+ try {
864
+ session.testCases[type] = session.testCases[type].filter(tc => tc.id !== id);
865
+ approvalSessions.set(sessionId, session);
866
+ return res.json({ status: 'deleted', testCases: session.testCases });
867
+ } catch {
868
+ return res.status(500).json({ error: 'Failed to delete test case' });
869
+ }
870
+ });
975
871
 
872
+ app.post(`/approve/${sessionId}`, (req, res) => {
976
873
  const session = approvalSessions.get(sessionId);
977
- if (!session) {
978
- return `❌ Session not found. Invalid session ID: ${sessionId}`;
874
+ if (session) {
875
+ // overwrite authoritative copy with the submitted version
876
+ session.testCases = req.body;
877
+ session.finalTestCases = req.body;
878
+ session.status = 'approved';
879
+ approvalSessions.set(sessionId, session);
979
880
  }
881
+ res.json({ status: 'approved', message: 'Test cases approved successfully!' });
882
+ });
980
883
 
981
- const currentTime = Date.now();
982
- const elapsedTime = Math.floor((currentTime - session.startTime) / 1000);
884
+ app.post(`/cancel/${sessionId}`, (_req, res) => {
885
+ const session = approvalSessions.get(sessionId);
886
+ if (session) {
887
+ session.status = 'cancelled';
888
+ approvalSessions.set(sessionId, session);
889
+ }
890
+ res.json({ status: 'cancelled', message: 'Review cancelled' });
891
+ });
983
892
 
984
- if (session.status === 'approved') {
985
- const approvedTestCases = session.finalTestCases || session.testCases;
893
+ const serverInstance = app.listen(port, async () => {
894
+ try {
895
+ const { default: open } = await import('open');
896
+ await open(`http://localhost:${port}`);
897
+ } catch { /* silent */ }
898
+ });
986
899
 
987
- if (session.server) {
988
- session.server.close();
989
- }
990
- approvalSessions.delete(sessionId);
900
+ const stored = approvalSessions.get(sessionId);
901
+ stored.server = serverInstance;
902
+ approvalSessions.set(sessionId, stored);
903
+
904
+ // timeout safeguard
905
+ setTimeout(() => {
906
+ const s = approvalSessions.get(sessionId);
907
+ if (s && s.status === 'pending') {
908
+ s.status = 'timeout';
909
+ s.server?.close();
910
+ approvalSessions.set(sessionId, s);
911
+ }
912
+ }, 300000);
913
+
914
+ return {
915
+ status: "review_started",
916
+ sessionId,
917
+ message: "Test case review interface opened.",
918
+ testCasesCount: testCases.length,
919
+ browserUrl: `http://localhost:${port}`,
920
+ instructions: "Use check_approval_status tool to poll."
921
+ };
922
+ } catch (err) {
923
+ return { status: "error", message: `Error setting up review interface: ${err.message}` };
924
+ }
925
+ }
926
+ );
991
927
 
992
- return `✅ Test cases approved successfully! Elapsed time: ${elapsedTime}s\n\nApproved test cases:\n${JSON.stringify(approvedTestCases, null, 2)}`;
993
- } else if (session.status === 'cancelled') {
994
- if (session.server) {
995
- session.server.close();
996
- }
997
- approvalSessions.delete(sessionId);
998
928
 
999
- return `❌ Review was cancelled by the user. Elapsed time: ${elapsedTime}s`;
1000
- } else if (session.status === 'timeout' || elapsedTime > 300) {
1001
- if (session.server) {
1002
- session.server.close();
1003
- }
1004
- approvalSessions.delete(sessionId);
929
+ tool(
930
+ "check_approval_status",
931
+ "Check the approval status of test cases review session (short-circuits if already approved)",
932
+ { sessionId: zod_1.z.string().describe("Session ID from review_testcases") },
933
+ async ({ sessionId }) => {
934
+ const session = approvalSessions.get(sessionId);
935
+ if (!session) return `❌ Session not found. Invalid session ID: ${sessionId}`;
936
+
937
+ const now = Date.now();
938
+ const elapsed = Math.floor((now - session.startTime) / 1000);
939
+
940
+ if (session.status === 'approved') {
941
+ const approvedTestCases = session.finalTestCases || session.testCases;
942
+ session.server?.close();
943
+ approvalSessions.delete(sessionId);
944
+ return `✅ Test cases approved. Elapsed: ${elapsed}s\n${JSON.stringify(approvedTestCases, null, 2)}`;
945
+ }
1005
946
 
1006
- return `⏰ Review session timed out. Elapsed time: ${elapsedTime}s`;
1007
- } else {
1008
- const remainingTime = Math.max(0, 300 - elapsedTime);
1009
- return `⏳ Test cases are still pending approval. Elapsed: ${elapsedTime}s, Remaining: ${remainingTime}s`;
1010
- }
947
+ await new Promise(r => setTimeout(r, 5000));
948
+
949
+ const refreshed = approvalSessions.get(sessionId);
950
+ if (!refreshed) return `❌ Session expired`;
951
+
952
+ if (refreshed.status === 'approved') {
953
+ const approvedTestCases = refreshed.finalTestCases || refreshed.testCases;
954
+ refreshed.server?.close();
955
+ approvalSessions.delete(sessionId);
956
+ return `✅ Test cases approved. Elapsed: ${elapsed + 5}s\n${JSON.stringify(approvedTestCases, null, 2)}`;
957
+ } else if (refreshed.status === 'cancelled') {
958
+ refreshed.server?.close();
959
+ approvalSessions.delete(sessionId);
960
+ return `❌ Review cancelled. Elapsed: ${elapsed + 5}s`;
961
+ } else if (refreshed.status === 'timeout' || elapsed > 300) {
962
+ refreshed.server?.close();
963
+ approvalSessions.delete(sessionId);
964
+ return `⏰ Review session timed out. Elapsed: ${elapsed + 5}s`;
1011
965
  }
966
+
967
+ return `⏳ Still pending. Elapsed: ${elapsed + 5}s Remaining: ${Math.max(0, 300 - (elapsed + 5))}s`;
968
+ }
1012
969
  );
1013
970
 
971
+
1014
972
  return server;
1015
973
  };
1016
974
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.93",
3
+ "version": "2.0.95",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"