@qubiqlabs/mobiflow 0.9.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/bin/mobiflow.js +38 -50
- package/package.json +1 -1
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
|
-
*
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
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}@
|
|
235
|
-
` Or: "${py}" -m pip install
|
|
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.9.
|
|
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>",
|