@anuraghq/mobius 0.1.0 → 0.1.2
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 +3 -2
- package/bin/mobiusagent-driver-darwin-arm64 +0 -0
- package/dist/cli.js +2 -1
- package/dist/execution/drivers/mobiusagent.d.ts +2 -0
- package/dist/execution/drivers/mobiusagent.js +31 -17
- package/dist/execution/observe.d.ts +2 -0
- package/dist/execution/observe.js +27 -9
- package/dist/execution/session.d.ts +32 -2
- package/dist/execution/session.js +104 -47
- package/dist/interface/http.js +2 -1
- package/dist/interface/mcp.js +42 -13
- package/dist/interface/mobius-tools.d.ts +6 -0
- package/dist/interface/mobius-tools.js +147 -61
- package/dist/observability/audit.d.ts +1 -0
- package/dist/observability/audit.js +14 -1
- package/dist/policy/policy.d.ts +4 -0
- package/dist/policy/policy.js +11 -3
- package/ios-agent/setup.sh +10 -4
- package/package.json +35 -6
package/dist/policy/policy.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ export interface PolicyContext {
|
|
|
49
49
|
targetApp?: string;
|
|
50
50
|
/** Tap given as x,y rather than an element id. */
|
|
51
51
|
coordinate?: boolean;
|
|
52
|
+
/** Back / home / recents: always allowed, even inside a blocked app, so the agent can leave. */
|
|
53
|
+
nav?: boolean;
|
|
54
|
+
/** A sideways scroll: the same finger motion as a "slide to pay" swipe. */
|
|
55
|
+
horizontal?: boolean;
|
|
52
56
|
/** Everything on the current screen, for context rules (checkout screens raise risk). */
|
|
53
57
|
screen?: Element[];
|
|
54
58
|
/** Every element under the tap point (an overlay may sit on top of the real button). */
|
package/dist/policy/policy.js
CHANGED
|
@@ -128,9 +128,13 @@ export class Policy {
|
|
|
128
128
|
const amount = decl?.amount ?? amountInvolved(t, ctx.screen);
|
|
129
129
|
let v = { ...base, action_class: cls, rule_id: base.rule_id, amount };
|
|
130
130
|
if (base.decision !== 'blocked' && ctx.mutating) {
|
|
131
|
+
// A ceiling rule ("allow payments up to ₹200") is checked against the largest amount on screen too: a label
|
|
132
|
+
// saying "Pay ₹1" must not unlock a ₹50,000 checkout.
|
|
133
|
+
const shown = (ctx.screen ?? []).flatMap(e => [...`${e.text ?? ''} ${e.label ?? ''}`.matchAll(/(?:₹|rs\.?|inr|\$|€|£)\s?([\d,]+(?:\.\d+)?)/gi)].map(m => Number(m[1].replace(/,/g, ''))));
|
|
134
|
+
const ceiling = amount === null ? null : Math.max(amount, ...shown);
|
|
131
135
|
const r = this.config.rules.find(r => (!r.app || appRe([r.app]).test(ctx.app ?? '')) && (!r.tool || r.tool.includes(ctx.tool))
|
|
132
136
|
&& (!r.action_class || r.action_class.includes(cls)) && (!r.target || new RegExp(r.target, 'i').test(words))
|
|
133
|
-
&& (r.max_amount === undefined || (
|
|
137
|
+
&& (r.max_amount === undefined || (ceiling !== null && ceiling <= r.max_amount)));
|
|
134
138
|
if (r)
|
|
135
139
|
v = { ...v, decision: r.effect, reason: `rule ${r.id}${r.description ? `: ${r.description}` : ''} (was: ${base.reason})`, rule_id: r.id };
|
|
136
140
|
}
|
|
@@ -142,6 +146,8 @@ export class Policy {
|
|
|
142
146
|
const out = (decision, reason, action_class = 'unknown', rule_id = `builtin.${decision}`) => ({ decision, reason, action_class, rule_id });
|
|
143
147
|
if (this.appBlocked(ctx.targetApp ?? null))
|
|
144
148
|
return out('blocked', `${ctx.targetApp} is in a blocked app category (finance/credentials)`);
|
|
149
|
+
if (ctx.nav)
|
|
150
|
+
return out('allow', 'navigation key');
|
|
145
151
|
if (this.appBlocked(ctx.app))
|
|
146
152
|
return out('blocked', `${ctx.app} is in a blocked app category (finance/credentials)`);
|
|
147
153
|
if (!ctx.mutating)
|
|
@@ -157,7 +163,8 @@ export class Policy {
|
|
|
157
163
|
if (ctx.secretField)
|
|
158
164
|
return out(this.config.secret_entry, 'typing into a password/PIN/OTP field', 'secret');
|
|
159
165
|
const t = ctx.target;
|
|
160
|
-
|
|
166
|
+
// camelCase ids split into words: btnPayNow, placeOrderButton.
|
|
167
|
+
const wordsOf = (e) => readable(`${e.text ?? ''} ${e.label ?? ''} ${e.resource_id ?? ''}`).replace(/([a-z])([A-Z])/g, '$1 $2').replace(/[_.]/g, ' ');
|
|
161
168
|
const words = t ? wordsOf(t) : '';
|
|
162
169
|
const gesture = ctx.tool === 'tap' || ctx.tool === 'swipe' || ctx.tool === 'scroll';
|
|
163
170
|
// Layer 0: the app itself declared this control sensitive. Trust the app over any label.
|
|
@@ -195,6 +202,7 @@ export class Policy {
|
|
|
195
202
|
device_setting_write: ['changing a system setting', 'account'],
|
|
196
203
|
set_permission: ['changing an app permission', 'permission'],
|
|
197
204
|
start_screen_recording: ['recording the screen (video cannot be masked)', 'secret'],
|
|
205
|
+
record_flow_start: ["recording the user's own taps (it can capture a PIN or unlock pattern)", 'secret'],
|
|
198
206
|
reboot_device: ['rebooting the device', 'unknown'],
|
|
199
207
|
};
|
|
200
208
|
const power = POWER[ctx.tool];
|
|
@@ -213,7 +221,7 @@ export class Policy {
|
|
|
213
221
|
}
|
|
214
222
|
// Layer 4: a swipe on a screen that offers "slide to …" commits. Sliders that only navigate ("Slide to switch" gender
|
|
215
223
|
// toggle on Slikk, live run) don't count; any other verb, or none we can read, still asks (fail closed).
|
|
216
|
-
if (ctx.tool === 'swipe' && ctx.screen?.some(e => { const m = /\b(?:slide|swipe) to\b\s*(\p{L}*)/iu.exec(wordsOf(e)); return !!m && !NAV_SLIDER.test(m[1]); })) {
|
|
224
|
+
if ((ctx.tool === 'swipe' || (ctx.tool === 'scroll' && ctx.horizontal)) && ctx.screen?.some(e => { const m = /\b(?:slide|swipe) to\b\s*(\p{L}*)/iu.exec(wordsOf(e)); return !!m && !NAV_SLIDER.test(m[1]); })) {
|
|
217
225
|
return out('approval_required', 'swipe on a screen with a "slide to …" control (swipe-to-pay/book)', 'payment');
|
|
218
226
|
}
|
|
219
227
|
if (ctx.submit)
|
package/ios-agent/setup.sh
CHANGED
|
@@ -15,12 +15,18 @@ cd "$DIR"
|
|
|
15
15
|
# Mobius fast touch (tap/swipe without WebDriverAgent's app lookup and settle wait). BSD-3 allows shipping a modified build.
|
|
16
16
|
PATCH="$HERE/wda-mobius-fast-touch.patch"
|
|
17
17
|
git apply --check "$PATCH" 2>/dev/null && git apply "$PATCH" && echo "applied Mobius fast-touch patch"
|
|
18
|
-
xcodebuild
|
|
18
|
+
# Run xcodebuild into a log and stop on failure: piping it through grep would hide a failed build, and a stale
|
|
19
|
+
# .xctestrun from an earlier build would then pass the final check.
|
|
20
|
+
build() { # build <log name> <xcodebuild args...>
|
|
21
|
+
log="$DIR/$1.log"; shift
|
|
22
|
+
if xcodebuild "$@" >"$log" 2>&1; then grep -E "\*\* " "$log" || true; else grep -E "error:|\*\* " "$log"; echo "xcodebuild failed (full log: $log)" >&2; exit 1; fi
|
|
23
|
+
}
|
|
24
|
+
build build-device build-for-testing -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner \
|
|
19
25
|
-destination 'generic/platform=iOS' DEVELOPMENT_TEAM="$TEAM" CODE_SIGN_STYLE=Automatic \
|
|
20
26
|
PRODUCT_BUNDLE_IDENTIFIER="${MOBIUS_WDA_BUNDLE:-dev.mobius.wda}" \
|
|
21
|
-
-allowProvisioningUpdates -allowProvisioningDeviceRegistration -derivedDataPath "$DIR/build"
|
|
27
|
+
-allowProvisioningUpdates -allowProvisioningDeviceRegistration -derivedDataPath "$DIR/build"
|
|
22
28
|
"$HERE/brand.sh"
|
|
23
29
|
# Simulators: an unsigned simulator build (no team needed).
|
|
24
|
-
|
|
25
|
-
-destination 'generic/platform=iOS Simulator' -derivedDataPath "$DIR/build-sim" CODE_SIGNING_ALLOWED=NO
|
|
30
|
+
build build-sim build-for-testing -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner \
|
|
31
|
+
-destination 'generic/platform=iOS Simulator' -derivedDataPath "$DIR/build-sim" CODE_SIGNING_ALLOWED=NO
|
|
26
32
|
ls "$DIR"/build/Build/Products/*.xctestrun >/dev/null && echo "WebDriverAgent ready in $DIR"
|
package/package.json
CHANGED
|
@@ -1,18 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anuraghq/mobius",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Let AI agents use a real phone, safely. MCP server + its own fast device driver for Android, iPhones and iOS simulators: 75 tools, approvals, policy, masking, signed audit, performance and accessibility reports.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
7
|
+
"bin": {
|
|
8
|
+
"mobius": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin",
|
|
13
|
+
"ios-agent",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.5"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"android",
|
|
24
|
+
"ios",
|
|
25
|
+
"iphone",
|
|
26
|
+
"simulator",
|
|
27
|
+
"emulator",
|
|
28
|
+
"mobile",
|
|
29
|
+
"ai-agent",
|
|
30
|
+
"agent-safety",
|
|
31
|
+
"claude",
|
|
32
|
+
"automation",
|
|
33
|
+
"testing",
|
|
34
|
+
"qa",
|
|
35
|
+
"performance",
|
|
36
|
+
"accessibility"
|
|
37
|
+
],
|
|
11
38
|
"scripts": {
|
|
12
39
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
13
40
|
"prepack": "npm run build && node scripts/prepack.mjs"
|
|
14
41
|
},
|
|
15
|
-
"publishConfig": {
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
16
45
|
"repository": "github:Anu-Code07/mobius",
|
|
17
46
|
"homepage": "https://github.com/Anu-Code07/mobius#readme",
|
|
18
47
|
"dependencies": {
|