qraftai-runner 0.1.0 → 0.3.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 (2) hide show
  1. package/dist/index.js +61 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ import { createRequire } from "node:module";
7
7
  import { writeFileSync, existsSync, statSync, mkdirSync } from "node:fs";
8
8
  import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
+ import { execFile as execFile2 } from "node:child_process";
11
+ import { promisify as promisify2 } from "node:util";
10
12
 
11
13
  // ../server/mobile/appiumManager.js
12
14
  import { spawn } from "node:child_process";
@@ -66,6 +68,7 @@ import { execFile } from "node:child_process";
66
68
  import { promisify } from "node:util";
67
69
  var pexec = promisify(execFile);
68
70
  var DEFAULT_NEW_COMMAND_TIMEOUT = parseInt(process.env.APPIUM_CMD_TIMEOUT || "120");
71
+ var STUDIO_CMD_TIMEOUT = parseInt(process.env.STUDIO_APPIUM_CMD_TIMEOUT || "3600");
69
72
  var MJPEG_PORT = parseInt(process.env.MOBILE_MJPEG_PORT || "9100");
70
73
  async function listIosSimulators() {
71
74
  try {
@@ -1070,6 +1073,7 @@ var AppiumDriver = class extends AutomationDriver {
1070
1073
  };
1071
1074
 
1072
1075
  // index.js
1076
+ var pexec2 = promisify2(execFile2);
1073
1077
  var VERSION = (() => {
1074
1078
  const req = createRequire(import.meta.url);
1075
1079
  for (const p of ["./package.json", "../package.json"]) {
@@ -1087,10 +1091,49 @@ function parseArgs(argv) {
1087
1091
  if (a === "--server" || a === "-s") out.server = argv[++i];
1088
1092
  else if (a === "--token" || a === "-t") out.token = argv[++i];
1089
1093
  else if (a === "--name" || a === "-n") out.name = argv[++i];
1094
+ else if (a === "--check" || a === "--doctor") out.check = true;
1095
+ else if (a === "--skip-check") out.skipCheck = true;
1090
1096
  else if (a === "--help" || a === "-h") out.help = true;
1091
1097
  }
1092
1098
  return out;
1093
1099
  }
1100
+ async function have(cmd, cmdArgs, re) {
1101
+ try {
1102
+ const { stdout, stderr } = await pexec2(cmd, cmdArgs, { timeout: 8e3 });
1103
+ return re ? re.test(`${stdout || ""}${stderr || ""}`) : true;
1104
+ } catch {
1105
+ return false;
1106
+ }
1107
+ }
1108
+ async function preflight() {
1109
+ const java = await have("java", ["-version"]);
1110
+ const adb = await have("adb", ["version"]);
1111
+ const appium = await have("appium", ["--version"]);
1112
+ const uia = appium ? await have("appium", ["driver", "list", "--installed"], /uiautomator2/i) : false;
1113
+ let device = false;
1114
+ try {
1115
+ device = (await listAdbDevices()).length > 0 || (await listAvds()).length > 0;
1116
+ } catch {
1117
+ }
1118
+ return [
1119
+ { label: "Java (JDK 17+)", ok: java, critical: false, hint: "install a JDK 17+ (e.g. brew install openjdk)" },
1120
+ { label: "adb (Android SDK platform-tools)", ok: adb, critical: true, hint: "install Android SDK; add platform-tools to PATH + set ANDROID_HOME" },
1121
+ { label: "Appium 2", ok: appium, critical: true, hint: "npm i -g appium" },
1122
+ { label: "uiautomator2 driver", ok: uia, critical: true, hint: "appium driver install uiautomator2" },
1123
+ { label: "A device (booted emulator / USB phone)", ok: device, critical: false, hint: "boot an emulator or plug in a phone with USB debugging" }
1124
+ ];
1125
+ }
1126
+ function reportPreflight(checks) {
1127
+ console.log("\nPrerequisite check:");
1128
+ let criticalMissing = false, deviceMissing = false;
1129
+ for (const c of checks) {
1130
+ console.log(` ${c.ok ? "\u2713" : "\u2717"} ${c.label}${c.ok ? "" : ` \u2192 ${c.hint}`}`);
1131
+ if (!c.ok && c.critical) criticalMissing = true;
1132
+ if (!c.ok && !c.critical && /device/i.test(c.label)) deviceMissing = true;
1133
+ }
1134
+ if (!criticalMissing && deviceMissing) console.log(" (no device yet \u2014 connect one and it'll appear on \u21BB Recheck in the app)");
1135
+ return { criticalMissing };
1136
+ }
1094
1137
  var args = parseArgs(process.argv.slice(2));
1095
1138
  if (args.help) {
1096
1139
  console.log(`QraftAI Runner v${VERSION}
@@ -1103,11 +1146,18 @@ Options:
1103
1146
  -s, --server wss://<your-qraftai-host>/runner-stream (or QRAFTAI_SERVER)
1104
1147
  -t, --token org-scoped runner token from the Mobile tab (or QRAFTAI_TOKEN)
1105
1148
  -n, --name a label shown in the app for this machine (or QRAFTAI_RUNNER_NAME)
1149
+ --check check prerequisites and exit (no connection; also --doctor)
1150
+ --skip-check connect without the startup prerequisite check
1106
1151
 
1107
1152
  Prerequisites (on this machine): JDK 17+, Android SDK (adb + emulator),
1108
1153
  Appium 2 + uiautomator2, and a device (AVD or USB phone with USB debugging).`);
1109
1154
  process.exit(0);
1110
1155
  }
1156
+ if (args.check) {
1157
+ const { criticalMissing } = reportPreflight(await preflight());
1158
+ console.log(criticalMissing ? "\n\u2716 Missing required tools above.\n" : "\n\u2713 Ready to run the runner.\n");
1159
+ process.exit(criticalMissing ? 1 : 0);
1160
+ }
1111
1161
  var SERVER = args.server || process.env.QRAFTAI_SERVER;
1112
1162
  var TOKEN = args.token || process.env.QRAFTAI_TOKEN;
1113
1163
  var NAME = args.name || process.env.QRAFTAI_RUNNER_NAME || "";
@@ -1130,11 +1180,12 @@ function sendMsg(type, payload = {}, reqId) {
1130
1180
  var ok = (reqId, result) => sendMsg("RESULT", { result }, reqId);
1131
1181
  var err = (reqId, message) => sendMsg("ERROR", { message }, reqId);
1132
1182
  var HANDLERS = {
1133
- async OPEN_SESSION({ sessionId, device, kind = "avd", platform = "android", app }) {
1183
+ async OPEN_SESSION({ sessionId, device, kind = "avd", platform = "android", app, studio = false }) {
1134
1184
  if (sessions.has(sessionId)) return { platform: sessions.get(sessionId).platform };
1135
1185
  const resolvedApp = await resolveApp(app);
1136
1186
  const { host, port } = await ensureAppium();
1137
- const capabilities = buildCapabilities(device, resolvedApp, kind, {}, platform);
1187
+ const overrides = studio ? { "appium:newCommandTimeout": STUDIO_CMD_TIMEOUT } : {};
1188
+ const capabilities = buildCapabilities(device, resolvedApp, kind, overrides, platform);
1138
1189
  const driver = new AppiumDriver({
1139
1190
  capabilities,
1140
1191
  connection: { protocol: "http", hostname: host, port, path: "/" }
@@ -1296,4 +1347,12 @@ async function shutdown() {
1296
1347
  }
1297
1348
  process.on("SIGINT", shutdown);
1298
1349
  process.on("SIGTERM", shutdown);
1350
+ if (!args.skipCheck) {
1351
+ const { criticalMissing } = reportPreflight(await preflight());
1352
+ if (criticalMissing) {
1353
+ console.error("\n\u2716 Fix the required tools above, then re-run. (bypass with --skip-check)\n");
1354
+ process.exit(1);
1355
+ }
1356
+ console.log("");
1357
+ }
1299
1358
  connect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qraftai-runner",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "QraftAI local device runner — bridge your own Android emulator/phone to a QraftAI workspace.",
5
5
  "type": "module",
6
6
  "bin": {