@nbakka/mcp-appium 2.0.95 → 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 +76 -36
- 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,13 +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
|
-
|
|
880
|
-
|
|
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
|
+
|
|
881
915
|
res.json({ status: 'approved', message: 'Test cases approved successfully!' });
|
|
882
916
|
});
|
|
883
917
|
|
|
@@ -885,23 +919,24 @@ tool(
|
|
|
885
919
|
const session = approvalSessions.get(sessionId);
|
|
886
920
|
if (session) {
|
|
887
921
|
session.status = 'cancelled';
|
|
922
|
+
session.cancelledAt = Date.now();
|
|
888
923
|
approvalSessions.set(sessionId, session);
|
|
889
924
|
}
|
|
890
925
|
res.json({ status: 'cancelled', message: 'Review cancelled' });
|
|
891
926
|
});
|
|
892
927
|
|
|
893
|
-
|
|
894
|
-
try {
|
|
895
|
-
const { default: open } = await import('open');
|
|
896
|
-
await open(`http://localhost:${port}`);
|
|
897
|
-
} catch { /* silent */ }
|
|
898
|
-
});
|
|
899
|
-
|
|
928
|
+
// Update session with server reference
|
|
900
929
|
const stored = approvalSessions.get(sessionId);
|
|
901
|
-
stored.server =
|
|
930
|
+
stored.server = server;
|
|
902
931
|
approvalSessions.set(sessionId, stored);
|
|
903
932
|
|
|
904
|
-
//
|
|
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
|
|
905
940
|
setTimeout(() => {
|
|
906
941
|
const s = approvalSessions.get(sessionId);
|
|
907
942
|
if (s && s.status === 'pending') {
|
|
@@ -911,24 +946,23 @@ tool(
|
|
|
911
946
|
}
|
|
912
947
|
}, 300000);
|
|
913
948
|
|
|
914
|
-
return
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
949
|
+
return `✅ Test case review interface opened successfully!
|
|
950
|
+
Session ID: ${sessionId}
|
|
951
|
+
Test Cases Count: ${testCases.length}
|
|
952
|
+
Browser URL: http://localhost:${port}
|
|
953
|
+
Status: review_started
|
|
954
|
+
|
|
955
|
+
Instructions: Use check_approval_status tool with session ID "${sessionId}" to poll for approval status.`;
|
|
956
|
+
|
|
922
957
|
} catch (err) {
|
|
923
|
-
return
|
|
958
|
+
return `❌ Error setting up review interface: ${err.message}`;
|
|
924
959
|
}
|
|
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
|
|