@skrillex1224/android-toolkit 1.0.7 → 1.0.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/browser.js CHANGED
@@ -25,7 +25,7 @@ var Code = Object.freeze({
25
25
  ContentUnavailable: 30010003,
26
26
  SourceExtractionFailed: 30010004,
27
27
  AutomationFailed: 30010005,
28
- DataAccessUnavailable: 30010008,
28
+ AppRuntimeUnavailable: 30010008,
29
29
  AppNotInstalled: 30010009
30
30
  });
31
31
  var Status = Object.freeze({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/constants.js", "../src/errors.js", "../entrys/browser.js"],
4
- "sourcesContent": ["export const Code = Object.freeze({\n Success: 0,\n UnknownError: -1,\n NotLogin: 30000001,\n Timeout: 30000003,\n InitialTimeout: 30000004,\n OverallTimeout: 30000005,\n InvalidRequest: 30010001,\n AdbUnavailable: 30010002,\n ContentUnavailable: 30010003,\n SourceExtractionFailed: 30010004,\n AutomationFailed: 30010005,\n DataAccessUnavailable: 30010008,\n AppNotInstalled: 30010009,\n});\n\nexport const Status = Object.freeze({\n Success: 'SUCCESS',\n Failed: 'FAILED',\n});\n\nexport const UnicodeIme = Object.freeze({\n packageName: 'io.appium.settings',\n component: 'io.appium.settings/.UnicodeIME',\n inputAction: 'ADB_INPUT_TEXT',\n});\n\nexport const AppiumSettings = Object.freeze({\n packageName: 'io.appium.settings',\n clipboardReceiver: 'io.appium.settings/.receivers.ClipboardReceiver',\n clipboardGetAction: 'io.appium.settings.clipboard.get',\n});\n\nconst normalizeShare = (share) => {\n const source = share && typeof share === 'object' ? share : {};\n return {\n mode: 'clipboard',\n prefix: String(source.prefix || '').trim(),\n xurl: Array.isArray(source.xurl) ? source.xurl : [],\n };\n};\n\nconst createActorInfo = (info) => {\n const key = String(info.key || '').trim();\n return Object.freeze({\n key,\n name: String(info.name || key),\n icon: String(info.icon || `https://static.heartbitai.com/general/actors/${key}.png`),\n share: Object.freeze(normalizeShare(info.share)),\n });\n};\n\nexport const ActorInfo = Object.freeze({\n 'doubao.android': createActorInfo({\n key: 'doubao.android',\n name: '\u8C46\u5305 Android',\n share: {\n prefix: 'https://www.doubao.com/thread/',\n xurl: [],\n },\n }),\n});\n", "import { Code } from './constants.js';\n\nexport class CrawlerError extends Error {\n constructor(input = {}, options = {}) {\n const payload = typeof input === 'string' ? { message: input } : input;\n super(payload.message || 'Android crawler error', options);\n this.name = 'CrawlerError';\n this.code = payload.code || Code.UnknownError;\n this.context = payload.context || {};\n }\n\n static isCrawlerError(error) {\n return error instanceof CrawlerError || error?.name === 'CrawlerError';\n }\n\n static from(error, fallback = {}) {\n if (CrawlerError.isCrawlerError(error)) return error;\n return new CrawlerError({\n message: error?.message || String(error),\n code: fallback.code || Code.UnknownError,\n context: fallback.context || {},\n });\n }\n}\n\nexport function serializeError(error) {\n if (!error) return { message: '' };\n return {\n name: error.name || 'Error',\n message: error.message || String(error),\n stack: error.stack || '',\n code: error.code || '',\n context: error.context || undefined,\n };\n}\n", "import * as Constants from '../src/constants.js';\nimport * as Errors from '../src/errors.js';\n\nconst ToolkitMode = Object.freeze({\n default: 'default',\n});\n\nexport const useAndroidToolKit = () => {\n return {\n Constants,\n Errors,\n };\n};\n\nuseAndroidToolKit.Mode = ToolkitMode;\n"],
4
+ "sourcesContent": ["export const Code = Object.freeze({\n Success: 0,\n UnknownError: -1,\n NotLogin: 30000001,\n Timeout: 30000003,\n InitialTimeout: 30000004,\n OverallTimeout: 30000005,\n InvalidRequest: 30010001,\n AdbUnavailable: 30010002,\n ContentUnavailable: 30010003,\n SourceExtractionFailed: 30010004,\n AutomationFailed: 30010005,\n AppRuntimeUnavailable: 30010008,\n AppNotInstalled: 30010009,\n});\n\nexport const Status = Object.freeze({\n Success: 'SUCCESS',\n Failed: 'FAILED',\n});\n\nexport const UnicodeIme = Object.freeze({\n packageName: 'io.appium.settings',\n component: 'io.appium.settings/.UnicodeIME',\n inputAction: 'ADB_INPUT_TEXT',\n});\n\nexport const AppiumSettings = Object.freeze({\n packageName: 'io.appium.settings',\n clipboardReceiver: 'io.appium.settings/.receivers.ClipboardReceiver',\n clipboardGetAction: 'io.appium.settings.clipboard.get',\n});\n\nconst normalizeShare = (share) => {\n const source = share && typeof share === 'object' ? share : {};\n return {\n mode: 'clipboard',\n prefix: String(source.prefix || '').trim(),\n xurl: Array.isArray(source.xurl) ? source.xurl : [],\n };\n};\n\nconst createActorInfo = (info) => {\n const key = String(info.key || '').trim();\n return Object.freeze({\n key,\n name: String(info.name || key),\n icon: String(info.icon || `https://static.heartbitai.com/general/actors/${key}.png`),\n share: Object.freeze(normalizeShare(info.share)),\n });\n};\n\nexport const ActorInfo = Object.freeze({\n 'doubao.android': createActorInfo({\n key: 'doubao.android',\n name: '\u8C46\u5305 Android',\n share: {\n prefix: 'https://www.doubao.com/thread/',\n xurl: [],\n },\n }),\n});\n", "import { Code } from './constants.js';\n\nexport class CrawlerError extends Error {\n constructor(input = {}, options = {}) {\n const payload = typeof input === 'string' ? { message: input } : input;\n super(payload.message || 'Android crawler error', options);\n this.name = 'CrawlerError';\n this.code = payload.code || Code.UnknownError;\n this.context = payload.context || {};\n }\n\n static isCrawlerError(error) {\n return error instanceof CrawlerError || error?.name === 'CrawlerError';\n }\n\n static from(error, fallback = {}) {\n if (CrawlerError.isCrawlerError(error)) return error;\n return new CrawlerError({\n message: error?.message || String(error),\n code: fallback.code || Code.UnknownError,\n context: fallback.context || {},\n });\n }\n}\n\nexport function serializeError(error) {\n if (!error) return { message: '' };\n return {\n name: error.name || 'Error',\n message: error.message || String(error),\n stack: error.stack || '',\n code: error.code || '',\n context: error.context || undefined,\n };\n}\n", "import * as Constants from '../src/constants.js';\nimport * as Errors from '../src/errors.js';\n\nconst ToolkitMode = Object.freeze({\n default: 'default',\n});\n\nexport const useAndroidToolKit = () => {\n return {\n Constants,\n Errors,\n };\n};\n\nuseAndroidToolKit.Mode = ToolkitMode;\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/index.cjs CHANGED
@@ -57,7 +57,7 @@ var Code = Object.freeze({
57
57
  ContentUnavailable: 30010003,
58
58
  SourceExtractionFailed: 30010004,
59
59
  AutomationFailed: 30010005,
60
- DataAccessUnavailable: 30010008,
60
+ AppRuntimeUnavailable: 30010008,
61
61
  AppNotInstalled: 30010009
62
62
  });
63
63
  var Status = Object.freeze({
@@ -1037,7 +1037,10 @@ async function pressEnter2(ctx) {
1037
1037
  }
1038
1038
  async function scroll(ctx, selector, direction, options = {}) {
1039
1039
  const node = await DeviceView.find(ctx, selector, options);
1040
- const box = node.bounds;
1040
+ await scrollBounds(ctx, node.bounds, direction, options);
1041
+ }
1042
+ async function scrollBounds(ctx, bounds2, direction, options = {}) {
1043
+ const box = bounds2;
1041
1044
  const ratio = Number(options.swipeRatio || 0.72);
1042
1045
  const durationMs = Number(options.durationMs || 360);
1043
1046
  const x = box.centerX;
@@ -1064,6 +1067,9 @@ async function scrollUntilStable(ctx, selector, direction, options = {}) {
1064
1067
  const maxSwipes = Math.max(1, Number(options.maxSwipes || 20));
1065
1068
  const stableRoundsTarget = Math.max(1, Number(options.stableRounds || 2));
1066
1069
  const hashSource = String(options.stability || options.hashSource || "view");
1070
+ if (hashSource === "screenshot") {
1071
+ return scrollUntilScreenshotStable(ctx, selector, direction, options, maxSwipes, stableRoundsTarget, hashSource);
1072
+ }
1067
1073
  let lastHash = "";
1068
1074
  let stableRounds = 0;
1069
1075
  for (let index = 0; index < maxSwipes; index += 1) {
@@ -1071,7 +1077,7 @@ async function scrollUntilStable(ctx, selector, direction, options = {}) {
1071
1077
  if (currentHash === lastHash) {
1072
1078
  stableRounds += 1;
1073
1079
  if (stableRounds >= stableRoundsTarget) {
1074
- return { swipes: index, stableRounds, hash: currentHash, hashSource };
1080
+ return { swipes: index, stableRounds, hash: currentHash, hashSource, capped: false };
1075
1081
  }
1076
1082
  } else {
1077
1083
  stableRounds = 0;
@@ -1079,7 +1085,27 @@ async function scrollUntilStable(ctx, selector, direction, options = {}) {
1079
1085
  }
1080
1086
  await scroll(ctx, selector, direction, options);
1081
1087
  }
1082
- return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource };
1088
+ return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource, capped: true };
1089
+ }
1090
+ async function scrollUntilScreenshotStable(ctx, selector, direction, options, maxSwipes, stableRoundsTarget, hashSource) {
1091
+ const node = await DeviceView.find(ctx, selector, options);
1092
+ const bounds2 = node.bounds;
1093
+ let lastHash = await scrollStateHash(ctx, selector, options, hashSource);
1094
+ let stableRounds = 0;
1095
+ for (let index = 0; index < maxSwipes; index += 1) {
1096
+ await scrollBounds(ctx, bounds2, direction, options);
1097
+ const currentHash = await scrollStateHash(ctx, selector, options, hashSource);
1098
+ if (currentHash === lastHash) {
1099
+ stableRounds += 1;
1100
+ if (stableRounds >= stableRoundsTarget) {
1101
+ return { swipes: index + 1, stableRounds, hash: currentHash, hashSource, capped: false };
1102
+ }
1103
+ } else {
1104
+ stableRounds = 0;
1105
+ lastHash = currentHash;
1106
+ }
1107
+ }
1108
+ return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource, capped: true };
1083
1109
  }
1084
1110
  async function scrollStateHash(ctx, selector, options, hashSource) {
1085
1111
  if (hashSource === "screenshot") {
@@ -1164,8 +1190,8 @@ async function query(ctx, options = {}) {
1164
1190
  const dbPaths = await resolveDeviceDbPaths(ctx, config);
1165
1191
  if (dbPaths.length === 0) {
1166
1192
  throw new CrawlerError({
1167
- message: "data_access_unavailable: sqlite database not found",
1168
- code: Code.DataAccessUnavailable,
1193
+ message: "app_runtime_unavailable: sqlite database not found",
1194
+ code: Code.AppRuntimeUnavailable,
1169
1195
  context: { dbDir: config.dbDir, dbPath: config.dbPath, dbNamePrefix: config.dbNamePrefix }
1170
1196
  });
1171
1197
  }
@@ -1214,8 +1240,8 @@ async function resolveDeviceDbPaths(ctx, config) {
1214
1240
  maxBuffer: 2 * 1024 * 1024
1215
1241
  }).catch((error) => {
1216
1242
  throw new CrawlerError({
1217
- message: `data_access_unavailable: sqlite dbDir unavailable ${error?.message || String(error)}`,
1218
- code: Code.DataAccessUnavailable,
1243
+ message: `app_runtime_unavailable: sqlite dbDir unavailable ${error?.message || String(error)}`,
1244
+ code: Code.AppRuntimeUnavailable,
1219
1245
  context: { dbDir: config.dbDir }
1220
1246
  });
1221
1247
  });
@@ -1240,8 +1266,8 @@ async function pullDatabaseSnapshot(ctx, dbPath, localDir) {
1240
1266
  await adbSuShell(ctx, copyCommand, { timeoutMs: 3e4, maxBuffer: 4 * 1024 * 1024 });
1241
1267
  await adbPull(adbPath, serial, remoteDb, localDb).catch((error) => {
1242
1268
  throw new CrawlerError({
1243
- message: `data_access_unavailable: sqlite snapshot pull failed ${error?.message || String(error)}`,
1244
- code: Code.DataAccessUnavailable,
1269
+ message: `app_runtime_unavailable: sqlite snapshot pull failed ${error?.message || String(error)}`,
1270
+ code: Code.AppRuntimeUnavailable,
1245
1271
  context: { dbPath }
1246
1272
  });
1247
1273
  });
@@ -1700,17 +1726,28 @@ async function captureScreen(ctx, options = {}) {
1700
1726
  const frameBuffers = [];
1701
1727
  const seen = /* @__PURE__ */ new Set();
1702
1728
  const targetSelector = options.selector || { id: "message_list" };
1703
- await Device.hideKeyboard(ctx, { attempts: 2, settleMs: 350 }).catch(() => {
1729
+ const shouldHideKeyboard = options.hideKeyboard !== false;
1730
+ if (shouldHideKeyboard) await Device.hideKeyboard(ctx, { attempts: 2, settleMs: 350 }).catch(() => {
1704
1731
  });
1705
- await DeviceInput.scrollToTop(ctx, targetSelector, {
1706
- maxSwipes: Number(options.scrollToTopMaxSwipes || 12),
1707
- stableRounds: 2,
1708
- settleMs: 400
1709
- }).catch((error) => {
1732
+ try {
1733
+ const topResult = await DeviceInput.scrollToTop(ctx, targetSelector, {
1734
+ maxSwipes: Number(options.scrollToTopMaxSwipes || 12),
1735
+ stableRounds: Number(options.scrollToTopStableRounds || 2),
1736
+ settleMs: 400,
1737
+ stability: options.scrollStability || options.stability || "view"
1738
+ });
1739
+ Logger.info("captureScreen scrollToTop", {
1740
+ swipes: topResult?.swipes,
1741
+ stableRounds: topResult?.stableRounds,
1742
+ hashSource: topResult?.hashSource,
1743
+ capped: topResult?.capped === true
1744
+ });
1745
+ } catch (error) {
1746
+ if (options.strictScrollToTop === true) throw error;
1710
1747
  Logger.warn("captureScreen scrollToTop skipped", { message: error?.message || String(error) });
1711
- });
1748
+ }
1712
1749
  for (let index = 0; index < DEFAULT_CAPTURE_COUNT; index += 1) {
1713
- await Device.hideKeyboard(ctx, { attempts: 1, settleMs: 250 }).catch(() => {
1750
+ if (shouldHideKeyboard) await Device.hideKeyboard(ctx, { attempts: 1, settleMs: 250 }).catch(() => {
1714
1751
  });
1715
1752
  await sleep(DEFAULT_SETTLE_MS);
1716
1753
  const png = await Device.screenshotPng(ctx);