@nbakka/mcp-appium 2.0.96 → 2.0.97

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 (2) hide show
  1. package/lib/server.js +67 -27
  2. package/package.json +1 -1
package/lib/server.js CHANGED
@@ -828,16 +828,47 @@ tool(
828
828
 
829
829
  const parsedTestCases = parseTestCases(testCases);
830
830
 
831
+ // Initialize session with proper locking mechanism
831
832
  approvalSessions.set(sessionId, {
832
833
  status: 'pending',
833
834
  testCases: parsedTestCases,
834
835
  originalTestCases: JSON.parse(JSON.stringify(parsedTestCases)),
835
836
  server: null,
836
- startTime: Date.now()
837
+ startTime: Date.now(),
838
+ locked: false
837
839
  });
838
840
 
839
841
  const app = express();
840
- const port = 3001;
842
+
843
+ // Try multiple ports if 3001 is busy
844
+ let port = 3001;
845
+ let server = null;
846
+
847
+ const tryStartServer = (portToTry) => {
848
+ return new Promise((resolve, reject) => {
849
+ const testServer = app.listen(portToTry, () => {
850
+ resolve({ server: testServer, port: portToTry });
851
+ }).on('error', (err) => {
852
+ if (err.code === 'EADDRINUSE') {
853
+ reject(err);
854
+ } else {
855
+ reject(err);
856
+ }
857
+ });
858
+ });
859
+ };
860
+
861
+ // Try ports 3001-3010
862
+ for (let i = 0; i < 10; i++) {
863
+ try {
864
+ const result = await tryStartServer(port + i);
865
+ server = result.server;
866
+ port = result.port;
867
+ break;
868
+ } catch (err) {
869
+ if (i === 9) throw new Error(`No available ports found (tried ${port}-${port + 9})`);
870
+ }
871
+ }
841
872
 
842
873
  app.use(express.json());
843
874
  app.use(express.static(path.join(__dirname, 'review-ui')));
@@ -871,12 +902,16 @@ tool(
871
902
 
872
903
  app.post(`/approve/${sessionId}`, (req, res) => {
873
904
  const session = approvalSessions.get(sessionId);
874
- if (session) {
875
- session.testCases = req.body;
876
- session.finalTestCases = req.body;
877
- session.status = 'approved';
878
- approvalSessions.set(sessionId, session);
879
- }
905
+ if (!session) return res.status(404).json({ error: 'Session not found' });
906
+
907
+ // Use atomic update with locking
908
+ session.locked = true;
909
+ session.testCases = req.body;
910
+ session.finalTestCases = req.body;
911
+ session.status = 'approved';
912
+ session.approvedAt = Date.now();
913
+ approvalSessions.set(sessionId, session);
914
+
880
915
  res.json({ status: 'approved', message: 'Test cases approved successfully!' });
881
916
  });
882
917
 
@@ -884,23 +919,24 @@ tool(
884
919
  const session = approvalSessions.get(sessionId);
885
920
  if (session) {
886
921
  session.status = 'cancelled';
922
+ session.cancelledAt = Date.now();
887
923
  approvalSessions.set(sessionId, session);
888
924
  }
889
925
  res.json({ status: 'cancelled', message: 'Review cancelled' });
890
926
  });
891
927
 
892
- const serverInstance = app.listen(port, async () => {
893
- try {
894
- const { default: open } = await import('open');
895
- await open(`http://localhost:${port}`);
896
- } catch { /* silent */ }
897
- });
898
-
928
+ // Update session with server reference
899
929
  const stored = approvalSessions.get(sessionId);
900
- stored.server = serverInstance;
930
+ stored.server = server;
901
931
  approvalSessions.set(sessionId, stored);
902
932
 
903
- // timeout safeguard
933
+ // Open browser
934
+ try {
935
+ const { default: open } = await import('open');
936
+ await open(`http://localhost:${port}`);
937
+ } catch { /* silent */ }
938
+
939
+ // Timeout safeguard - but don't override approved status
904
940
  setTimeout(() => {
905
941
  const s = approvalSessions.get(sessionId);
906
942
  if (s && s.status === 'pending') {
@@ -910,7 +946,6 @@ tool(
910
946
  }
911
947
  }, 300000);
912
948
 
913
- // Return a simple string instead of an object
914
949
  return `✅ Test case review interface opened successfully!
915
950
  Session ID: ${sessionId}
916
951
  Test Cases Count: ${testCases.length}
@@ -925,10 +960,9 @@ Instructions: Use check_approval_status tool with session ID "${sessionId}" to p
925
960
  }
926
961
  );
927
962
 
928
-
929
963
  tool(
930
964
  "check_approval_status",
931
- "Check the approval status of test cases review session (short-circuits if already approved)",
965
+ "Check the approval status of test cases review session (waits 20 seconds before checking)",
932
966
  { sessionId: zod_1.z.string().describe("Session ID from review_testcases") },
933
967
  async ({ sessionId }) => {
934
968
  const session = approvalSessions.get(sessionId);
@@ -937,6 +971,7 @@ tool(
937
971
  const now = Date.now();
938
972
  const elapsed = Math.floor((now - session.startTime) / 1000);
939
973
 
974
+ // Check if already approved (short-circuit without waiting)
940
975
  if (session.status === 'approved') {
941
976
  const approvedTestCases = session.finalTestCases || session.testCases;
942
977
  session.server?.close();
@@ -944,31 +979,36 @@ tool(
944
979
  return `✅ Test cases approved. Elapsed: ${elapsed}s\n${JSON.stringify(approvedTestCases, null, 2)}`;
945
980
  }
946
981
 
947
- await new Promise(r => setTimeout(r, 5000));
982
+ // Wait 20 seconds before checking status
983
+ await new Promise(r => setTimeout(r, 20000));
948
984
 
985
+ // Re-fetch session after wait
949
986
  const refreshed = approvalSessions.get(sessionId);
950
- if (!refreshed) return `❌ Session expired`;
987
+ if (!refreshed) return `❌ Session expired during wait`;
988
+
989
+ const newElapsed = Math.floor((Date.now() - refreshed.startTime) / 1000);
951
990
 
952
991
  if (refreshed.status === 'approved') {
953
992
  const approvedTestCases = refreshed.finalTestCases || refreshed.testCases;
954
993
  refreshed.server?.close();
955
994
  approvalSessions.delete(sessionId);
956
- return `✅ Test cases approved. Elapsed: ${elapsed + 5}s\n${JSON.stringify(approvedTestCases, null, 2)}`;
995
+ return `✅ Test cases approved. Elapsed: ${newElapsed}s\n${JSON.stringify(approvedTestCases, null, 2)}`;
957
996
  } else if (refreshed.status === 'cancelled') {
958
997
  refreshed.server?.close();
959
998
  approvalSessions.delete(sessionId);
960
- return `❌ Review cancelled. Elapsed: ${elapsed + 5}s`;
961
- } else if (refreshed.status === 'timeout' || elapsed > 300) {
999
+ return `❌ Review cancelled. Elapsed: ${newElapsed}s`;
1000
+ } else if (refreshed.status === 'timeout' || newElapsed > 300) {
962
1001
  refreshed.server?.close();
963
1002
  approvalSessions.delete(sessionId);
964
- return `⏰ Review session timed out. Elapsed: ${elapsed + 5}s`;
1003
+ return `⏰ Review session timed out. Elapsed: ${newElapsed}s`;
965
1004
  }
966
1005
 
967
- return `⏳ Still pending. Elapsed: ${elapsed + 5}s Remaining: ${Math.max(0, 300 - (elapsed + 5))}s`;
1006
+ return `⏳ Still pending. Elapsed: ${newElapsed}s Remaining: ${Math.max(0, 300 - newElapsed)}s`;
968
1007
  }
969
1008
  );
970
1009
 
971
1010
 
1011
+
972
1012
  return server;
973
1013
  };
974
1014
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.96",
3
+ "version": "2.0.97",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"