@skrillex1224/android-toolkit 0.1.9 → 1.0.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/README.md +51 -64
- package/browser.d.ts +31 -0
- package/dist/browser.js +107 -0
- package/dist/browser.js.map +7 -0
- package/dist/index.cjs +1910 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +1880 -0
- package/dist/index.js.map +7 -0
- package/index.d.ts +157 -184
- package/package.json +30 -10
- package/entrys/node.js +0 -26
- package/index.js +0 -1
- package/scripts/postinstall.js +0 -72
- package/src/apify-kit.js +0 -217
- package/src/constants.js +0 -75
- package/src/context.js +0 -89
- package/src/device.js +0 -761
- package/src/errors.js +0 -37
- package/src/frida-client.js +0 -1074
- package/src/internals/compression.js +0 -188
- package/src/internals/frida/webview_event_agent.js +0 -227
- package/src/launch.js +0 -70
- package/src/logger.js +0 -111
- package/src/share.js +0 -644
package/index.d.ts
CHANGED
|
@@ -1,191 +1,164 @@
|
|
|
1
1
|
export interface AndroidContext {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
startWebViewEventRecorder(ctx: AndroidContext, config?: Record<string, any>, options?: Record<string, any>): Promise<any>;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface AndroidShareScreenCompressionOptions {
|
|
90
|
-
enabled?: boolean;
|
|
91
|
-
maxBytes?: number;
|
|
92
|
-
maxBase64Bytes?: number;
|
|
93
|
-
type?: 'jpeg' | 'jpg';
|
|
94
|
-
outputType?: 'jpeg' | 'jpg';
|
|
95
|
-
quality?: number;
|
|
96
|
-
minQuality?: number;
|
|
97
|
-
minScale?: number;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export interface AndroidShareScreenCaptureOptions {
|
|
101
|
-
Frida?: AndroidFridaModule;
|
|
102
|
-
frida?: AndroidFridaModule;
|
|
103
|
-
actorInfo?: string | AndroidActorInfoItem;
|
|
104
|
-
actor?: string | AndroidActorInfoItem;
|
|
105
|
-
actorKey?: string | AndroidActorInfoItem;
|
|
106
|
-
restore?: boolean;
|
|
107
|
-
maxHeight?: number;
|
|
108
|
-
width?: number;
|
|
109
|
-
height?: number;
|
|
110
|
-
settleMs?: number;
|
|
111
|
-
scrollToTop?: boolean;
|
|
112
|
-
timeoutMs?: number;
|
|
113
|
-
fridaTimeoutSeconds?: number;
|
|
114
|
-
maxBytes?: number;
|
|
115
|
-
maxBase64Bytes?: number;
|
|
116
|
-
type?: 'jpeg' | 'jpg';
|
|
117
|
-
outputType?: 'jpeg' | 'jpg';
|
|
118
|
-
quality?: number;
|
|
119
|
-
minQuality?: number;
|
|
120
|
-
minScale?: number;
|
|
121
|
-
compression?: boolean | AndroidShareScreenCompressionOptions;
|
|
122
|
-
compress?: boolean;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export interface AndroidShareLinkCaptureOptions {
|
|
126
|
-
actorInfo?: string | AndroidActorInfoItem;
|
|
127
|
-
actor?: string | AndroidActorInfoItem;
|
|
128
|
-
actorKey?: string | AndroidActorInfoItem;
|
|
129
|
-
share?: Partial<AndroidActorShareInfo>;
|
|
130
|
-
timeoutMs?: number;
|
|
131
|
-
pollIntervalMs?: number;
|
|
132
|
-
payloadSnapshotMaxLen?: number;
|
|
133
|
-
signal?: AbortSignal;
|
|
134
|
-
performActions?: () => any | Promise<any>;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface AndroidShareLinkCaptureResult {
|
|
138
|
-
link: string | null;
|
|
139
|
-
payloadText: string;
|
|
140
|
-
payloadSnapshot: string;
|
|
141
|
-
source: 'clipboard' | 'custom' | 'none' | string;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export interface AndroidShareModule {
|
|
145
|
-
captureScreen(ctx: AndroidContext, options?: AndroidShareScreenCaptureOptions): Promise<string>;
|
|
146
|
-
captureLink(ctx: AndroidContext, options?: AndroidShareLinkCaptureOptions): Promise<AndroidShareLinkCaptureResult>;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export interface AndroidApifyKitInstance {
|
|
150
|
-
runStep<T>(name: string, meta: any, fn: () => Promise<T> | T): Promise<T>;
|
|
151
|
-
runStepLoose<T>(name: string, meta: any, fn: () => Promise<T> | T): Promise<T>;
|
|
152
|
-
pushSuccess(data: any): Promise<void>;
|
|
153
|
-
pushFailed(error: unknown, context?: Record<string, any>): Promise<void>;
|
|
154
|
-
pushArtifact(data: any): Promise<void>;
|
|
155
|
-
hasPushed(): boolean;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export interface AndroidApifyKitModule {
|
|
159
|
-
useApifyKit(options?: Record<string, any>): Promise<AndroidApifyKitInstance>;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export interface AndroidLaunchRunHandlerArgs {
|
|
163
|
-
input: any;
|
|
164
|
-
ctx: AndroidContext;
|
|
165
|
-
kit: {
|
|
166
|
-
ApifyKit: AndroidApifyKitInstance;
|
|
167
|
-
Device: AndroidDeviceModule;
|
|
168
|
-
Frida: AndroidFridaModule;
|
|
169
|
-
Share: AndroidShareModule;
|
|
170
|
-
Logger: AndroidLogger;
|
|
171
|
-
};
|
|
172
|
-
ApifyKit: AndroidApifyKitInstance;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export interface AndroidLaunchModule {
|
|
176
|
-
run(handler: (args: AndroidLaunchRunHandlerArgs) => Promise<void> | void, options?: Record<string, any>): Promise<void>;
|
|
2
|
+
runId: string;
|
|
3
|
+
query: string;
|
|
4
|
+
serial: string;
|
|
5
|
+
packageName: string;
|
|
6
|
+
adbPath: string;
|
|
7
|
+
fridaPath: string;
|
|
8
|
+
actorKey?: string;
|
|
9
|
+
appVersion?: string;
|
|
10
|
+
slotKey?: string;
|
|
11
|
+
instanceId?: string;
|
|
12
|
+
externalIp?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ViewBounds {
|
|
16
|
+
left: number;
|
|
17
|
+
top: number;
|
|
18
|
+
right: number;
|
|
19
|
+
bottom: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
centerX: number;
|
|
23
|
+
centerY: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ViewNode {
|
|
27
|
+
resourceId: string;
|
|
28
|
+
id: string;
|
|
29
|
+
text: string;
|
|
30
|
+
contentDesc: string;
|
|
31
|
+
className: string;
|
|
32
|
+
packageName: string;
|
|
33
|
+
bounds: ViewBounds;
|
|
34
|
+
clickable: boolean;
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
focusable: boolean;
|
|
37
|
+
focused: boolean;
|
|
38
|
+
scrollable: boolean;
|
|
39
|
+
visible: boolean;
|
|
40
|
+
children: ViewNode[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ViewSelector = {
|
|
44
|
+
id?: string;
|
|
45
|
+
text?: string;
|
|
46
|
+
textContains?: string;
|
|
47
|
+
contentDesc?: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export namespace Constants {
|
|
51
|
+
export const Code: Record<string, number>;
|
|
52
|
+
export const Status: Record<string, string>;
|
|
53
|
+
export const UnicodeIme: {
|
|
54
|
+
packageName: string;
|
|
55
|
+
component: string;
|
|
56
|
+
inputTextAction: string;
|
|
57
|
+
};
|
|
58
|
+
export const ActorInfo: Record<string, {
|
|
59
|
+
key: string;
|
|
60
|
+
name: string;
|
|
61
|
+
icon: string;
|
|
62
|
+
share: {
|
|
63
|
+
mode: 'clipboard';
|
|
64
|
+
prefix: string;
|
|
65
|
+
xurl: unknown[];
|
|
66
|
+
};
|
|
67
|
+
}>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export namespace Errors {
|
|
71
|
+
export class CrawlerError extends Error {
|
|
72
|
+
code: number;
|
|
73
|
+
context: Record<string, unknown>;
|
|
74
|
+
constructor(input?: string | {
|
|
75
|
+
message?: string;
|
|
76
|
+
code?: number;
|
|
77
|
+
context?: Record<string, unknown>;
|
|
78
|
+
});
|
|
79
|
+
static isCrawlerError(error: unknown): boolean;
|
|
80
|
+
static from(error: unknown, fallback?: {
|
|
81
|
+
code?: number;
|
|
82
|
+
context?: Record<string, unknown>;
|
|
83
|
+
}): CrawlerError;
|
|
84
|
+
}
|
|
85
|
+
export function serializeError(error: unknown): Record<string, unknown>;
|
|
177
86
|
}
|
|
178
87
|
|
|
179
88
|
export interface AndroidToolkit {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
89
|
+
Launch: {
|
|
90
|
+
run(handler: (args: {
|
|
91
|
+
input: Record<string, unknown>;
|
|
92
|
+
ctx: AndroidContext;
|
|
93
|
+
kit: AndroidToolkitRuntime;
|
|
94
|
+
ApifyKit: AndroidToolkitRuntime['ApifyKit'];
|
|
95
|
+
}) => Promise<void>, options?: Record<string, unknown>): Promise<void>;
|
|
96
|
+
};
|
|
97
|
+
ApifyKit: {
|
|
98
|
+
useApifyKit(options?: Record<string, unknown>): Promise<AndroidToolkitRuntime['ApifyKit']>;
|
|
99
|
+
};
|
|
100
|
+
DeviceInput: AndroidToolkitRuntime['DeviceInput'];
|
|
101
|
+
DeviceView: AndroidToolkitRuntime['DeviceView'];
|
|
102
|
+
Device: AndroidToolkitRuntime['Device'];
|
|
103
|
+
Mutation: AndroidToolkitRuntime['Mutation'];
|
|
104
|
+
Share: AndroidToolkitRuntime['Share'];
|
|
105
|
+
Frida: AndroidToolkitRuntime['Frida'];
|
|
106
|
+
Constants: typeof Constants;
|
|
107
|
+
Errors: typeof Errors;
|
|
108
|
+
Logger: AndroidToolkitRuntime['Logger'];
|
|
109
|
+
Context: {
|
|
110
|
+
createAndroidContext(input?: Record<string, unknown>, defaults?: Record<string, unknown>): AndroidContext;
|
|
111
|
+
firstNonEmpty(...values: unknown[]): string;
|
|
112
|
+
requireNonEmpty(value: unknown, name: string): string;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface AndroidToolkitRuntime {
|
|
117
|
+
ApifyKit: {
|
|
118
|
+
runStep<T>(step: string, target: unknown, actionFn: () => Promise<T>, options?: Record<string, unknown>): Promise<T>;
|
|
119
|
+
runStepLoose<T>(step: string, target: unknown, actionFn: () => Promise<T>, options?: Record<string, unknown>): Promise<T>;
|
|
120
|
+
pushSuccess(data?: Record<string, unknown>, options?: Record<string, unknown>): Promise<void>;
|
|
121
|
+
pushFailed(error: unknown, meta?: Record<string, unknown>): Promise<void>;
|
|
122
|
+
pushArtifact(data?: Record<string, unknown>): Promise<void>;
|
|
123
|
+
hasPushed(): boolean;
|
|
124
|
+
};
|
|
125
|
+
Device: Record<string, (...args: any[]) => Promise<any>>;
|
|
126
|
+
DeviceInput: {
|
|
127
|
+
click(ctx: AndroidContext, selectorOrPoint: ViewSelector | ViewBounds | {x: number; y: number}, options?: Record<string, unknown>): Promise<unknown>;
|
|
128
|
+
tap(ctx: AndroidContext, selectorOrPoint: ViewSelector | ViewBounds | {x: number; y: number}, options?: Record<string, unknown>): Promise<unknown>;
|
|
129
|
+
fill(ctx: AndroidContext, selector: ViewSelector, text: string, options?: Record<string, unknown>): Promise<void>;
|
|
130
|
+
type(ctx: AndroidContext, selector: ViewSelector, text: string, options?: Record<string, unknown>): Promise<void>;
|
|
131
|
+
press(ctx: AndroidContext, key: string, options?: Record<string, unknown>): Promise<void>;
|
|
132
|
+
pressEnter(ctx: AndroidContext, options?: Record<string, unknown>): Promise<void>;
|
|
133
|
+
scroll(ctx: AndroidContext, selector: ViewSelector, direction: 'up' | 'down', options?: Record<string, unknown>): Promise<void>;
|
|
134
|
+
scrollToEnd(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<unknown>;
|
|
135
|
+
scrollToTop(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<unknown>;
|
|
136
|
+
};
|
|
137
|
+
DeviceView: {
|
|
138
|
+
snapshot(ctx: AndroidContext, options?: Record<string, unknown>): Promise<{xml: string; tree: ViewNode[]; roots: ViewNode[]; flat: ViewNode[]; nodes: ViewNode[]}>;
|
|
139
|
+
find(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<ViewNode>;
|
|
140
|
+
findAll(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<ViewNode[]>;
|
|
141
|
+
waitFor(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<ViewNode>;
|
|
142
|
+
bounds(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<ViewBounds>;
|
|
143
|
+
text(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<string>;
|
|
144
|
+
exists(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<boolean>;
|
|
145
|
+
isScrollable(ctx: AndroidContext, selector: ViewSelector, options?: Record<string, unknown>): Promise<boolean>;
|
|
146
|
+
hashNode(node: ViewNode): string;
|
|
147
|
+
};
|
|
148
|
+
Mutation: {
|
|
149
|
+
waitForStable(ctx: AndroidContext, selectors: ViewSelector | ViewSelector[] | string | string[], options?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
150
|
+
waitForStableAcrossRoots(ctx: AndroidContext, selectors: ViewSelector | ViewSelector[] | string | string[], options?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
151
|
+
useMonitor(ctx: AndroidContext, selectors: ViewSelector | ViewSelector[] | string | string[], options?: Record<string, unknown>): Promise<{stop(): Promise<Record<string, unknown>>}>;
|
|
152
|
+
};
|
|
153
|
+
Share: {
|
|
154
|
+
captureScreen(ctx: AndroidContext, options?: Record<string, unknown>): Promise<string>;
|
|
155
|
+
captureLink(ctx: AndroidContext, options?: Record<string, unknown>): Promise<{link: string; source: string; payloadSnapshot: string}>;
|
|
156
|
+
};
|
|
157
|
+
Frida: {
|
|
158
|
+
querySQLite(ctx: AndroidContext, options: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
159
|
+
health(ctx: AndroidContext): Promise<Record<string, unknown>>;
|
|
160
|
+
};
|
|
161
|
+
Logger: Record<string, any>;
|
|
189
162
|
}
|
|
190
163
|
|
|
191
164
|
export function useAndroidToolKit(): AndroidToolkit;
|
package/package.json
CHANGED
|
@@ -1,29 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skrillex1224/android-toolkit",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Native Android automation toolkit for Apify-compatible actors.",
|
|
4
5
|
"type": "module",
|
|
5
|
-
"main": "index.
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
6
8
|
"types": "index.d.ts",
|
|
7
9
|
"exports": {
|
|
8
|
-
".":
|
|
10
|
+
".": {
|
|
11
|
+
"browser": {
|
|
12
|
+
"types": "./browser.d.ts",
|
|
13
|
+
"default": "./dist/browser.js"
|
|
14
|
+
},
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./browser": {
|
|
20
|
+
"types": "./browser.d.ts",
|
|
21
|
+
"default": "./dist/browser.js"
|
|
22
|
+
}
|
|
9
23
|
},
|
|
10
24
|
"files": [
|
|
11
|
-
"
|
|
25
|
+
"dist/",
|
|
26
|
+
"browser.d.ts",
|
|
12
27
|
"index.d.ts",
|
|
13
|
-
"entrys",
|
|
14
|
-
"src",
|
|
15
|
-
"scripts",
|
|
16
28
|
"README.md"
|
|
17
29
|
],
|
|
18
30
|
"scripts": {
|
|
19
|
-
"
|
|
20
|
-
"check": "node --check index.js && node --check entrys/node.js && node --check
|
|
21
|
-
"test": "node --test test/*.test.js"
|
|
31
|
+
"build": "node build.js",
|
|
32
|
+
"check": "node --check index.js && node --check entrys/node.js && node --check entrys/browser.js && node --check build.js && find src -name '*.js' -maxdepth 3 -print0 | xargs -0 -n1 node --check",
|
|
33
|
+
"test": "node --test test/*.test.js",
|
|
34
|
+
"prepublishOnly": "npm run build"
|
|
22
35
|
},
|
|
23
36
|
"engines": {
|
|
24
37
|
"node": ">=18"
|
|
25
38
|
},
|
|
26
39
|
"dependencies": {
|
|
40
|
+
"fast-xml-parser": "^4.5.1",
|
|
27
41
|
"jimp": "^1.6.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"esbuild": "^0.24.2"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
28
48
|
}
|
|
29
49
|
}
|
package/entrys/node.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Launch } from '../src/launch.js';
|
|
2
|
-
import { ApifyKit } from '../src/apify-kit.js';
|
|
3
|
-
import { Device } from '../src/device.js';
|
|
4
|
-
import { Frida } from '../src/frida-client.js';
|
|
5
|
-
import { Share } from '../src/share.js';
|
|
6
|
-
import * as Constants from '../src/constants.js';
|
|
7
|
-
import * as Errors from '../src/errors.js';
|
|
8
|
-
import { Logger } from '../src/logger.js';
|
|
9
|
-
import { Context } from '../src/context.js';
|
|
10
|
-
|
|
11
|
-
// Unified Entry Point
|
|
12
|
-
// 和 playwright-toolkit 的 usePlaywrightToolKit() 保持同一种入口格式:
|
|
13
|
-
// visitor/androider 只从这里取模块,不直接穿透到内部文件。
|
|
14
|
-
export const useAndroidToolKit = () => {
|
|
15
|
-
return {
|
|
16
|
-
Launch,
|
|
17
|
-
ApifyKit,
|
|
18
|
-
Device,
|
|
19
|
-
Frida,
|
|
20
|
-
Share,
|
|
21
|
-
Constants,
|
|
22
|
-
Errors,
|
|
23
|
-
Logger,
|
|
24
|
-
Context
|
|
25
|
-
};
|
|
26
|
-
};
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useAndroidToolKit } from './entrys/node.js';
|
package/scripts/postinstall.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import {execFileSync, spawnSync} from 'node:child_process';
|
|
4
|
-
|
|
5
|
-
log('检查 Android toolkit 运行环境');
|
|
6
|
-
checkAdb();
|
|
7
|
-
installOrCheckFrida();
|
|
8
|
-
|
|
9
|
-
function checkAdb() {
|
|
10
|
-
const adb = findCommand('adb');
|
|
11
|
-
if (adb) {
|
|
12
|
-
log(`已找到 adb: ${adb}`);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
warn(`未找到 adb。请安装 Android platform-tools,并确保 \`adb\` 在 PATH 中。${installHintForAdb()}`);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function installOrCheckFrida() {
|
|
19
|
-
const frida = findCommand('frida');
|
|
20
|
-
if (frida) {
|
|
21
|
-
log(`已找到 frida: ${frida}`);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const pip3 = findCommand('pip3');
|
|
26
|
-
const python3 = findCommand('python3');
|
|
27
|
-
if (!pip3 || !python3) {
|
|
28
|
-
warn(`未找到 frida,且当前机器缺少 python3/pip3,无法自动安装 frida-tools。${installHintForFrida()}`);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
log('尝试自动安装 frida-tools 到用户目录');
|
|
34
|
-
execFileSync(python3, ['-m', 'pip', 'install', '--user', '--upgrade', 'frida-tools'], {stdio: 'inherit'});
|
|
35
|
-
const installedFrida = findCommand('frida');
|
|
36
|
-
if (installedFrida) {
|
|
37
|
-
log(`frida 安装成功: ${installedFrida}`);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
warn('frida-tools 已安装,但 `frida` 仍不在 PATH 中。请把 Python user bin 加入 PATH。');
|
|
41
|
-
return;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
warn(`自动安装 frida-tools 失败: ${error.message || String(error)}`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
warn(`未能自动准备 frida,请手动确认 \`frida --version\` 可执行。${installHintForFrida()}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function findCommand(command) {
|
|
50
|
-
const result = spawnSync('which', [command], {encoding: 'utf8'});
|
|
51
|
-
return result.status === 0 ? String(result.stdout || '').trim() : '';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function installHintForAdb() {
|
|
55
|
-
if (process.platform === 'darwin') return ' macOS 可用 `brew install android-platform-tools`。';
|
|
56
|
-
if (process.platform === 'linux') return ' Linux 可安装 Android platform-tools,或用系统包管理器安装 adb(如 apt/dnf/pacman/apk)。';
|
|
57
|
-
return '';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function installHintForFrida() {
|
|
61
|
-
if (process.platform === 'darwin') return ' macOS 也可用 `python3 -m pip install --user frida-tools`。';
|
|
62
|
-
if (process.platform === 'linux') return ' Linux 也可用 `python3 -m pip install --user frida-tools`。';
|
|
63
|
-
return '';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function log(message) {
|
|
67
|
-
console.log(`[android-toolkit] ${message}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function warn(message) {
|
|
71
|
-
console.warn(`[android-toolkit] ${message}`);
|
|
72
|
-
}
|