@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 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({