@autifyhq/aximo-cli 0.6.0 → 0.7.0

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 (81) hide show
  1. package/README.md +79 -64
  2. package/dist/client.js +16 -1
  3. package/dist/commands/mobile/connect-core.d.ts +2 -1
  4. package/dist/commands/mobile/connect-core.js +104 -6
  5. package/dist/commands/mobile/connect.js +3 -1
  6. package/dist/commands/mobile/doctor.d.ts +3 -0
  7. package/dist/commands/mobile/doctor.js +44 -14
  8. package/dist/commands/mobile/doctor.test.d.ts +1 -0
  9. package/dist/commands/mobile/doctor.test.js +14 -0
  10. package/dist/commands/mobile/install.d.ts +9 -2
  11. package/dist/commands/mobile/install.js +43 -12
  12. package/dist/commands/mobile/install.test.js +44 -6
  13. package/dist/commands/mobile/list.d.ts +2 -0
  14. package/dist/commands/mobile/list.js +17 -11
  15. package/dist/commands/mobile/unregister.js +11 -6
  16. package/dist/mobile-control/app-install.d.ts +18 -5
  17. package/dist/mobile-control/app-install.js +33 -6
  18. package/dist/mobile-control/app-install.test.js +42 -7
  19. package/dist/mobile-control/appium-controller.d.ts +8 -0
  20. package/dist/mobile-control/appium-controller.js +19 -4
  21. package/dist/mobile-control/bundle-id-map.d.ts +9 -0
  22. package/dist/mobile-control/bundle-id-map.js +27 -0
  23. package/dist/mobile-control/devices.d.ts +6 -3
  24. package/dist/mobile-control/devices.js +26 -4
  25. package/dist/mobile-control/env-flags.d.ts +5 -0
  26. package/dist/mobile-control/env-flags.js +10 -0
  27. package/dist/mobile-control/env-flags.test.d.ts +1 -0
  28. package/dist/mobile-control/env-flags.test.js +21 -0
  29. package/dist/mobile-control/ios-auto-resign.d.ts +45 -0
  30. package/dist/mobile-control/ios-auto-resign.js +108 -0
  31. package/dist/mobile-control/ios-auto-resign.test.d.ts +1 -0
  32. package/dist/mobile-control/ios-auto-resign.test.js +150 -0
  33. package/dist/mobile-control/ios-devicectl.d.ts +39 -0
  34. package/dist/mobile-control/ios-devicectl.js +87 -0
  35. package/dist/mobile-control/ios-devicectl.test.d.ts +1 -0
  36. package/dist/mobile-control/ios-devicectl.test.js +110 -0
  37. package/dist/mobile-control/ios-identity.d.ts +27 -0
  38. package/dist/mobile-control/ios-identity.js +49 -0
  39. package/dist/mobile-control/ios-identity.test.d.ts +1 -0
  40. package/dist/mobile-control/ios-identity.test.js +102 -0
  41. package/dist/mobile-control/ios-mint.d.ts +39 -0
  42. package/dist/mobile-control/ios-mint.js +78 -0
  43. package/dist/mobile-control/ios-mint.test.d.ts +1 -0
  44. package/dist/mobile-control/ios-mint.test.js +94 -0
  45. package/dist/mobile-control/ios-profiles.d.ts +46 -0
  46. package/dist/mobile-control/ios-profiles.js +106 -0
  47. package/dist/mobile-control/ios-profiles.test.d.ts +1 -0
  48. package/dist/mobile-control/ios-profiles.test.js +154 -0
  49. package/dist/mobile-control/ios-resign.d.ts +37 -0
  50. package/dist/mobile-control/ios-resign.js +101 -0
  51. package/dist/mobile-control/ios-resign.test.d.ts +1 -0
  52. package/dist/mobile-control/ios-resign.test.js +84 -0
  53. package/dist/mobile-control/ios-session-error.d.ts +10 -0
  54. package/dist/mobile-control/ios-session-error.js +34 -0
  55. package/dist/mobile-control/ios-session-error.test.d.ts +1 -0
  56. package/dist/mobile-control/ios-session-error.test.js +30 -0
  57. package/dist/mobile-control/ios-signing.d.ts +33 -5
  58. package/dist/mobile-control/ios-signing.js +56 -0
  59. package/dist/mobile-control/ios-signing.test.js +87 -0
  60. package/dist/mobile-control/ios-tunnel.d.ts +54 -0
  61. package/dist/mobile-control/ios-tunnel.js +130 -0
  62. package/dist/mobile-control/ios-tunnel.test.d.ts +1 -0
  63. package/dist/mobile-control/ios-tunnel.test.js +115 -0
  64. package/dist/mobile-control/ios-wda-cache.d.ts +39 -0
  65. package/dist/mobile-control/ios-wda-cache.js +157 -0
  66. package/dist/mobile-control/ios-wda-cache.test.d.ts +1 -0
  67. package/dist/mobile-control/ios-wda-cache.test.js +128 -0
  68. package/dist/mobile-control/ios-wda-diagnostics.d.ts +14 -0
  69. package/dist/mobile-control/ios-wda-diagnostics.js +138 -0
  70. package/dist/mobile-control/ios-wda-diagnostics.test.d.ts +1 -0
  71. package/dist/mobile-control/ios-wda-diagnostics.test.js +77 -0
  72. package/dist/mobile-control/ios-wda-preinstall.d.ts +64 -0
  73. package/dist/mobile-control/ios-wda-preinstall.js +162 -0
  74. package/dist/mobile-control/ios-wda-preinstall.test.d.ts +1 -0
  75. package/dist/mobile-control/ios-wda-preinstall.test.js +107 -0
  76. package/dist/mobile-control/mobile-errors.d.ts +12 -0
  77. package/dist/mobile-control/mobile-errors.js +91 -0
  78. package/dist/mobile-control/mobile-errors.test.d.ts +1 -0
  79. package/dist/mobile-control/mobile-errors.test.js +48 -0
  80. package/dist/mobile-control/tunnel.js +45 -1
  81. package/package.json +2 -1
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mobileErrorHint, withMobileHint } from "./mobile-errors.js";
3
+ describe("mobileErrorHint (iOS)", () => {
4
+ it("guides trusting the developer cert", () => {
5
+ expect(mobileErrorHint("...its profile has not been explicitly trusted by the user", "ios")).toContain("VPN & Device Management");
6
+ });
7
+ it("guides setting a unique WDA bundle id when the id is taken", () => {
8
+ expect(mobileErrorHint('The app identifier "com.facebook.WebDriverAgentRunner.xctrunner" cannot be registered to your development team', "ios")).toContain("AXIMO_IOS_WDA_BUNDLE_ID");
9
+ });
10
+ it("guides installing the iOS platform components", () => {
11
+ expect(mobileErrorHint("error: iOS 26.5 is not installed. Please download", "ios")).toContain("downloadPlatform");
12
+ });
13
+ it("explains that a sideloaded .ipa must be device-signed", () => {
14
+ expect(mobileErrorHint("Last event ApplicationVerificationFailed", "ios")).toContain("signed for THIS device");
15
+ });
16
+ it("falls back to a general WDA hint for a bare xcodebuild failure", () => {
17
+ expect(mobileErrorHint("xcodebuild failed with code 65", "ios")).toContain("aximo mobile doctor");
18
+ });
19
+ it("matches a refused connection on a dynamic wdaLocalPort", () => {
20
+ expect(mobileErrorHint("Connection refused to port 51234", "ios")).toContain("device tunnel");
21
+ });
22
+ it("matches the driver's RemoteXPC tunnel errors", () => {
23
+ expect(mobileErrorHint("RemoteXPC tunnel is not available for this session", "ios")).toContain("device tunnel");
24
+ });
25
+ it("hints unlocking for a locked device but not for 'unlocked' messages", () => {
26
+ expect(mobileErrorHint("The device 'iPhone' is locked", "ios")).toContain("Unlock");
27
+ expect(mobileErrorHint("the device was not, or could not be, unlocked", "ios")).toContain("Unlock");
28
+ expect(mobileErrorHint("the device screen is unlocked and ready", "ios")).toBeUndefined();
29
+ });
30
+ it("returns undefined for an unrecognized message", () => {
31
+ expect(mobileErrorHint("some unrelated failure", "ios")).toBeUndefined();
32
+ });
33
+ });
34
+ describe("mobileErrorHint (Android)", () => {
35
+ it("guides uninstalling on a signature mismatch", () => {
36
+ expect(mobileErrorHint("Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE]", "android")).toContain("adb uninstall");
37
+ });
38
+ });
39
+ describe("withMobileHint", () => {
40
+ it("appends the hint to the message when one matches", () => {
41
+ const out = withMobileHint("Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE]", "android");
42
+ expect(out).toContain("Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE] — ");
43
+ expect(out).toContain("out of storage");
44
+ });
45
+ it("returns the message unchanged when nothing matches", () => {
46
+ expect(withMobileHint("weird error", "ios")).toBe("weird error");
47
+ });
48
+ });
@@ -119,6 +119,7 @@ export async function runControlTunnel(options) {
119
119
  registerHandlers(socket, options.controller, options.installApp);
120
120
  await new Promise((resolve, reject) => {
121
121
  let shuttingDown = false;
122
+ let serverKicks = [];
122
123
  const cleanup = () => {
123
124
  if (shuttingDown)
124
125
  return;
@@ -126,12 +127,27 @@ export async function runControlTunnel(options) {
126
127
  socket.close();
127
128
  resolve();
128
129
  };
130
+ // Handshake rejections that re-trying can never fix (bad/revoked token,
131
+ // feature disabled). Everything else is treated as transient.
132
+ const isTerminalAuthError = (message) => /device token|not enabled for this organization/i.test(message);
129
133
  socket.on("connect", () => {
130
134
  console.log(`Connected to Aximo as "${options.deviceName}" (${options.wsUrl}${SOCKET_PATH})`);
135
+ // Once a session has been established, outlive server deploys/restarts:
136
+ // retry forever instead of giving up after the initial-connect budget.
137
+ socket.io.opts.reconnectionAttempts = Infinity;
131
138
  });
132
139
  socket.on("connect_error", (err) => {
140
+ if (isTerminalAuthError(err.message)) {
141
+ // Mark as shutting down BEFORE close(): close emits a client-initiated
142
+ // "disconnect" that would otherwise double-handle this terminal error.
143
+ shuttingDown = true;
144
+ socket.close();
145
+ reject(new Error(`Connection rejected: ${err.message}. Re-register the device with \`aximo mobile register\`.`));
146
+ return;
147
+ }
133
148
  // socket.active is false once reconnection is given up; reject so the CLI exits non-zero.
134
149
  if (!socket.active) {
150
+ shuttingDown = true;
135
151
  socket.close();
136
152
  reject(new Error(`Connection error: ${err.message}`));
137
153
  return;
@@ -143,8 +159,30 @@ export async function runControlTunnel(options) {
143
159
  // resolved the promise, so don't report our own close as an error.
144
160
  if (shuttingDown)
145
161
  return;
162
+ // A server-initiated disconnect (gateway deploy/restart kicking the socket)
163
+ // does NOT auto-reconnect per socket.io semantics — reconnect explicitly so
164
+ // a routine deploy doesn't strand the device offline. But rapid repeated
165
+ // kicks mean another `connect` for this device keeps displacing us (the
166
+ // gateway kicks the old socket on every re-registration): back off and exit
167
+ // instead of fighting it for the slot.
168
+ if (reason === "io server disconnect") {
169
+ const now = Date.now();
170
+ serverKicks = serverKicks.filter((t) => now - t < 30_000);
171
+ serverKicks.push(now);
172
+ if (serverKicks.length >= 3) {
173
+ shuttingDown = true;
174
+ socket.close();
175
+ reject(new Error("Disconnected repeatedly by the server — another `aximo mobile connect` " +
176
+ "is likely running for this device. Stop the other instance first."));
177
+ return;
178
+ }
179
+ console.log("Server closed the connection (restart/deploy); reconnecting...");
180
+ socket.connect();
181
+ return;
182
+ }
146
183
  // socket.active is false when the disconnect is terminal (the client won't reconnect).
147
184
  if (!socket.active) {
185
+ shuttingDown = true;
148
186
  socket.close();
149
187
  reject(new Error(`Disconnected: ${reason}`));
150
188
  return;
@@ -155,7 +193,13 @@ export async function runControlTunnel(options) {
155
193
  console.log("Server is shutting down; awaiting reconnect...");
156
194
  });
157
195
  process.on("SIGINT", () => {
158
- console.log("\nDisconnecting...");
196
+ // Second Ctrl+C: teardown is hanging (e.g. Appium closing a session
197
+ // mid-build) — force-quit instead of becoming un-interruptible.
198
+ if (shuttingDown) {
199
+ console.error("Force exit.");
200
+ process.exit(130);
201
+ }
202
+ console.log("\nDisconnecting... (press Ctrl+C again to force quit)");
159
203
  cleanup();
160
204
  });
161
205
  process.on("SIGTERM", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autifyhq/aximo-cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Autify Aximo Command Line Interface (CLI)",
5
5
  "keywords": [
6
6
  "autify",
@@ -38,6 +38,7 @@
38
38
  "dependencies": {
39
39
  "@modelcontextprotocol/sdk": "^1.29.0",
40
40
  "appium": "^3.5.2",
41
+ "applesign": "^5.0.1",
41
42
  "citty": "0.2.2",
42
43
  "socket.io-client": "^4.8.1",
43
44
  "undici": "^7.28.0",