@bryce3310687963/android-toolkit 1.0.51 → 1.0.56
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 +27 -0
- package/dist/index.cjs +37 -5
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +37 -5
- package/dist/index.js.map +3 -3
- package/index.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,6 +82,12 @@ Excluded:
|
|
|
82
82
|
|
|
83
83
|
## Clear App Cache
|
|
84
84
|
|
|
85
|
+
`Device.writeCacheFile(ctx, { dirName, fileName, text })` writes text directly to
|
|
86
|
+
`/data/data/<package>/cache/<dirName>/<fileName>` with root, sets app ownership,
|
|
87
|
+
and returns the device path. Its ADB command arguments and raw command errors
|
|
88
|
+
are redacted, including retries, so file contents do not enter logs or error
|
|
89
|
+
reports. Callers that stage credentials must remove the temporary files after use.
|
|
90
|
+
|
|
85
91
|
`Device.clearAppCache(ctx, packageName = ctx.packageName)` requires root ADB access.
|
|
86
92
|
Stop the app before clearing its cache, then launch it again:
|
|
87
93
|
|
|
@@ -99,3 +105,24 @@ WebView cookies, and login data are outside the cleanup scope. Missing cache
|
|
|
99
105
|
directories are skipped; invalid package names and cleanup errors fail the call.
|
|
100
106
|
Each actor chooses whether to call this method; `Launch.run` does not clear caches
|
|
101
107
|
automatically.
|
|
108
|
+
|
|
109
|
+
## Reset Rebuildable App Databases
|
|
110
|
+
|
|
111
|
+
`DeviceSQLite.reset(ctx, { dbNamePrefix })` requires root ADB access and a stopped
|
|
112
|
+
app. It removes regular files beginning with the explicitly supplied prefix from
|
|
113
|
+
`/data/data/<package>/databases`, including their WAL, SHM, and journal files.
|
|
114
|
+
The app recreates those databases when launched. This resets entire matching
|
|
115
|
+
databases, not individual table rows; use it only for databases the app can rebuild.
|
|
116
|
+
Other databases, preferences, files, and login configuration are preserved.
|
|
117
|
+
Missing databases are skipped. Invalid targets, symbolic links, unexpected
|
|
118
|
+
directories, and deletion errors fail the call.
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
await Device.forceStopApp(ctx);
|
|
122
|
+
await Device.clearAppCache(ctx);
|
|
123
|
+
await DeviceSQLite.reset(ctx, { dbNamePrefix: 'chat_database_' });
|
|
124
|
+
await Device.startLauncher(ctx);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Database prefixes belong in actor code. Local resets do not delete cloud history;
|
|
128
|
+
the app may download old conversations again after launching.
|
package/dist/index.cjs
CHANGED
|
@@ -912,9 +912,16 @@ async function adbExec(ctx, args, options = {}) {
|
|
|
912
912
|
const startedAt = Date.now();
|
|
913
913
|
try {
|
|
914
914
|
const { stdout } = await execAdbWithRetry(adbPath, args, options);
|
|
915
|
-
Logger.useTemplate().adbExec({ duration: Logger.duration(startedAt), args: compactArgs(args) });
|
|
915
|
+
Logger.useTemplate().adbExec({ duration: Logger.duration(startedAt), args: options.sensitive ? "[REDACTED]" : compactArgs(args) });
|
|
916
916
|
return stdout;
|
|
917
917
|
} catch (error) {
|
|
918
|
+
if (options.sensitive) {
|
|
919
|
+
throw new CrawlerError({
|
|
920
|
+
message: "ADB command failed (sensitive content omitted)",
|
|
921
|
+
code: isAdbUnavailableError(error) ? Code.AdbUnavailable : Code.AutomationFailed,
|
|
922
|
+
context: { adbPath }
|
|
923
|
+
});
|
|
924
|
+
}
|
|
918
925
|
if (isAdbUnavailableError(error)) {
|
|
919
926
|
throw new CrawlerError({
|
|
920
927
|
message: `adb_unavailable: ${error?.message || String(error)}`,
|
|
@@ -943,7 +950,8 @@ async function writeCacheFile(ctx, options = {}) {
|
|
|
943
950
|
].join(" && ");
|
|
944
951
|
await Device.adbShell(ctx, [`su -c ${shellQuoteForRemote(command)}`], {
|
|
945
952
|
timeoutMs: Number(options.timeoutMs || 1e4),
|
|
946
|
-
maxBuffer: Number(options.maxBuffer || 1024)
|
|
953
|
+
maxBuffer: Number(options.maxBuffer || 1024),
|
|
954
|
+
sensitive: true
|
|
947
955
|
});
|
|
948
956
|
return filePath;
|
|
949
957
|
}
|
|
@@ -1267,8 +1275,8 @@ async function execAdbWithRetry(adbPath, args, options = {}) {
|
|
|
1267
1275
|
label: options.label || "adb",
|
|
1268
1276
|
attempt,
|
|
1269
1277
|
attempts,
|
|
1270
|
-
args: compactArgs(args),
|
|
1271
|
-
message: error?.message || String(error)
|
|
1278
|
+
args: options.sensitive ? "[REDACTED]" : compactArgs(args),
|
|
1279
|
+
message: options.sensitive ? "sensitive ADB command failed" : error?.message || String(error)
|
|
1272
1280
|
});
|
|
1273
1281
|
await sleep(delayMs * attempt);
|
|
1274
1282
|
}
|
|
@@ -1965,8 +1973,32 @@ var import_node_util2 = require("node:util");
|
|
|
1965
1973
|
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
1966
1974
|
var DeviceSQLite = {
|
|
1967
1975
|
query,
|
|
1968
|
-
health
|
|
1976
|
+
health,
|
|
1977
|
+
reset
|
|
1969
1978
|
};
|
|
1979
|
+
async function reset(ctx, options = {}) {
|
|
1980
|
+
const packageName = requireNonEmpty2(ctx?.packageName, "packageName");
|
|
1981
|
+
const dbNamePrefix = requireNonEmpty2(options.dbNamePrefix, "dbNamePrefix");
|
|
1982
|
+
if (!/^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)+$/.test(packageName) || !/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(dbNamePrefix)) {
|
|
1983
|
+
throw new CrawlerError({ message: "invalid_request: invalid database reset target", code: Code.InvalidRequest });
|
|
1984
|
+
}
|
|
1985
|
+
const command = [
|
|
1986
|
+
"set -e",
|
|
1987
|
+
`db_dir=${shellQuote(`/data/data/${packageName}/databases`)}`,
|
|
1988
|
+
'if [ -L "$db_dir" ]; then echo "refusing symlink database directory" >&2; exit 1; fi',
|
|
1989
|
+
'if [ ! -d "$db_dir" ]; then exit 0; fi',
|
|
1990
|
+
`for db_file in "$db_dir"/${shellQuote(dbNamePrefix)}*; do`,
|
|
1991
|
+
' [ -e "$db_file" ] || [ -L "$db_file" ] || continue',
|
|
1992
|
+
' if [ -L "$db_file" ] || [ ! -f "$db_file" ]; then echo "refusing unexpected database entry: $db_file" >&2; exit 1; fi',
|
|
1993
|
+
"done",
|
|
1994
|
+
// 主库和 WAL/SHM/journal 一起删除,避免旧日志在重建时恢复聊天记录。
|
|
1995
|
+
`rm -f -- "$db_dir"/${shellQuote(dbNamePrefix)}*`
|
|
1996
|
+
].join("\n");
|
|
1997
|
+
await adbSuShell(ctx, command, { timeoutMs: 3e4, maxBuffer: 4096 }).catch((error) => {
|
|
1998
|
+
throw CrawlerError.from(error, { code: Code.AutomationFailed, context: { packageName, dbNamePrefix } });
|
|
1999
|
+
});
|
|
2000
|
+
Logger.info("DeviceSQLite.reset", { packageName, dbNamePrefix });
|
|
2001
|
+
}
|
|
1970
2002
|
async function query(ctx, options = {}) {
|
|
1971
2003
|
if (!options.sql) {
|
|
1972
2004
|
throw new CrawlerError({
|