swipium 1.0.1 → 1.2.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 (132) hide show
  1. package/README.md +21 -6
  2. package/THREAT_MODEL.md +59 -0
  3. package/dist/appMap/codeIndex.js +37 -5
  4. package/dist/appMap/codeIndex.js.map +1 -1
  5. package/dist/appMap/diff.js +60 -0
  6. package/dist/appMap/diff.js.map +1 -0
  7. package/dist/appMap/query.js +22 -4
  8. package/dist/appMap/query.js.map +1 -1
  9. package/dist/automation/capabilities.js +167 -0
  10. package/dist/automation/capabilities.js.map +1 -0
  11. package/dist/automation/gestures.js +94 -0
  12. package/dist/automation/gestures.js.map +1 -0
  13. package/dist/automation/maestroIr.js +282 -0
  14. package/dist/automation/maestroIr.js.map +1 -0
  15. package/dist/automation/plan.js +268 -0
  16. package/dist/automation/plan.js.map +1 -0
  17. package/dist/automation/report.js +83 -0
  18. package/dist/automation/report.js.map +1 -0
  19. package/dist/automation/selectors.js +201 -0
  20. package/dist/automation/selectors.js.map +1 -0
  21. package/dist/automation/types.js +13 -0
  22. package/dist/automation/types.js.map +1 -0
  23. package/dist/automation/waits.js +91 -0
  24. package/dist/automation/waits.js.map +1 -0
  25. package/dist/automation/webview.js +81 -0
  26. package/dist/automation/webview.js.map +1 -0
  27. package/dist/drivers/WdaDriver.js +18 -5
  28. package/dist/drivers/WdaDriver.js.map +1 -1
  29. package/dist/explore/candidates.js +17 -0
  30. package/dist/explore/candidates.js.map +1 -1
  31. package/dist/explore/runner.js +29 -2
  32. package/dist/explore/runner.js.map +1 -1
  33. package/dist/flows/repair.js +363 -0
  34. package/dist/flows/repair.js.map +1 -0
  35. package/dist/interop/maestro.js +151 -0
  36. package/dist/interop/maestro.js.map +1 -0
  37. package/dist/lib/wda.js +17 -0
  38. package/dist/lib/wda.js.map +1 -1
  39. package/dist/mobileAudit/checks.js +251 -0
  40. package/dist/mobileAudit/checks.js.map +1 -0
  41. package/dist/mobileAudit/evidence.js +49 -0
  42. package/dist/mobileAudit/evidence.js.map +1 -0
  43. package/dist/mobileAudit/profiles.js +73 -0
  44. package/dist/mobileAudit/profiles.js.map +1 -0
  45. package/dist/mobileAudit/results.js +41 -0
  46. package/dist/mobileAudit/results.js.map +1 -0
  47. package/dist/mobileAudit/runner.js +229 -0
  48. package/dist/mobileAudit/runner.js.map +1 -0
  49. package/dist/oracle/failures.js +1 -0
  50. package/dist/oracle/failures.js.map +1 -1
  51. package/dist/oracle/locator.js +177 -0
  52. package/dist/oracle/locator.js.map +1 -0
  53. package/dist/orchestration/envelope.js.map +1 -1
  54. package/dist/prompts/index.js +10 -0
  55. package/dist/prompts/index.js.map +1 -1
  56. package/dist/report/export.js +3 -1
  57. package/dist/report/export.js.map +1 -1
  58. package/dist/server.js +49 -2
  59. package/dist/server.js.map +1 -1
  60. package/dist/services/build.js +15 -2
  61. package/dist/services/build.js.map +1 -1
  62. package/dist/services/prepareIos.js +34 -4
  63. package/dist/services/prepareIos.js.map +1 -1
  64. package/dist/services/report.js +26 -9
  65. package/dist/services/report.js.map +1 -1
  66. package/dist/session/store.js.map +1 -1
  67. package/dist/suite/pom.js +24 -9
  68. package/dist/suite/pom.js.map +1 -1
  69. package/dist/testSuite/exporter.js +70 -0
  70. package/dist/testSuite/exporter.js.map +1 -0
  71. package/dist/testSuite/lint.js +53 -0
  72. package/dist/testSuite/lint.js.map +1 -0
  73. package/dist/tools/act.js +23 -2
  74. package/dist/tools/act.js.map +1 -1
  75. package/dist/tools/appControl.js +176 -0
  76. package/dist/tools/appControl.js.map +1 -0
  77. package/dist/tools/capabilities.js +75 -4
  78. package/dist/tools/capabilities.js.map +1 -1
  79. package/dist/tools/device.js +151 -0
  80. package/dist/tools/device.js.map +1 -0
  81. package/dist/tools/flow.js +67 -1
  82. package/dist/tools/flow.js.map +1 -1
  83. package/dist/tools/flowRepair.js +66 -0
  84. package/dist/tools/flowRepair.js.map +1 -0
  85. package/dist/tools/history.js +56 -0
  86. package/dist/tools/history.js.map +1 -0
  87. package/dist/tools/idling.js +104 -0
  88. package/dist/tools/idling.js.map +1 -0
  89. package/dist/tools/inputCapabilities.js +30 -0
  90. package/dist/tools/inputCapabilities.js.map +1 -0
  91. package/dist/tools/issues.js +218 -0
  92. package/dist/tools/issues.js.map +1 -0
  93. package/dist/tools/jobs.js +12 -1
  94. package/dist/tools/jobs.js.map +1 -1
  95. package/dist/tools/locator.js +57 -0
  96. package/dist/tools/locator.js.map +1 -0
  97. package/dist/tools/maestro.js +78 -0
  98. package/dist/tools/maestro.js.map +1 -0
  99. package/dist/tools/metro.js +260 -0
  100. package/dist/tools/metro.js.map +1 -0
  101. package/dist/tools/mobileAudit.js +115 -0
  102. package/dist/tools/mobileAudit.js.map +1 -0
  103. package/dist/tools/network.js +97 -5
  104. package/dist/tools/network.js.map +1 -1
  105. package/dist/tools/permissions.js +149 -0
  106. package/dist/tools/permissions.js.map +1 -0
  107. package/dist/tools/screenInfo.js +70 -0
  108. package/dist/tools/screenInfo.js.map +1 -0
  109. package/dist/tools/screenRecord.js +215 -6
  110. package/dist/tools/screenRecord.js.map +1 -1
  111. package/dist/tools/seed.js +101 -0
  112. package/dist/tools/seed.js.map +1 -0
  113. package/dist/tools/state.js +83 -0
  114. package/dist/tools/state.js.map +1 -1
  115. package/dist/tools/suite.js +49 -1
  116. package/dist/tools/suite.js.map +1 -1
  117. package/dist/tools/testSuite.js +233 -0
  118. package/dist/tools/testSuite.js.map +1 -0
  119. package/dist/tools/testThis.js +11 -3
  120. package/dist/tools/testThis.js.map +1 -1
  121. package/dist/tools/visual.js +139 -0
  122. package/dist/tools/visual.js.map +1 -0
  123. package/dist/tools/visualText.js +61 -0
  124. package/dist/tools/visualText.js.map +1 -0
  125. package/dist/tools/wait.js +58 -0
  126. package/dist/tools/wait.js.map +1 -0
  127. package/dist/version.js +54 -3
  128. package/dist/version.js.map +1 -1
  129. package/docs/README.md +4 -4
  130. package/docs/mcp-server.md +3 -3
  131. package/docs/tools.md +111 -2
  132. package/package.json +3 -2
@@ -0,0 +1,260 @@
1
+ // qa_metro — own the debug React Native / Expo Metro flow (review §4.2 / Rec 5).
2
+ // A debug RN/Expo APK needs a Metro dev server on :8081 + `adb reverse` to fetch its JS
3
+ // bundle. `status` reports whether that's ready; `start` (consent-gated) sets up the
4
+ // reverse and launches Metro, logging to a session artifact.
5
+ import { z } from 'zod';
6
+ import { openSync, closeSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { spawn, spawnSync } from 'node:child_process';
9
+ import { qaOk, qaError } from '../lib/result.js';
10
+ import { requireConsent, consumeConsent } from '../consent/consent.js';
11
+ import { run } from '../lib/spawn.js';
12
+ import { detectFramework } from '../context/detect.js';
13
+ import { METRO_PORT, metroReadiness } from '../lib/metroState.js';
14
+ import { parseSnapshot } from '../snapshot/parse.js';
15
+ import { detectRedBox } from '../snapshot/overlays.js';
16
+ import { getDriver, resolveDevice, bindDevice } from '../session/attach.js';
17
+ /** True if `pid` is a live process whose command looks like a Metro/Expo/RN bundler. Guards
18
+ * against killing a recycled PID — e.g. a `metroPid` persisted before a machine or server
19
+ * restart that now belongs to an unrelated process. Best-effort (POSIX `ps`); on any doubt
20
+ * we report "not ours" so we never signal a stranger. */
21
+ function metroProcessLooksAlive(pid) {
22
+ try {
23
+ const out = spawnSync('ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8' });
24
+ if (out.status !== 0 || !out.stdout)
25
+ return false;
26
+ return /metro|expo|react-native|node/i.test(out.stdout);
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ /** Kill a Metro process that was started detached (so it leads its own process group). We signal
33
+ * the whole GROUP (negative pid) so the real `node` bundler holding :8081 dies, not just the
34
+ * `npx` launcher that spawned it. Falls back to signalling the pid directly if it is not a group
35
+ * leader. Returns true if a signal was delivered. */
36
+ function killMetroGroup(pid) {
37
+ try {
38
+ process.kill(-pid, 'SIGTERM');
39
+ return true;
40
+ }
41
+ catch {
42
+ /* not a group leader, or group already gone — fall through */
43
+ }
44
+ try {
45
+ process.kill(pid, 'SIGTERM');
46
+ return true;
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ }
52
+ /** Stop every Metro server Swipium started, on server shutdown / client disconnect, so a
53
+ * budget-stop or crash mid-run doesn't leave a `node` bundler holding :8081. Idempotent. */
54
+ export async function stopAllMetro(sessions) {
55
+ for (const session of sessions.list()) {
56
+ const pid = session.metroPid;
57
+ if (!pid)
58
+ continue;
59
+ if (metroProcessLooksAlive(pid))
60
+ killMetroGroup(pid);
61
+ session.metroPid = undefined;
62
+ try {
63
+ sessions.persist(session);
64
+ }
65
+ catch {
66
+ /* ignore */
67
+ }
68
+ }
69
+ }
70
+ function metroCommand(framework) {
71
+ // Expo dev-client vs bare RN Metro.
72
+ if (framework === 'expo')
73
+ return { cmd: 'npx', args: ['expo', 'start', '--dev-client', '--port', String(METRO_PORT)] };
74
+ return { cmd: 'npx', args: ['react-native', 'start', '--port', String(METRO_PORT)] };
75
+ }
76
+ export function registerMetro(server, sessions) {
77
+ server.registerTool('qa_metro', {
78
+ title: 'Metro dev server (debug RN/Expo)',
79
+ description: 'For debug React Native / Expo builds that need a Metro bundler on :8081. action="status" reports Metro/reverse/serving state; action="diagnose" adds RedBox detection + logcat evidence + a recovery roadmap (incl. whether reinstall/rebuild is required); action="start" (consent-gated) runs `adb reverse tcp:8081 tcp:8081` + launches Metro, logging to a session artifact + tracking its PID; action="stop" kills it + removes the reverse. After start, wait a few seconds then relaunch with qa_prepare_target.',
80
+ inputSchema: {
81
+ sessionId: z.string(),
82
+ action: z.enum(['status', 'diagnose', 'start', 'stop']),
83
+ consentId: z.string().optional(),
84
+ approve: z.boolean().optional(),
85
+ },
86
+ }, async ({ sessionId, action, consentId, approve }) => {
87
+ const session = sessions.get(sessionId);
88
+ if (!session)
89
+ return qaError({ what: `Unknown sessionId ${sessionId}`, changedState: false, retrySafe: true, nextSteps: ['Call qa_start_session first.'] });
90
+ // Centralized device resolution (P0.1/P0.3): use the session device, else the single
91
+ // online one (bind it), else ask / guide — never a circular "no device" dead end.
92
+ const dev = await resolveDevice(session);
93
+ if (dev.needSelection && action !== 'status') {
94
+ return qaError({ what: 'Multiple devices online — choose one', changedState: false, retrySafe: true, nextSteps: [`Re-run qa_prepare_target with device="<serial>". Online: ${dev.available.join(', ')}`] });
95
+ }
96
+ const serial = dev.effective;
97
+ if (serial && !session.device)
98
+ bindDevice(session, serial); // self-bind single online device
99
+ const fw = detectFramework(session.root);
100
+ const rd = await metroReadiness(serial);
101
+ const listening = rd.listening;
102
+ const reverseSet = rd.reverseSet;
103
+ if (action === 'status') {
104
+ const deviceHint = serial ? '' : '\n⚠ No device bound — adb reverse needs one. Call qa_prepare_target first (it binds the device), then qa_metro.';
105
+ return qaOk({ framework: fw, metroListening: listening, reverseSet, serving: rd.serving, ready: rd.ready, port: METRO_PORT, sessionDevice: dev.sessionDevice ?? null, availableDevices: dev.available, device: serial ?? null, metroPid: session.metroPid ?? null, needsDevice: !serial }, `framework=${fw} metro:${METRO_PORT}=${listening ? 'listening' : 'down'} serving=${rd.serving} adb-reverse=${serial ? (reverseSet ? 'set' : 'unset') : 'n/a (no device)'} device=${serial ?? 'none'} online=[${dev.available.join(',')}]${session.metroPid ? ` pid=${session.metroPid}` : ''}` +
106
+ (rd.ready ? '\nReady — debug bundle can load.' : deviceHint || '\nNot fully ready — run qa_metro action="start" (or qa_metro diagnose).'));
107
+ }
108
+ if (action === 'diagnose') {
109
+ const { driver } = await getDriver(session);
110
+ let redbox = { present: false, unableToLoadScript: false };
111
+ let logUri;
112
+ if (driver) {
113
+ try {
114
+ const parsed = parseSnapshot(await driver.dumpXml());
115
+ redbox = detectRedBox(parsed.allNodes);
116
+ }
117
+ catch {
118
+ /* dump may fail on redbox/animation */
119
+ }
120
+ const log = await driver.logcat(200, 'ReactNative|ReactNativeJS|Metro|bundle|Unable to load|loadJSBundle|DevServer');
121
+ if (log)
122
+ logUri = sessions.saveArtifact(session, 'logcat', `logcat-${Date.now()}.txt`, log, 'text/plain', 'metro diagnose');
123
+ }
124
+ const recovery = [];
125
+ let requiresReinstall = false;
126
+ if (!serial)
127
+ recovery.push(dev.needSelection ? `Multiple devices online — re-run qa_prepare_target device="<serial>". Online: ${dev.available.join(', ')}` : 'No device online — boot one via qa_prepare_target (it boots + sets reverse), then retry.');
128
+ if (!listening)
129
+ recovery.push('Start Metro: qa_metro action="start".');
130
+ if (serial && listening && !reverseSet)
131
+ recovery.push('Set reverse: qa_metro action="start" (sets adb reverse).');
132
+ if (listening && !rd.serving)
133
+ recovery.push('Metro is up but not serving the bundle yet — wait ~10s for the first transform, then retry.');
134
+ if (redbox.unableToLoadScript) {
135
+ requiresReinstall = true;
136
+ recovery.push('RedBox is "Unable to load script": the installed build cannot fetch the JS bundle. If Metro+reverse are ready and it still fails, the APK is bundle-less/asset-only — RECOVERY REQUIRES REINSTALL/REBUILD of a working debug or release APK (a data wipe alone will not fix it).');
137
+ }
138
+ else if (redbox.present) {
139
+ recovery.push('RedBox present (not load-script): tap RELOAD via qa_act, or fix the JS error shown.');
140
+ }
141
+ if (rd.ready && !redbox.present)
142
+ recovery.push('Metro ready and no RedBox — the bundle should load.');
143
+ return qaOk({ framework: fw, ...rd, sessionDevice: dev.sessionDevice ?? null, availableDevices: dev.available, device: serial ?? null, redBox: redbox, requiresReinstall, logcatUri: logUri ?? null, recovery }, `framework=${fw} | metro listening=${rd.listening} serving=${rd.serving} reverse=${reverseSet} ready=${rd.ready}\n` +
144
+ `redbox=${redbox.present ? (redbox.unableToLoadScript ? 'UNABLE-TO-LOAD-SCRIPT' : 'present') : 'none'}${requiresReinstall ? ' ⚠ requires reinstall/rebuild' : ''}\n` +
145
+ `recovery:\n - ${recovery.join('\n - ') || '(none — looks healthy)'}` +
146
+ (logUri ? `\nlogcat evidence: ${logUri}` : ''));
147
+ }
148
+ if (action === 'stop') {
149
+ let killed = false;
150
+ if (session.metroPid) {
151
+ // Only signal a PID we can still confirm is a Metro/node bundler (guards against a
152
+ // persisted PID that was recycled across a restart), and kill the whole process group
153
+ // so the real bundler dies, not just the npx launcher.
154
+ if (metroProcessLooksAlive(session.metroPid)) {
155
+ killed = killMetroGroup(session.metroPid);
156
+ }
157
+ }
158
+ if (serial) {
159
+ try {
160
+ await run('adb', ['-s', serial, 'reverse', '--remove', `tcp:${METRO_PORT}`], { timeoutMs: 5000 });
161
+ }
162
+ catch {
163
+ /* ignore */
164
+ }
165
+ }
166
+ const had = session.metroPid;
167
+ session.metroPid = undefined;
168
+ sessions.persist(session);
169
+ sessions.recordMutation(session, {
170
+ tool: 'qa_metro',
171
+ action: 'stop_metro',
172
+ risk: 'low',
173
+ target: { device: serial ?? null, port: METRO_PORT, pid: had ?? null, killed, reverseRemoved: !!serial },
174
+ consent: { required: false, approved: true },
175
+ status: 'restored',
176
+ });
177
+ return qaOk({ stopped: killed, pid: had ?? null, reverseRemoved: !!serial }, had ? `Stopped Metro pid ${had}${killed ? '' : ' (was already gone)'}; removed adb reverse.` : 'No Swipium-started Metro to stop (it may have been started externally).');
178
+ }
179
+ // action === 'start' — `serial` is the single online device (auto-bound) or a session
180
+ // device. If none online, guide to prepare_target to BOOT (terminal, non-circular).
181
+ if (!serial) {
182
+ return qaError({
183
+ what: dev.needSelection ? 'Multiple devices online — choose one' : 'No device online — Metro reverse needs a running device',
184
+ changedState: false,
185
+ retrySafe: true,
186
+ nextSteps: dev.needSelection
187
+ ? [`Re-run qa_prepare_target device="<serial>". Online: ${dev.available.join(', ')}`]
188
+ : ['Boot a device first: qa_prepare_target { bindOnly:true } (boots an AVD + sets reverse), then qa_metro start.'],
189
+ });
190
+ }
191
+ if (listening && reverseSet) {
192
+ return qaOk({ framework: fw, metroListening: true, reverseSet: true, alreadyRunning: true }, 'Metro already listening and adb reverse already set — nothing to do.');
193
+ }
194
+ const { cmd, args } = metroCommand(fw);
195
+ const cmdStr = `adb -s ${serial} reverse tcp:${METRO_PORT} tcp:${METRO_PORT} && (cd ${session.root} && ${cmd} ${args.join(' ')})`;
196
+ const gate = consumeConsent(consentId, approve, { action: 'start_metro', affects: { port: METRO_PORT, framework: fw } });
197
+ if (!gate.approved) {
198
+ sessions.recordMutation(session, {
199
+ tool: 'qa_metro',
200
+ action: 'start_metro',
201
+ risk: 'low',
202
+ target: { device: serial, port: METRO_PORT, framework: fw },
203
+ consent: { required: true, approved: false },
204
+ status: 'requested',
205
+ });
206
+ return requireConsent({
207
+ action: 'start_metro',
208
+ risk: 'low',
209
+ exactCommand: cmdStr,
210
+ affects: { port: METRO_PORT, framework: fw },
211
+ explain: `This looks like a debug ${fw} build needing Metro on :${METRO_PORT}. Set adb reverse and start Metro?`,
212
+ });
213
+ }
214
+ sessions.recordMutation(session, {
215
+ tool: 'qa_metro',
216
+ action: 'start_metro',
217
+ risk: 'low',
218
+ target: { device: serial, port: METRO_PORT, framework: fw },
219
+ consent: { required: true, consentId, approved: true },
220
+ status: 'approved',
221
+ });
222
+ // adb reverse, then spawn Metro detached with output to a session log artifact.
223
+ try {
224
+ await run('adb', ['-s', serial, 'reverse', `tcp:${METRO_PORT}`, `tcp:${METRO_PORT}`], { timeoutMs: 8000, rejectOnNonZero: true });
225
+ }
226
+ catch (e) {
227
+ sessions.recordMutation(session, {
228
+ tool: 'qa_metro',
229
+ action: 'start_metro',
230
+ risk: 'low',
231
+ target: { device: serial, port: METRO_PORT, framework: fw },
232
+ consent: { required: true, consentId, approved: true },
233
+ status: 'blocked',
234
+ detail: String(e),
235
+ });
236
+ return qaError({ what: `adb reverse failed: ${String(e)}`, commandAttempted: `adb -s ${serial} reverse tcp:${METRO_PORT} tcp:${METRO_PORT}`, changedState: true, retrySafe: true, nextSteps: ['Check the device is online.'] });
237
+ }
238
+ // Register the artifact (creates the empty file) BEFORE opening the append fd, so we
239
+ // don't truncate a live log. saveArtifact writes to <dir>/metro/metro.log.
240
+ const logUri = sessions.saveArtifact(session, 'metro', 'metro.log', '', 'text/plain');
241
+ const logPath = join(session.dir, 'metro', 'metro.log');
242
+ const fd = openSync(logPath, 'a');
243
+ const child = spawn(cmd, args, { cwd: session.root, detached: true, stdio: ['ignore', fd, fd] });
244
+ child.unref();
245
+ closeSync(fd); // child inherited the fd; the parent must not keep it open (review #2)
246
+ session.metroPid = child.pid;
247
+ sessions.persist(session);
248
+ sessions.recordMutation(session, {
249
+ tool: 'qa_metro',
250
+ action: 'start_metro',
251
+ risk: 'low',
252
+ target: { device: serial, port: METRO_PORT, framework: fw, pid: child.pid ?? null },
253
+ consent: { required: true, consentId, approved: true },
254
+ status: 'executed',
255
+ ledgerUri: logUri,
256
+ });
257
+ return qaOk({ framework: fw, started: true, port: METRO_PORT, reverseSet: true, pid: child.pid, logUri, logPath }, `Started Metro (${fw}) on :${METRO_PORT} + adb reverse set. Log → ${logUri}\nWait ~5–10s for "Metro waiting", then relaunch the app with qa_prepare_target.`);
258
+ });
259
+ }
260
+ //# sourceMappingURL=metro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metro.js","sourceRoot":"","sources":["../../src/tools/metro.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ // SWIPIUM Mobile QA Toolkit MCP tool (SWIPIUM-REQ-07). qa_mobile_audit turns a named profile
2
+ // (smoke | account_cycle | store_compliance | resilience | release_gate) into an ordered, classified
3
+ // checklist plus the account-cycle safety contract the agent executes with the existing driver tools.
4
+ // It also surfaces the issue-ledger recurrence state so a release gate sees regressions. Thin wrapper
5
+ // over src/mobileAudit/* and src/issues/*.
6
+ import { z } from 'zod';
7
+ import { qaOk, qaError } from '../lib/result.js';
8
+ import { resolveProjectRoot } from '../context/projectRoot.js';
9
+ import { getDriver } from '../session/attach.js';
10
+ import { accountCycleSafety, checksForProfile, ALL_PROFILES } from '../mobileAudit/profiles.js';
11
+ import { runMobileAudit } from '../mobileAudit/runner.js';
12
+ import { auditRunToMarkdown } from '../mobileAudit/results.js';
13
+ import { generateSessionReport } from '../services/report.js';
14
+ import { queryIssues } from '../issues/index.js';
15
+ import { issuesResourceUri } from '../issues/store.js';
16
+ import { resolveSourceRevision } from '../issues/sourceRevision.js';
17
+ import { loadPolicy } from '../issues/store.js';
18
+ function nowIso() {
19
+ return new Date().toISOString();
20
+ }
21
+ async function rootFor(server, sessions, args) {
22
+ if (args.sessionId) {
23
+ const s = sessions.get(args.sessionId);
24
+ if (!s)
25
+ return { hint: `Unknown sessionId ${args.sessionId}` };
26
+ return { root: s.root };
27
+ }
28
+ const resolved = await resolveProjectRoot(server, args.projectRoot);
29
+ if (!resolved.root)
30
+ return { hint: resolved.hint };
31
+ return { root: resolved.root };
32
+ }
33
+ export function registerMobileAudit(server, sessions) {
34
+ server.registerTool('qa_mobile_audit', {
35
+ title: 'Mobile QA release audit',
36
+ description: 'Run a named mobile-QA audit profile in one of two modes. mode:"plan" (default) returns the ordered checklist (each with expected behavior + report classification), the account-cycle safety contract, and an `executor` handoff — it does NOT drive the device. mode:"execute" REQUIRES a sessionId with an attached device and DRIVES the audit: it runs each check, records issue-ledger entries with evidence for fail/blocked checks, returns per-check MobileAuditCheckResults + a release-impact decision, and (best-effort) a report URI. Profiles: smoke (launch/health/nav), account_cycle (create/logout/login-again/forgot-password on a DISPOSABLE generated account — needs allowGeneratedData), store_compliance (privacy/terms/account-deletion/subscription/paywall), resilience (offline/restore/relaunch/rotation — network is always restored), release_gate (all profiles + locator readiness + issue-ledger recurrence). Safety: logout is permitted only inside the controlled account-cycle workflow on a disposable account; delete/pay/send stay refused; a real account is never deleted; OTP/MFA returns needs_input; a check never reports pass without observed evidence.',
37
+ inputSchema: {
38
+ projectRoot: z.string().optional(),
39
+ sessionId: z.string().optional(),
40
+ profile: z.enum(ALL_PROFILES).describe('Audit profile to run.'),
41
+ mode: z.enum(['plan', 'execute']).optional().describe('plan (default): return the checklist + safety contract without driving the device. execute: drive the audit against the attached device, record issues + evidence, and return per-check results + release impact.'),
42
+ allowGeneratedData: z.boolean().optional().describe('Permit safe generated disposable-account data (test/staging).'),
43
+ allowTestAccountDeletion: z.boolean().optional().describe('Permit destructive cleanup of disposable test accounts only.'),
44
+ offlineMode: z.boolean().optional().describe('Hint that resilience checks should drive offline state.'),
45
+ sourceRevision: z.object({ commit: z.string().optional(), buildVersion: z.string().optional(), branch: z.string().optional() }).optional(),
46
+ targetApp: z.string().optional(),
47
+ waitForCompletion: z.boolean().optional().describe('Reserved; execute currently always runs to completion and returns the full result.'),
48
+ },
49
+ }, async ({ projectRoot, sessionId, profile, mode, allowGeneratedData, allowTestAccountDeletion, offlineMode, sourceRevision, targetApp }) => {
50
+ const { root, hint } = await rootFor(server, sessions, { projectRoot, sessionId });
51
+ if (!root)
52
+ return qaError({ what: 'Could not resolve a project root', changedState: false, retrySafe: true, nextSteps: ['Pass projectRoot or sessionId.'], clientHint: hint });
53
+ const prof = profile;
54
+ const policyForRev = loadPolicy(root);
55
+ const revisionResolved = resolveSourceRevision({
56
+ explicit: sourceRevision ? { provider: 'explicit', ...sourceRevision } : undefined,
57
+ env: process.env,
58
+ allowGitMetadataRead: policyForRev.allowGitMetadataRead,
59
+ root,
60
+ });
61
+ // ---- EXECUTE: drive the audit against the attached device ----
62
+ if (mode === 'execute') {
63
+ const session = sessionId ? sessions.get(sessionId) : undefined;
64
+ if (!session)
65
+ return qaError({ what: 'execute mode needs a sessionId with a prepared device', changedState: false, retrySafe: true, nextSteps: ['Call qa_start_session + qa_prepare_target (or qa_test_this), then qa_mobile_audit { mode:"execute" }.'] });
66
+ const { driver } = await getDriver(session);
67
+ if (!driver)
68
+ return qaError({ what: 'No device attached to this session', changedState: false, retrySafe: true, nextSteps: ['Prepare a device first (qa_test_this / qa_prepare_target), then re-run.'] });
69
+ try {
70
+ const run = await runMobileAudit(sessions, session, driver, { profile: prof, allowGeneratedData, allowTestAccountDeletion, offlineMode, sourceRevision: revisionResolved, now: nowIso() });
71
+ let reportUri;
72
+ try {
73
+ const r = await generateSessionReport(sessions, session, {});
74
+ reportUri = r.reportUri;
75
+ run.reportUri = reportUri;
76
+ }
77
+ catch {
78
+ /* report is best-effort */
79
+ }
80
+ const passed = run.checks.filter((c) => c.status === 'pass').length;
81
+ return qaOk({ ...run, markdown: auditRunToMarkdown(run), resourceUri: issuesResourceUri(root) }, `🔍 mobile audit ${prof}: ${run.state}, release=${run.releaseImpact} — ${passed}/${run.checks.length} pass, ${run.issueIds.length} issue(s)${run.recurrenceWarnings.length ? `, ${run.recurrenceWarnings.length} recurrence` : ''}`);
82
+ }
83
+ catch (e) {
84
+ return qaError({ what: `Mobile audit failed: ${String(e.message ?? e)}`, changedState: true, retrySafe: true, nextSteps: ['Check device health (qa_check_health) and retry.'] });
85
+ }
86
+ }
87
+ // ---- PLAN (default) ----
88
+ const checks = checksForProfile(prof);
89
+ const safety = accountCycleSafety(prof, { allowGeneratedData, allowTestAccountDeletion, offlineMode });
90
+ // qa_mobile_audit PLANS the audit; the controlled account-cycle steps (logout permitted only on
91
+ // a disposable account) are EXECUTED by qa_explore with accountCycle:true + allowGeneratedData.
92
+ const executor = safety.allowsLogout
93
+ ? { tool: 'qa_explore', args: { accountCycle: true, allowGeneratedData: allowGeneratedData === true }, note: 'Runs the account-cycle workflow where logout is permitted on a disposable account; delete/pay/send stay refused.' }
94
+ : undefined;
95
+ // Account-cycle / release-gate without generated-data permission can't safely create accounts.
96
+ const blockedChecks = checks.filter((c) => c.requiresGeneratedData && !allowGeneratedData).map((c) => c.id);
97
+ // Surface issue-ledger recurrence so a release gate sees regressions.
98
+ const issues = queryIssues(root, nowIso(), { includeSuppressed: false });
99
+ const recurrence = issues.recurrenceCandidates.map((r) => ({ issueId: r.issueId, summary: r.summary, message: r.lastRecurrenceMessage }));
100
+ const summary = `audit profile=${prof}: ${checks.length} check(s)${blockedChecks.length ? `, ${blockedChecks.length} need allowGeneratedData` : ''}${recurrence.length ? `, ${recurrence.length} recurrence warning(s)` : ''}`;
101
+ return qaOk({
102
+ mode: 'plan',
103
+ profile: prof,
104
+ targetApp,
105
+ checks,
106
+ accountCycleSafety: safety,
107
+ executor,
108
+ blockedChecks,
109
+ recurrenceWarnings: recurrence,
110
+ sourceRevision: revisionResolved,
111
+ resourceUri: issuesResourceUri(root),
112
+ }, summary);
113
+ });
114
+ }
115
+ //# sourceMappingURL=mobileAudit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobileAudit.js","sourceRoot":"","sources":["../../src/tools/mobileAudit.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,qGAAqG;AACrG,sGAAsG;AACtG,sGAAsG;AACtG,2CAA2C;AAE3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,YAAY,EAAqB,MAAM,4BAA4B,CAAC;AACnH,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,SAAS,MAAM;IACb,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,MAAiB,EACjB,QAAsB,EACtB,IAAkD;IAElD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,qBAAqB,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACpE,IAAI,CAAC,QAAQ,CAAC,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnD,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,QAAsB;IAC3E,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,KAAK,EAAE,yBAAyB;QAChC,WAAW,EACT,yoCAAyoC;QAC3oC,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAClC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,YAAqC,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACxF,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mNAAmN,CAAC;YAC1Q,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;YACpH,wBAAwB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;YACzH,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;YACvG,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE;YAC1I,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oFAAoF,CAAC;SACzI;KACF,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,EAAE;QACxI,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI;YAAE,OAAO,OAAO,CAAC,EAAE,IAAI,EAAE,kCAAkC,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,gCAAgC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAE/K,MAAM,IAAI,GAAG,OAAuB,CAAC;QACrC,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;YAC7C,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS;YAClF,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,oBAAoB,EAAE,YAAY,CAAC,oBAAoB;YACvD,IAAI;SACL,CAAC,CAAC;QAEH,iEAAiE;QACjE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAChE,IAAI,CAAC,OAAO;gBAAE,OAAO,OAAO,CAAC,EAAE,IAAI,EAAE,uDAAuD,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,uGAAuG,CAAC,EAAE,CAAC,CAAC;YAC5P,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM;gBAAE,OAAO,OAAO,CAAC,EAAE,IAAI,EAAE,oCAAoC,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,yEAAyE,CAAC,EAAE,CAAC,CAAC;YAC1M,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC3L,IAAI,SAA6B,CAAC;gBAClC,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,MAAM,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;oBAC7D,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;oBACxB,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACP,2BAA2B;gBAC7B,CAAC;gBACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;gBACpE,OAAO,IAAI,CACT,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,EACnF,mBAAmB,IAAI,KAAK,GAAG,CAAC,KAAK,aAAa,GAAG,CAAC,aAAa,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAQ,CAAC,MAAM,YAAY,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,kBAAkB,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CACpO,CAAC;YACJ,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,OAAO,CAAC,EAAE,IAAI,EAAE,wBAAwB,MAAM,CAAE,CAAW,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,kDAAkD,CAAC,EAAE,CAAC,CAAC;YAC9L,CAAC;QACH,CAAC;QAED,2BAA2B;QAC3B,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,WAAW,EAAE,CAAC,CAAC;QAEvG,gGAAgG;QAChG,gGAAgG;QAChG,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY;YAClC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,kBAAkB,EAAE,kBAAkB,KAAK,IAAI,EAAE,EAAE,IAAI,EAAE,kHAAkH,EAAE;YACjO,CAAC,CAAC,SAAS,CAAC;QAEd,+FAA+F;QAC/F,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,qBAAqB,IAAI,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAE5G,sEAAsE;QACtE,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAC;QACzE,MAAM,UAAU,GAAG,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAC;QAE1I,MAAM,OAAO,GAAG,iBAAiB,IAAI,KAAK,MAAM,CAAC,MAAM,YAAY,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,MAAM,0BAA0B,CAAC,CAAC,CAAC,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,MAAM,wBAAwB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC/N,OAAO,IAAI,CACT;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,IAAI;YACb,SAAS;YACT,MAAM;YACN,kBAAkB,EAAE,MAAM;YAC1B,QAAQ;YACR,aAAa;YACb,kBAAkB,EAAE,UAAU;YAC9B,cAAc,EAAE,gBAAgB;YAChC,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC;SACrC,EACD,OAAO,CACR,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -1,13 +1,19 @@
1
- // Network restore helpers retained for flows that temporarily change device connectivity.
1
+ // qa_network — safe offline/online testing without raw adb (Phase 2 CR2).
2
+ // Uses `cmd connectivity airplane-mode` (API 30+). Records the original state on first
3
+ // change so it can be restored; report + session-end restore use session.network.
4
+ import { z } from 'zod';
5
+ import { qaOk, qaError } from '../lib/result.js';
6
+ import { requireConsent, consumeConsent } from '../consent/consent.js';
7
+ import { getDriver } from '../session/attach.js';
2
8
  /** Restore network to the recorded original state. Exported for report/session-end use. */
3
9
  export async function restoreNetwork(sessions, session, d) {
4
10
  if (!session.network?.changed)
5
11
  return null;
6
12
  const original = session.network.originalAirplane;
7
- await d.setAirplane(original);
13
+ await d.setAirplane(original); // original airplane flag
8
14
  sessions.addEnvChange(session, `network restored (airplane=${original})`);
9
15
  sessions.recordMutation(session, {
10
- tool: 'qa_report',
16
+ tool: 'qa_network',
11
17
  action: 'network_restore',
12
18
  risk: 'low',
13
19
  target: { toAirplane: original },
@@ -19,7 +25,7 @@ export async function restoreNetwork(sessions, session, d) {
19
25
  sessions.persist(session);
20
26
  return `restored (airplane=${original})`;
21
27
  }
22
- /** Best-effort restore of every session that changed the network, for server shutdown. */
28
+ /** Best-effort restore of EVERY session that changed the network — for server shutdown. */
23
29
  export async function restoreAllNetwork(sessions) {
24
30
  const { DirectDriver } = await import('../drivers/DirectDriver.js');
25
31
  for (const s of sessions.list()) {
@@ -31,7 +37,7 @@ export async function restoreAllNetwork(sessions) {
31
37
  try {
32
38
  await d.setAirplane(s.network.originalAirplane);
33
39
  sessions.recordMutation(s, {
34
- tool: 'swipium_shutdown',
40
+ tool: 'qa_network',
35
41
  action: 'network_restore',
36
42
  risk: 'low',
37
43
  target: { toAirplane: s.network.originalAirplane, shutdown: true },
@@ -47,4 +53,90 @@ export async function restoreAllNetwork(sessions) {
47
53
  }
48
54
  }
49
55
  }
56
+ export function registerNetwork(server, sessions) {
57
+ server.registerTool('qa_network', {
58
+ title: 'Network state control',
59
+ description: 'Offline/online testing via airplane mode (no raw adb; needs Android 11+ `cmd connectivity`). Actions: status, offline, online, restore. Swipium records the original airplane state on the first change and restores it on qa_report, on explicit `restore`, and best-effort on server shutdown (SIGINT/SIGTERM/transport close). offline/online require consent.',
60
+ inputSchema: {
61
+ sessionId: z.string(),
62
+ action: z.enum(['status', 'offline', 'online', 'restore']),
63
+ consentId: z.string().optional(),
64
+ approve: z.boolean().optional(),
65
+ },
66
+ }, async ({ sessionId, action, consentId, approve }) => {
67
+ const session = sessions.get(sessionId);
68
+ const { driver: d } = session ? await getDriver(session) : { driver: undefined };
69
+ if (!session || !d) {
70
+ return qaError({ what: 'No device attached', changedState: false, retrySafe: true, nextSteps: ['Call qa_prepare_target first.'] });
71
+ }
72
+ const airplane = await d.airplaneOn();
73
+ if (action === 'status') {
74
+ return qaOk({ network: airplane ? 'offline' : 'online', airplaneOn: airplane, changedBySwipium: !!session.network?.changed, restoreAvailable: !!session.network?.changed }, `network=${airplane ? 'offline (airplane on)' : 'online'}${session.network?.changed ? ' (changed by Swipium — will restore)' : ''}`);
75
+ }
76
+ if (action === 'restore') {
77
+ const msg = await restoreNetwork(sessions, session, d);
78
+ return qaOk({ network: (await d.airplaneOn()) ? 'offline' : 'online', restored: !!msg }, msg ? `network ${msg}` : 'nothing to restore (Swipium did not change the network)');
79
+ }
80
+ // offline / online — consent-gated, records original on first change.
81
+ const wantAirplane = action === 'offline';
82
+ const gate = consumeConsent(consentId, approve, { action: 'network_change', affects: { to: action } });
83
+ if (!gate.approved) {
84
+ sessions.recordMutation(session, {
85
+ tool: 'qa_network',
86
+ action: 'network_change',
87
+ risk: 'medium',
88
+ target: { to: action, airplane: wantAirplane },
89
+ consent: { required: true, approved: false, payloadHash: action },
90
+ status: 'requested',
91
+ });
92
+ return requireConsent({
93
+ action: 'network_change',
94
+ risk: 'medium',
95
+ exactCommand: `adb shell cmd connectivity airplane-mode ${wantAirplane ? 'enable' : 'disable'}`,
96
+ affects: { to: action },
97
+ explain: `Set the device ${action} (airplane mode ${wantAirplane ? 'ON' : 'OFF'})? Swipium will restore the original state on qa_report / session end.`,
98
+ });
99
+ }
100
+ sessions.recordMutation(session, {
101
+ tool: 'qa_network',
102
+ action: 'network_change',
103
+ risk: 'medium',
104
+ target: { to: action, airplane: wantAirplane },
105
+ consent: { required: true, consentId, approved: true, payloadHash: action },
106
+ status: 'approved',
107
+ });
108
+ if (!session.network?.changed) {
109
+ session.network = { changed: true, originalAirplane: airplane };
110
+ }
111
+ sessions.persist(session);
112
+ try {
113
+ await d.setAirplane(wantAirplane);
114
+ }
115
+ catch (e) {
116
+ // Older images / no `cmd connectivity airplane-mode` (pre-Android 11).
117
+ if (!session.network.changed)
118
+ session.network = undefined;
119
+ sessions.persist(session);
120
+ return qaError({
121
+ what: `Network control unsupported on this device: ${String(e)}`,
122
+ changedState: false,
123
+ retrySafe: false,
124
+ nextSteps: ['Needs Android 11+ (`cmd connectivity airplane-mode`). Use a newer emulator image, or toggle network manually.'],
125
+ });
126
+ }
127
+ sessions.addEnvChange(session, `network → ${action} (airplane=${wantAirplane})`);
128
+ await new Promise((r) => setTimeout(r, 1200)); // settle
129
+ const now = await d.airplaneOn();
130
+ sessions.recordMutation(session, {
131
+ tool: 'qa_network',
132
+ action: 'network_change',
133
+ risk: 'medium',
134
+ target: { to: action, airplane: wantAirplane },
135
+ consent: { required: true, approved: true, payloadHash: action },
136
+ status: 'executed',
137
+ detail: `network=${now ? 'offline' : 'online'}`,
138
+ });
139
+ return qaOk({ network: now ? 'offline' : 'online', previousStateRecorded: true, restoreAvailable: true }, `network → ${now ? 'offline' : 'online'}. Original recorded; restore on qa_report / qa_network restore.`);
140
+ });
141
+ }
50
142
  //# sourceMappingURL=network.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/tools/network.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAK1F,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAsB,EAAE,OAAgB,EAAE,CAAS;IACtF,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;IAClD,MAAM,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,8BAA8B,QAAQ,GAAG,CAAC,CAAC;IAC1E,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE;QAC/B,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,iBAAiB;QACzB,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE;QAChC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC5C,MAAM,EAAE,UAAU;QAClB,MAAM,EAAE,qBAAqB,QAAQ,EAAE;KACxC,CAAC,CAAC;IACH,OAAO,CAAC,OAAO,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC;IACjE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,OAAO,sBAAsB,QAAQ,GAAG,CAAC;AAC3C,CAAC;AAED,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAsB;IAC5D,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;IACpE,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO;YAAE,SAAS;QAClC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC1E,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,IAAI,CAAC;YACH,MAAM,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YAChD,QAAQ,CAAC,cAAc,CAAC,CAAC,EAAE;gBACzB,IAAI,EAAE,kBAAkB;gBACxB,MAAM,EAAE,iBAAiB;gBACzB,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAClE,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC5C,MAAM,EAAE,UAAU;gBAClB,MAAM,EAAE,iCAAiC;aAC1C,CAAC,CAAC;YACH,CAAC,CAAC,OAAO,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAC7E,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/tools/network.ts"],"names":[],"mappings":"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"}