@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.
- package/README.md +79 -64
- package/dist/client.js +16 -1
- package/dist/commands/mobile/connect-core.d.ts +2 -1
- package/dist/commands/mobile/connect-core.js +104 -6
- package/dist/commands/mobile/connect.js +3 -1
- package/dist/commands/mobile/doctor.d.ts +3 -0
- package/dist/commands/mobile/doctor.js +44 -14
- package/dist/commands/mobile/doctor.test.d.ts +1 -0
- package/dist/commands/mobile/doctor.test.js +14 -0
- package/dist/commands/mobile/install.d.ts +9 -2
- package/dist/commands/mobile/install.js +43 -12
- package/dist/commands/mobile/install.test.js +44 -6
- package/dist/commands/mobile/list.d.ts +2 -0
- package/dist/commands/mobile/list.js +17 -11
- package/dist/commands/mobile/unregister.js +11 -6
- package/dist/mobile-control/app-install.d.ts +18 -5
- package/dist/mobile-control/app-install.js +33 -6
- package/dist/mobile-control/app-install.test.js +42 -7
- package/dist/mobile-control/appium-controller.d.ts +8 -0
- package/dist/mobile-control/appium-controller.js +19 -4
- package/dist/mobile-control/bundle-id-map.d.ts +9 -0
- package/dist/mobile-control/bundle-id-map.js +27 -0
- package/dist/mobile-control/devices.d.ts +6 -3
- package/dist/mobile-control/devices.js +26 -4
- package/dist/mobile-control/env-flags.d.ts +5 -0
- package/dist/mobile-control/env-flags.js +10 -0
- package/dist/mobile-control/env-flags.test.d.ts +1 -0
- package/dist/mobile-control/env-flags.test.js +21 -0
- package/dist/mobile-control/ios-auto-resign.d.ts +45 -0
- package/dist/mobile-control/ios-auto-resign.js +108 -0
- package/dist/mobile-control/ios-auto-resign.test.d.ts +1 -0
- package/dist/mobile-control/ios-auto-resign.test.js +150 -0
- package/dist/mobile-control/ios-devicectl.d.ts +39 -0
- package/dist/mobile-control/ios-devicectl.js +87 -0
- package/dist/mobile-control/ios-devicectl.test.d.ts +1 -0
- package/dist/mobile-control/ios-devicectl.test.js +110 -0
- package/dist/mobile-control/ios-identity.d.ts +27 -0
- package/dist/mobile-control/ios-identity.js +49 -0
- package/dist/mobile-control/ios-identity.test.d.ts +1 -0
- package/dist/mobile-control/ios-identity.test.js +102 -0
- package/dist/mobile-control/ios-mint.d.ts +39 -0
- package/dist/mobile-control/ios-mint.js +78 -0
- package/dist/mobile-control/ios-mint.test.d.ts +1 -0
- package/dist/mobile-control/ios-mint.test.js +94 -0
- package/dist/mobile-control/ios-profiles.d.ts +46 -0
- package/dist/mobile-control/ios-profiles.js +106 -0
- package/dist/mobile-control/ios-profiles.test.d.ts +1 -0
- package/dist/mobile-control/ios-profiles.test.js +154 -0
- package/dist/mobile-control/ios-resign.d.ts +37 -0
- package/dist/mobile-control/ios-resign.js +101 -0
- package/dist/mobile-control/ios-resign.test.d.ts +1 -0
- package/dist/mobile-control/ios-resign.test.js +84 -0
- package/dist/mobile-control/ios-session-error.d.ts +10 -0
- package/dist/mobile-control/ios-session-error.js +34 -0
- package/dist/mobile-control/ios-session-error.test.d.ts +1 -0
- package/dist/mobile-control/ios-session-error.test.js +30 -0
- package/dist/mobile-control/ios-signing.d.ts +33 -5
- package/dist/mobile-control/ios-signing.js +56 -0
- package/dist/mobile-control/ios-signing.test.js +87 -0
- package/dist/mobile-control/ios-tunnel.d.ts +54 -0
- package/dist/mobile-control/ios-tunnel.js +130 -0
- package/dist/mobile-control/ios-tunnel.test.d.ts +1 -0
- package/dist/mobile-control/ios-tunnel.test.js +115 -0
- package/dist/mobile-control/ios-wda-cache.d.ts +39 -0
- package/dist/mobile-control/ios-wda-cache.js +157 -0
- package/dist/mobile-control/ios-wda-cache.test.d.ts +1 -0
- package/dist/mobile-control/ios-wda-cache.test.js +128 -0
- package/dist/mobile-control/ios-wda-diagnostics.d.ts +14 -0
- package/dist/mobile-control/ios-wda-diagnostics.js +138 -0
- package/dist/mobile-control/ios-wda-diagnostics.test.d.ts +1 -0
- package/dist/mobile-control/ios-wda-diagnostics.test.js +77 -0
- package/dist/mobile-control/ios-wda-preinstall.d.ts +64 -0
- package/dist/mobile-control/ios-wda-preinstall.js +162 -0
- package/dist/mobile-control/ios-wda-preinstall.test.d.ts +1 -0
- package/dist/mobile-control/ios-wda-preinstall.test.js +107 -0
- package/dist/mobile-control/mobile-errors.d.ts +12 -0
- package/dist/mobile-control/mobile-errors.js +91 -0
- package/dist/mobile-control/mobile-errors.test.d.ts +1 -0
- package/dist/mobile-control/mobile-errors.test.js +48 -0
- package/dist/mobile-control/tunnel.js +45 -1
- 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
|
-
|
|
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.
|
|
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",
|