@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.
@@ -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). */
@@ -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 || (amount !== null && amount <= r.max_amount)));
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
- const wordsOf = (e) => readable(`${e.text ?? ''} ${e.label ?? ''} ${e.resource_id ?? ''}`).replace(/[_.]/g, ' ');
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)
@@ -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 build-for-testing -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner \
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" | grep -E "error:|\*\* " || true
27
+ -allowProvisioningUpdates -allowProvisioningDeviceRegistration -derivedDataPath "$DIR/build"
22
28
  "$HERE/brand.sh"
23
29
  # Simulators: an unsigned simulator build (no team needed).
24
- xcodebuild build-for-testing -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner \
25
- -destination 'generic/platform=iOS Simulator' -derivedDataPath "$DIR/build-sim" CODE_SIGNING_ALLOWED=NO | grep -E "error:|\*\* " || true
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.0",
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": { "mobius": "dist/cli.js" },
8
- "files": ["dist", "bin", "ios-agent", "README.md", "LICENSE"],
9
- "engines": { "node": ">=22.5" },
10
- "keywords": ["mcp", "model-context-protocol", "android", "ios", "iphone", "simulator", "emulator", "mobile", "ai-agent", "agent-safety", "claude", "automation", "testing", "qa", "performance", "accessibility"],
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": { "access": "public" },
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": {