@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.
- package/lib/server.js +67 -27
- 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
|
-
|
|
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
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
-
|
|
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 =
|
|
930
|
+
stored.server = server;
|
|
901
931
|
approvalSessions.set(sessionId, stored);
|
|
902
932
|
|
|
903
|
-
//
|
|
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 (
|
|
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
|
-
|
|
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: ${
|
|
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: ${
|
|
961
|
-
} else if (refreshed.status === 'timeout' ||
|
|
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: ${
|
|
1003
|
+
return `⏰ Review session timed out. Elapsed: ${newElapsed}s`;
|
|
965
1004
|
}
|
|
966
1005
|
|
|
967
|
-
return `⏳ Still pending. Elapsed: ${
|
|
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
|
|