@qubiqlabs/mobiflow 0.8.0 → 0.9.1

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 CHANGED
@@ -166,6 +166,7 @@ FOSS sample apps (Joplin, Bitwarden, Wikipedia): [docs/SAMPLE_APPS.md](docs/SAMP
166
166
  |---------|----------|
167
167
  | `mobiflow init` | Wizard / non-interactive scaffold (+ optional deps install) |
168
168
  | `mobiflow setup` | Detect / auto-install missing Maestro, JDK, pip packages |
169
+ | `mobiflow apps list` / `apps install wikipedia` | Download FOSS sample APKs + install on connected device |
169
170
  | `mobiflow run <case.txt>` | Explore → LLM YAML → device (+ heal) |
170
171
  | `mobiflow run cases/ [--tag smoke]` | Suite: all (or tagged) cases + aggregate JUnit/HTML |
171
172
  | `mobiflow suite [cases/]` | Same as `run` on a directory (defaults to `cases/`) |
package/bin/mobiflow.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * npm bin wrapper for the Python MobiFlow CLI.
4
- * Resolves: PATH mobiflow python -m mobiflow pip install → retry.
4
+ * Ensures the Python package matches this package.json version, then runs
5
+ * ``python -m mobiflow``.
5
6
  *
6
7
  * Windows note: never run ``python -c "…"`` through ``cmd.exe`` (shell:true) —
7
8
  * quoting breaks and a real 3.12 install looks like “no Python 3.11+”.
@@ -9,8 +10,6 @@
9
10
  "use strict";
10
11
 
11
12
  const { spawnSync } = require("child_process");
12
- const fs = require("fs");
13
- const path = require("path");
14
13
 
15
14
  const PKG = require("../package.json");
16
15
  const VERSION = PKG.version || "0.1.0";
@@ -145,6 +144,19 @@ function moduleAvailable(py) {
145
144
  return r.status === 0;
146
145
  }
147
146
 
147
+ /** Installed Python package version, or null if missing / unreadable. */
148
+ function installedVersion(py) {
149
+ const code =
150
+ "from importlib.metadata import version\n" +
151
+ "print(version('mobiflow'))";
152
+ const r = run(py, ["-c", code], {
153
+ stdio: ["ignore", "pipe", "pipe"],
154
+ });
155
+ if (r.status !== 0 || !r.stdout) return null;
156
+ const ver = r.stdout.toString().trim().split(/\r?\n/).pop() || "";
157
+ return ver || null;
158
+ }
159
+
148
160
  function pipInstall(py, spec) {
149
161
  console.error(`[mobiflow] Installing Python package: ${spec}`);
150
162
  const r = run(py, ["-m", "pip", "install", "--upgrade", spec], {
@@ -154,26 +166,34 @@ function pipInstall(py, spec) {
154
166
  }
155
167
 
156
168
  function ensureMobiflow(py) {
157
- if (moduleAvailable(py)) return true;
169
+ const current = installedVersion(py);
170
+ if (current === VERSION) return true;
171
+ if (current) {
172
+ console.error(
173
+ `[mobiflow] Python package is ${current}; need ${VERSION} — upgrading…`
174
+ );
175
+ }
158
176
 
177
+ // Prefer git tags: the npm wrapper version tracks the GitHub release.
178
+ // PyPI may lag or be empty for early releases.
159
179
  const specs = [
160
180
  process.env.MOBIFLOW_PIP_SPEC,
181
+ `git+${REPO}@v${VERSION}`,
161
182
  `mobiflow==${VERSION}`,
162
183
  "mobiflow",
163
- `git+${REPO}@v${VERSION}`,
164
184
  `git+${REPO}@main`,
165
185
  ].filter(Boolean);
166
186
 
167
187
  for (const spec of specs) {
168
- if (pipInstall(py, spec) && moduleAvailable(py)) return true;
188
+ if (!pipInstall(py, spec)) continue;
189
+ const got = installedVersion(py);
190
+ if (got === VERSION) return true;
191
+ if (moduleAvailable(py) && (spec.includes("@main") || process.env.MOBIFLOW_PIP_SPEC)) {
192
+ return true;
193
+ }
169
194
  }
170
- return false;
171
- }
172
-
173
- function pathLookup(binName) {
174
- // Use where.exe explicitly — ``where`` with shell can behave oddly.
175
- const cmd = IS_WIN ? "where.exe" : "which";
176
- return run(cmd, [binName], { stdio: ["ignore", "pipe", "ignore"] });
195
+ // Last resort: whatever is importable (may be older — caller still runs).
196
+ return moduleAvailable(py);
177
197
  }
178
198
 
179
199
  function main(argv) {
@@ -193,46 +213,14 @@ function main(argv) {
193
213
  process.exit(1);
194
214
  }
195
215
 
196
- // Prefer an already-installed console script on PATH (avoid recursion).
197
- const self = path.resolve(__filename);
198
- const onPath = pathLookup("mobiflow");
199
- if (onPath.status === 0 && onPath.stdout) {
200
- const candidates = onPath.stdout
201
- .toString()
202
- .split(/\r?\n/)
203
- .map((s) => s.trim())
204
- .filter(Boolean);
205
- for (const bin of candidates) {
206
- let resolved = bin;
207
- try {
208
- resolved = fs.realpathSync(bin);
209
- } catch {
210
- /* ignore */
211
- }
212
- if (resolved === self) continue;
213
- if (resolved.includes(`${path.sep}node_modules${path.sep}`)) continue;
214
- if (resolved.includes(`${path.sep}@qubiqlabs${path.sep}mobiflow${path.sep}`)) {
215
- continue;
216
- }
217
- if (resolved.includes(`${path.sep}mobiflow${path.sep}bin${path.sep}`)) {
218
- continue;
219
- }
220
- // Console scripts on Windows are often .cmd — shell helps those only.
221
- const r = spawnSync(bin, argv, {
222
- stdio: "inherit",
223
- windowsHide: true,
224
- env: process.env,
225
- shell: IS_WIN && /\.(cmd|bat)$/i.test(bin),
226
- });
227
- process.exit(r.status ?? 1);
228
- }
229
- }
230
-
216
+ // Always ensure the Python package matches this npm wrapper version first.
217
+ // Do not hand off to a stale console script on PATH (that hid new commands
218
+ // like ``apps`` after npm upgrades).
231
219
  if (!ensureMobiflow(py)) {
232
220
  console.error(
233
221
  "[mobiflow] Could not install the Python package.\n" +
234
- ` Try: "${py}" -m pip install "git+${REPO}@main"\n` +
235
- ` Or: "${py}" -m pip install mobiflow`
222
+ ` Try: "${py}" -m pip install "git+${REPO}@v${VERSION}"\n` +
223
+ ` Or: "${py}" -m pip install "git+${REPO}@main"`
236
224
  );
237
225
  process.exit(1);
238
226
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qubiqlabs/mobiflow",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "CLI: NL \u2192 Maestro mobile flows via LLM, run on device/emulator, self-heal. (npm wrapper for the Python package)",
5
5
  "license": "Apache-2.0",
6
6
  "author": "QubiQ Labs <labs@qubiq.ai>",