visor-ai 0.2.8 → 0.2.9
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/dist/adapters.js +224 -22
- package/dist/appMap.js +1902 -137
- package/dist/cli.js +304 -45
- package/dist/localRuntime.js +8 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -47,6 +47,7 @@ const GLOBAL_SPEC = {
|
|
|
47
47
|
'app-id': 'string',
|
|
48
48
|
attach: 'boolean',
|
|
49
49
|
'no-map': 'boolean',
|
|
50
|
+
repair: 'boolean',
|
|
50
51
|
verbose: 'boolean'
|
|
51
52
|
};
|
|
52
53
|
const ACTION_SPEC = {
|
|
@@ -54,12 +55,17 @@ const ACTION_SPEC = {
|
|
|
54
55
|
target: 'string',
|
|
55
56
|
x: 'number',
|
|
56
57
|
y: 'number',
|
|
58
|
+
'start-x': 'number',
|
|
59
|
+
'start-y': 'number',
|
|
60
|
+
'end-x': 'number',
|
|
61
|
+
'end-y': 'number',
|
|
57
62
|
direction: 'string',
|
|
58
63
|
percent: 'number',
|
|
59
64
|
normalized: 'boolean',
|
|
60
65
|
to: 'string',
|
|
61
66
|
name: 'string',
|
|
62
67
|
value: 'string',
|
|
68
|
+
'start-value': 'number',
|
|
63
69
|
label: 'string',
|
|
64
70
|
ms: 'number',
|
|
65
71
|
path: 'string'
|
|
@@ -75,7 +81,11 @@ const COMMAND_SPECS = {
|
|
|
75
81
|
'server-url': 'string',
|
|
76
82
|
'app-id': 'string',
|
|
77
83
|
attach: 'boolean',
|
|
78
|
-
'no-map': 'boolean'
|
|
84
|
+
'no-map': 'boolean',
|
|
85
|
+
repair: 'boolean',
|
|
86
|
+
crawl: 'boolean',
|
|
87
|
+
'crawl-depth': 'number',
|
|
88
|
+
'crawl-limit': 'number'
|
|
79
89
|
},
|
|
80
90
|
discover: {
|
|
81
91
|
device: 'string',
|
|
@@ -84,7 +94,11 @@ const COMMAND_SPECS = {
|
|
|
84
94
|
'server-url': 'string',
|
|
85
95
|
'app-id': 'string',
|
|
86
96
|
attach: 'boolean',
|
|
87
|
-
'no-map': 'boolean'
|
|
97
|
+
'no-map': 'boolean',
|
|
98
|
+
repair: 'boolean',
|
|
99
|
+
crawl: 'boolean',
|
|
100
|
+
'crawl-depth': 'number',
|
|
101
|
+
'crawl-limit': 'number'
|
|
88
102
|
},
|
|
89
103
|
benchmark: {
|
|
90
104
|
runs: 'number',
|
|
@@ -96,7 +110,9 @@ const COMMAND_SPECS = {
|
|
|
96
110
|
'server-url': 'string',
|
|
97
111
|
'app-id': 'string',
|
|
98
112
|
attach: 'boolean',
|
|
99
|
-
'no-map': 'boolean'
|
|
113
|
+
'no-map': 'boolean',
|
|
114
|
+
repair: 'boolean',
|
|
115
|
+
'compare-map': 'boolean'
|
|
100
116
|
},
|
|
101
117
|
report: { format: 'string' },
|
|
102
118
|
start: {
|
|
@@ -121,6 +137,13 @@ const COMMAND_SPECS = {
|
|
|
121
137
|
wait: ACTION_SPEC,
|
|
122
138
|
source: ACTION_SPEC
|
|
123
139
|
};
|
|
140
|
+
const ACTION_ARG_OPTION_NAMES = {
|
|
141
|
+
'start-x': 'startX',
|
|
142
|
+
'start-y': 'startY',
|
|
143
|
+
'end-x': 'endX',
|
|
144
|
+
'end-y': 'endY',
|
|
145
|
+
'start-value': 'startValue'
|
|
146
|
+
};
|
|
124
147
|
function helpText() {
|
|
125
148
|
return [
|
|
126
149
|
'Visor TypeScript CLI',
|
|
@@ -152,6 +175,116 @@ function helpText() {
|
|
|
152
175
|
' visor status'
|
|
153
176
|
].join('\n');
|
|
154
177
|
}
|
|
178
|
+
function commandHelpText(command) {
|
|
179
|
+
if (command === 'tap') {
|
|
180
|
+
const examples = [
|
|
181
|
+
'visor tap --target accessibility=Continue',
|
|
182
|
+
'visor tap --target text=Continue',
|
|
183
|
+
'visor tap --target text~=Settings',
|
|
184
|
+
'visor tap --target "first-in-section=Featured products"',
|
|
185
|
+
'visor tap --x 120 --y 640',
|
|
186
|
+
'visor tap --x 0.5 --y 0.92 --normalized'
|
|
187
|
+
];
|
|
188
|
+
return {
|
|
189
|
+
usageText: [
|
|
190
|
+
'Visor tap',
|
|
191
|
+
'',
|
|
192
|
+
'Usage:',
|
|
193
|
+
' visor tap --target <selector> [runtime options]',
|
|
194
|
+
' visor tap --x <points> --y <points> [--normalized] [runtime options]',
|
|
195
|
+
'',
|
|
196
|
+
'Options:',
|
|
197
|
+
' --target <selector> Selector to tap',
|
|
198
|
+
' --x <points> X coordinate in screen points, or fraction with --normalized',
|
|
199
|
+
' --y <points> Y coordinate in screen points, or fraction with --normalized',
|
|
200
|
+
' --normalized Treat x/y as fractions of current screen size',
|
|
201
|
+
' --no-map Disable app-map reads and writes',
|
|
202
|
+
' --repair Allow opt-in exploratory app-map repair'
|
|
203
|
+
].join('\n'),
|
|
204
|
+
examples
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (command === 'discover') {
|
|
208
|
+
const examples = [
|
|
209
|
+
'visor discover --app-id com.example.app',
|
|
210
|
+
'visor discover --app-id com.example.app --crawl --crawl-depth 2 --crawl-limit 24'
|
|
211
|
+
];
|
|
212
|
+
return {
|
|
213
|
+
usageText: [
|
|
214
|
+
'Visor discover',
|
|
215
|
+
'',
|
|
216
|
+
'Usage:',
|
|
217
|
+
' visor discover [--app-id <id>] [--crawl] [runtime options]',
|
|
218
|
+
'',
|
|
219
|
+
'Options:',
|
|
220
|
+
' --crawl Explore safe controls and record app-map edges',
|
|
221
|
+
' --crawl-depth <n> Maximum crawl depth, default 2',
|
|
222
|
+
' --crawl-limit <n> Maximum crawl actions, default 24',
|
|
223
|
+
' --no-map Disable app-map reads and writes'
|
|
224
|
+
].join('\n'),
|
|
225
|
+
examples
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (command === 'act') {
|
|
229
|
+
const examples = [
|
|
230
|
+
'visor act --name back',
|
|
231
|
+
'visor act --name reset --app-id com.example.app',
|
|
232
|
+
'visor act --name drag --start-x 120 --start-y 640 --end-x 320 --end-y 640',
|
|
233
|
+
'visor act --name slider --target accessibility=Amount --value 0.5'
|
|
234
|
+
];
|
|
235
|
+
return {
|
|
236
|
+
usageText: [
|
|
237
|
+
'Visor act',
|
|
238
|
+
'',
|
|
239
|
+
'Usage:',
|
|
240
|
+
' visor act --name <type|back|home|reset|drag|slider> [options]',
|
|
241
|
+
'',
|
|
242
|
+
'Options:',
|
|
243
|
+
' --name <operation> Helper action name',
|
|
244
|
+
' --target <selector> Target selector for type or slider',
|
|
245
|
+
' --value <value> Text value for type, or 0..1 slider value',
|
|
246
|
+
' --start-x <points> Drag start x coordinate',
|
|
247
|
+
' --start-y <points> Drag start y coordinate',
|
|
248
|
+
' --end-x <points> Drag end x coordinate',
|
|
249
|
+
' --end-y <points> Drag end y coordinate',
|
|
250
|
+
' --start-value <0..1> Slider starting value, default 0.5',
|
|
251
|
+
' --normalized Treat drag coordinates as viewport fractions'
|
|
252
|
+
].join('\n'),
|
|
253
|
+
examples
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
if (command === 'benchmark') {
|
|
257
|
+
const examples = [
|
|
258
|
+
'visor benchmark scenarios/checkout-smoke.json --runs 20',
|
|
259
|
+
'visor benchmark scenarios/checkout-smoke.json --runs 5 --compare-map'
|
|
260
|
+
];
|
|
261
|
+
return {
|
|
262
|
+
usageText: [
|
|
263
|
+
'Visor benchmark',
|
|
264
|
+
'',
|
|
265
|
+
'Usage:',
|
|
266
|
+
' visor benchmark <scenario> [--runs <n>] [--threshold <percent>] [--compare-map]',
|
|
267
|
+
'',
|
|
268
|
+
'Options:',
|
|
269
|
+
' --runs <n> Number of runs, default 20',
|
|
270
|
+
' --threshold <percent> Required determinism score, default 95',
|
|
271
|
+
' --compare-map Run no-map and app-map variants for A/B comparison'
|
|
272
|
+
].join('\n'),
|
|
273
|
+
examples
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
usageText: [
|
|
278
|
+
`Visor ${command}`,
|
|
279
|
+
'',
|
|
280
|
+
'Usage:',
|
|
281
|
+
` visor ${command} [options]`,
|
|
282
|
+
'',
|
|
283
|
+
'Run visor --help for the full command list.'
|
|
284
|
+
].join('\n'),
|
|
285
|
+
examples: []
|
|
286
|
+
};
|
|
287
|
+
}
|
|
155
288
|
function envelopeOk(commandId, startedAt, artifacts = [], nextAction = '') {
|
|
156
289
|
return {
|
|
157
290
|
status: 'ok',
|
|
@@ -237,7 +370,11 @@ async function resolvedRuntime(options) {
|
|
|
237
370
|
app_id: typeof options['app-id'] === 'string' ? options['app-id'] : undefined,
|
|
238
371
|
attach_to_running: Boolean(options.attach),
|
|
239
372
|
map: {
|
|
240
|
-
enabled: !Boolean(options['no-map'])
|
|
373
|
+
enabled: !Boolean(options['no-map']),
|
|
374
|
+
...(options.repair === true ? { repair: true } : {}),
|
|
375
|
+
...(options.crawl === true ? { crawl: true } : {}),
|
|
376
|
+
...(typeof options['crawl-depth'] === 'number' ? { crawlDepth: options['crawl-depth'] } : {}),
|
|
377
|
+
...(typeof options['crawl-limit'] === 'number' ? { crawlLimit: options['crawl-limit'] } : {})
|
|
241
378
|
}
|
|
242
379
|
};
|
|
243
380
|
}
|
|
@@ -253,11 +390,12 @@ function actionArgs(command, options) {
|
|
|
253
390
|
'runtime',
|
|
254
391
|
'app-id',
|
|
255
392
|
'attach',
|
|
256
|
-
'no-map'
|
|
393
|
+
'no-map',
|
|
394
|
+
'repair'
|
|
257
395
|
]);
|
|
258
396
|
const args = Object.entries(options).reduce((acc, [key, value]) => {
|
|
259
397
|
if (!commonIgnored.has(key) && value !== undefined) {
|
|
260
|
-
acc[key] = value;
|
|
398
|
+
acc[ACTION_ARG_OPTION_NAMES[key] ?? key] = value;
|
|
261
399
|
}
|
|
262
400
|
return acc;
|
|
263
401
|
}, {});
|
|
@@ -289,14 +427,78 @@ function targetInitializationNextStep(error) {
|
|
|
289
427
|
}
|
|
290
428
|
return 'Verify Appium driver setup, target device state, and app id, then retry.';
|
|
291
429
|
}
|
|
292
|
-
function
|
|
430
|
+
function isRecord(value) {
|
|
431
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
432
|
+
}
|
|
433
|
+
function optionalString(value) {
|
|
434
|
+
return typeof value === 'string' ? value : undefined;
|
|
435
|
+
}
|
|
436
|
+
function mapArray(value) {
|
|
437
|
+
return Array.isArray(value) ? value : [];
|
|
438
|
+
}
|
|
439
|
+
function mapActionCount(variants) {
|
|
440
|
+
let total = 0;
|
|
441
|
+
for (const variant of variants) {
|
|
442
|
+
if (!isRecord(variant)) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
total += mapArray(variant.actions).length;
|
|
446
|
+
}
|
|
447
|
+
return total;
|
|
448
|
+
}
|
|
449
|
+
function appMapSummaryFromPath(mapPath) {
|
|
450
|
+
try {
|
|
451
|
+
const parsed = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
|
|
452
|
+
if (!isRecord(parsed)) {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
const variants = mapArray(parsed.variants);
|
|
456
|
+
return {
|
|
457
|
+
schema_version: typeof parsed.schema_version === 'number' ? parsed.schema_version : undefined,
|
|
458
|
+
identity: optionalString(parsed.identity),
|
|
459
|
+
app_id: optionalString(parsed.app_id),
|
|
460
|
+
platform: optionalString(parsed.platform),
|
|
461
|
+
screens: mapArray(parsed.screens).length,
|
|
462
|
+
variants: variants.length,
|
|
463
|
+
edges: mapArray(parsed.edges).length,
|
|
464
|
+
actions: mapActionCount(variants),
|
|
465
|
+
auth_required_variants: variants.filter((variant) => isRecord(variant) && variant.auth_required === true).length,
|
|
466
|
+
updated_at: optionalString(parsed.updated_at)
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function addDiscoverMapSummary(result) {
|
|
474
|
+
if (!isRecord(result.map)) {
|
|
475
|
+
return result;
|
|
476
|
+
}
|
|
477
|
+
const map = result.map;
|
|
478
|
+
if (map.summary !== undefined || typeof map.path !== 'string') {
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
const summary = appMapSummaryFromPath(map.path);
|
|
482
|
+
if (!summary) {
|
|
483
|
+
return result;
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
...result,
|
|
487
|
+
map: {
|
|
488
|
+
...map,
|
|
489
|
+
summary
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function cmdHelp(command) {
|
|
293
494
|
const commandId = makeId('cmd');
|
|
294
495
|
const startedAt = utcNowIso();
|
|
295
496
|
const response = envelopeOk(commandId, startedAt, [], 'validate');
|
|
497
|
+
const commandHelp = command ? commandHelpText(command) : undefined;
|
|
296
498
|
response.data = {
|
|
297
|
-
usageText: helpText(),
|
|
298
|
-
commands: Array.from(ALL_COMMANDS),
|
|
299
|
-
examples: [
|
|
499
|
+
usageText: commandHelp?.usageText ?? helpText(),
|
|
500
|
+
commands: command ? [command] : Array.from(ALL_COMMANDS),
|
|
501
|
+
examples: commandHelp?.examples ?? [
|
|
300
502
|
'visor validate scenarios/checkout-smoke.json',
|
|
301
503
|
'visor run path/to/scenario.json --runtime local --output artifacts-local',
|
|
302
504
|
'visor start --server-url http://127.0.0.1:4723',
|
|
@@ -474,7 +676,7 @@ export async function cmdDiscover(parsed) {
|
|
|
474
676
|
appId: runtime.app_id
|
|
475
677
|
});
|
|
476
678
|
const response = envelopeOk(commandId, startedAt, [], 'run');
|
|
477
|
-
response.data = result;
|
|
679
|
+
response.data = addDiscoverMapSummary(result);
|
|
478
680
|
return { code: 0, response };
|
|
479
681
|
}
|
|
480
682
|
catch (error) {
|
|
@@ -525,42 +727,97 @@ export async function cmdBenchmark(parsed) {
|
|
|
525
727
|
}
|
|
526
728
|
const runs = typeof parsed.options.runs === 'number' ? parsed.options.runs : 20;
|
|
527
729
|
const threshold = typeof parsed.options.threshold === 'number' ? parsed.options.threshold : 95;
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
730
|
+
const runtimeRequest = {
|
|
731
|
+
platform: runtime.platform,
|
|
732
|
+
server_url: runtime.server_url,
|
|
733
|
+
device: runtime.device,
|
|
734
|
+
app_id: runtime.app_id,
|
|
735
|
+
attach_to_running: runtime.attach_to_running
|
|
736
|
+
};
|
|
737
|
+
const runVariant = async (name, mapEnabled, mapOptions) => {
|
|
738
|
+
const signatures = [];
|
|
739
|
+
const runIds = [];
|
|
740
|
+
let failures = 0;
|
|
741
|
+
for (let index = 0; index < runs; index += 1) {
|
|
742
|
+
try {
|
|
743
|
+
const result = await runDaemonScenario(runtimeRequest, scenario, runtime.device, runtime.timeout, runtime.output_dir, mapOptions);
|
|
744
|
+
writeReports(result, runtime.output_dir);
|
|
745
|
+
signatures.push(result.determinism_signature);
|
|
746
|
+
runIds.push(result.run_id);
|
|
747
|
+
if (result.status !== 'ok') {
|
|
748
|
+
failures += 1;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
if (error instanceof DaemonUnavailableError) {
|
|
753
|
+
const response = envelopeFail(commandId, startedAt, 'TARGET_ERROR', 'Benchmark requires visor start', errorMessage(error), 'Run `visor start`, verify the target emulator/simulator is booted, and retry.');
|
|
754
|
+
return { error: { code: 1, response } };
|
|
755
|
+
}
|
|
756
|
+
if (error instanceof DaemonRequestTimeoutError) {
|
|
757
|
+
const response = envelopeFail(commandId, startedAt, 'TARGET_ERROR', 'Timed out waiting for Visor daemon', errorMessage(error), 'Inspect .visor/daemon/daemon.log and .visor/appium/*.log, then retry or run `visor stop --force` before restarting.');
|
|
758
|
+
return { error: { code: 1, response } };
|
|
759
|
+
}
|
|
547
760
|
failures += 1;
|
|
548
761
|
}
|
|
549
762
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
763
|
+
const score = determinismCheck(signatures);
|
|
764
|
+
return {
|
|
765
|
+
variant: {
|
|
766
|
+
name,
|
|
767
|
+
mapEnabled,
|
|
768
|
+
runs,
|
|
769
|
+
threshold,
|
|
770
|
+
determinismScore: score,
|
|
771
|
+
pass: score >= threshold && failures === 0,
|
|
772
|
+
failures,
|
|
773
|
+
runIds
|
|
554
774
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
775
|
+
};
|
|
776
|
+
};
|
|
777
|
+
if (parsed.options['compare-map'] === true) {
|
|
778
|
+
const variants = [];
|
|
779
|
+
for (const variantConfig of [
|
|
780
|
+
{ name: 'no-map', mapEnabled: false },
|
|
781
|
+
{ name: 'map', mapEnabled: true }
|
|
782
|
+
]) {
|
|
783
|
+
const outcome = await runVariant(variantConfig.name, variantConfig.mapEnabled, {
|
|
784
|
+
...runtime.map,
|
|
785
|
+
enabled: variantConfig.mapEnabled,
|
|
786
|
+
appId: runtime.app_id
|
|
787
|
+
});
|
|
788
|
+
if ('error' in outcome) {
|
|
789
|
+
return outcome.error;
|
|
558
790
|
}
|
|
559
|
-
|
|
791
|
+
variants.push(outcome.variant);
|
|
560
792
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
793
|
+
const failures = variants.reduce((total, variant) => total + Number(variant.failures ?? 0), 0);
|
|
794
|
+
const score = Math.min(...variants.map((variant) => Number(variant.determinismScore ?? 0)));
|
|
795
|
+
const passGate = variants.every((variant) => variant.pass === true);
|
|
796
|
+
const runIds = variants.flatMap((variant) => Array.isArray(variant.runIds) ? variant.runIds : []);
|
|
797
|
+
const response = envelopeOk(commandId, startedAt, [], 'report');
|
|
798
|
+
response.data = {
|
|
799
|
+
runs,
|
|
800
|
+
threshold,
|
|
801
|
+
determinismScore: score,
|
|
802
|
+
pass: passGate,
|
|
803
|
+
failures,
|
|
804
|
+
runIds,
|
|
805
|
+
variants,
|
|
806
|
+
warnings: warningIssues(issues)
|
|
807
|
+
};
|
|
808
|
+
return { code: passGate ? 0 : 3, response };
|
|
809
|
+
}
|
|
810
|
+
const outcome = await runVariant('benchmark', runtime.map.enabled !== false, {
|
|
811
|
+
...runtime.map,
|
|
812
|
+
appId: runtime.app_id
|
|
813
|
+
});
|
|
814
|
+
if ('error' in outcome) {
|
|
815
|
+
return outcome.error;
|
|
816
|
+
}
|
|
817
|
+
const score = Number(outcome.variant.determinismScore ?? 0);
|
|
818
|
+
const failures = Number(outcome.variant.failures ?? 0);
|
|
819
|
+
const passGate = outcome.variant.pass === true;
|
|
820
|
+
const runIds = Array.isArray(outcome.variant.runIds) ? outcome.variant.runIds : [];
|
|
564
821
|
const response = envelopeOk(commandId, startedAt, [], 'report');
|
|
565
822
|
response.data = {
|
|
566
823
|
runs,
|
|
@@ -690,10 +947,12 @@ export async function cmdStop(parsed) {
|
|
|
690
947
|
}
|
|
691
948
|
}
|
|
692
949
|
export async function executeCommand(argv) {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
argv.
|
|
696
|
-
|
|
950
|
+
const requestedHelp = argv[0] === 'help' || argv.includes('--help') || argv.includes('-h');
|
|
951
|
+
if (requestedHelp) {
|
|
952
|
+
const helpCommand = argv.find((token) => ALL_COMMANDS.has(token));
|
|
953
|
+
return cmdHelp(helpCommand);
|
|
954
|
+
}
|
|
955
|
+
if (argv.length === 0) {
|
|
697
956
|
return cmdHelp();
|
|
698
957
|
}
|
|
699
958
|
if (argv.includes('--version') || argv.includes('-v')) {
|
package/dist/localRuntime.js
CHANGED
|
@@ -38,6 +38,14 @@ export class LocalRuntimeAdapter {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
async act(args) {
|
|
41
|
+
const name = String(args.name ?? '');
|
|
42
|
+
if (name === 'reset') {
|
|
43
|
+
this.counter = 0;
|
|
44
|
+
this.route = 'app://home';
|
|
45
|
+
}
|
|
46
|
+
if (name === 'home') {
|
|
47
|
+
this.route = 'app://home';
|
|
48
|
+
}
|
|
41
49
|
return {
|
|
42
50
|
action: 'act',
|
|
43
51
|
platform: 'android',
|