swipium 1.0.0
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/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/appMap/automationLink.js +74 -0
- package/dist/appMap/automationLink.js.map +1 -0
- package/dist/appMap/build.js +193 -0
- package/dist/appMap/build.js.map +1 -0
- package/dist/appMap/codeIndex.js +73 -0
- package/dist/appMap/codeIndex.js.map +1 -0
- package/dist/appMap/featureModel.js +83 -0
- package/dist/appMap/featureModel.js.map +1 -0
- package/dist/appMap/firstRunApply.js +131 -0
- package/dist/appMap/firstRunApply.js.map +1 -0
- package/dist/appMap/fsWalk.js +75 -0
- package/dist/appMap/fsWalk.js.map +1 -0
- package/dist/appMap/issues.js +227 -0
- package/dist/appMap/issues.js.map +1 -0
- package/dist/appMap/migrations.js +87 -0
- package/dist/appMap/migrations.js.map +1 -0
- package/dist/appMap/prelaunch.js +66 -0
- package/dist/appMap/prelaunch.js.map +1 -0
- package/dist/appMap/projectRegistry.js +70 -0
- package/dist/appMap/projectRegistry.js.map +1 -0
- package/dist/appMap/provenance.js +52 -0
- package/dist/appMap/provenance.js.map +1 -0
- package/dist/appMap/query.js +118 -0
- package/dist/appMap/query.js.map +1 -0
- package/dist/appMap/runtimeMerge.js +207 -0
- package/dist/appMap/runtimeMerge.js.map +1 -0
- package/dist/appMap/schema.js +65 -0
- package/dist/appMap/schema.js.map +1 -0
- package/dist/appMap/screenMatch.js +75 -0
- package/dist/appMap/screenMatch.js.map +1 -0
- package/dist/appMap/staticScan.js +614 -0
- package/dist/appMap/staticScan.js.map +1 -0
- package/dist/appMap/store.js +173 -0
- package/dist/appMap/store.js.map +1 -0
- package/dist/appMap/tsAstScan.js +184 -0
- package/dist/appMap/tsAstScan.js.map +1 -0
- package/dist/artifacts/bundletool.js +199 -0
- package/dist/artifacts/bundletool.js.map +1 -0
- package/dist/artifacts/resolve.js +388 -0
- package/dist/artifacts/resolve.js.map +1 -0
- package/dist/automationGen/appiumModel.js +129 -0
- package/dist/automationGen/appiumModel.js.map +1 -0
- package/dist/automationGen/ciEmitter.js +80 -0
- package/dist/automationGen/ciEmitter.js.map +1 -0
- package/dist/automationGen/jsEmitter.js +440 -0
- package/dist/automationGen/jsEmitter.js.map +1 -0
- package/dist/automationGen/packagePatch.js +56 -0
- package/dist/automationGen/packagePatch.js.map +1 -0
- package/dist/automationGen/projectProfile.js +317 -0
- package/dist/automationGen/projectProfile.js.map +1 -0
- package/dist/automationGen/pythonEmitter.js +386 -0
- package/dist/automationGen/pythonEmitter.js.map +1 -0
- package/dist/automationGen/readmeEmitter.js +66 -0
- package/dist/automationGen/readmeEmitter.js.map +1 -0
- package/dist/automationGen/suitePlan.js +94 -0
- package/dist/automationGen/suitePlan.js.map +1 -0
- package/dist/automationGen/validation.js +150 -0
- package/dist/automationGen/validation.js.map +1 -0
- package/dist/build/parseBuildLog.js +119 -0
- package/dist/build/parseBuildLog.js.map +1 -0
- package/dist/build/plan.js +150 -0
- package/dist/build/plan.js.map +1 -0
- package/dist/ci/preflight.js +85 -0
- package/dist/ci/preflight.js.map +1 -0
- package/dist/cli/init.js +105 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/scan.js +85 -0
- package/dist/cli/scan.js.map +1 -0
- package/dist/cli/suite.js +85 -0
- package/dist/cli/suite.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/consent/consent.js +43 -0
- package/dist/consent/consent.js.map +1 -0
- package/dist/context/detect.js +156 -0
- package/dist/context/detect.js.map +1 -0
- package/dist/context/findApps.js +198 -0
- package/dist/context/findApps.js.map +1 -0
- package/dist/context/projectRoot.js +41 -0
- package/dist/context/projectRoot.js.map +1 -0
- package/dist/context/scan.js +167 -0
- package/dist/context/scan.js.map +1 -0
- package/dist/core/target.js +115 -0
- package/dist/core/target.js.map +1 -0
- package/dist/core/targetPlan.js +140 -0
- package/dist/core/targetPlan.js.map +1 -0
- package/dist/drivers/DirectDriver.js +228 -0
- package/dist/drivers/DirectDriver.js.map +1 -0
- package/dist/drivers/Driver.js +4 -0
- package/dist/drivers/Driver.js.map +1 -0
- package/dist/drivers/SimctlDriver.js +127 -0
- package/dist/drivers/SimctlDriver.js.map +1 -0
- package/dist/drivers/WdaDriver.js +250 -0
- package/dist/drivers/WdaDriver.js.map +1 -0
- package/dist/explore/candidates.js +93 -0
- package/dist/explore/candidates.js.map +1 -0
- package/dist/explore/graph.js +116 -0
- package/dist/explore/graph.js.map +1 -0
- package/dist/explore/memory.js +35 -0
- package/dist/explore/memory.js.map +1 -0
- package/dist/explore/planner.js +105 -0
- package/dist/explore/planner.js.map +1 -0
- package/dist/explore/policy.js +85 -0
- package/dist/explore/policy.js.map +1 -0
- package/dist/explore/runner.js +475 -0
- package/dist/explore/runner.js.map +1 -0
- package/dist/explore/signatures.js +35 -0
- package/dist/explore/signatures.js.map +1 -0
- package/dist/explore/suite.js +55 -0
- package/dist/explore/suite.js.map +1 -0
- package/dist/featureTesting/executionBootstrap.js +140 -0
- package/dist/featureTesting/executionBootstrap.js.map +1 -0
- package/dist/firstRun/authStateMachine.js +83 -0
- package/dist/firstRun/authStateMachine.js.map +1 -0
- package/dist/firstRun/classifyScreen.js +201 -0
- package/dist/firstRun/classifyScreen.js.map +1 -0
- package/dist/firstRun/firstRunPlanner.js +207 -0
- package/dist/firstRun/firstRunPlanner.js.map +1 -0
- package/dist/firstRun/firstRunRunner.js +331 -0
- package/dist/firstRun/firstRunRunner.js.map +1 -0
- package/dist/firstRun/generatedDataPolicy.js +215 -0
- package/dist/firstRun/generatedDataPolicy.js.map +1 -0
- package/dist/firstRun/inputPlanner.js +67 -0
- package/dist/firstRun/inputPlanner.js.map +1 -0
- package/dist/firstRun/onboardingStateMachine.js +46 -0
- package/dist/firstRun/onboardingStateMachine.js.map +1 -0
- package/dist/firstRun/paywallPolicy.js +54 -0
- package/dist/firstRun/paywallPolicy.js.map +1 -0
- package/dist/firstRun/types.js +7 -0
- package/dist/firstRun/types.js.map +1 -0
- package/dist/fixtures/catalog.js +131 -0
- package/dist/fixtures/catalog.js.map +1 -0
- package/dist/flows/discover.js +18 -0
- package/dist/flows/discover.js.map +1 -0
- package/dist/flows/generate.js +211 -0
- package/dist/flows/generate.js.map +1 -0
- package/dist/flows/lint.js +97 -0
- package/dist/flows/lint.js.map +1 -0
- package/dist/flows/pack.js +68 -0
- package/dist/flows/pack.js.map +1 -0
- package/dist/flows/run.js +738 -0
- package/dist/flows/run.js.map +1 -0
- package/dist/flows/schema.js +240 -0
- package/dist/flows/schema.js.map +1 -0
- package/dist/flows/seedExec.js +108 -0
- package/dist/flows/seedExec.js.map +1 -0
- package/dist/flows/templates.js +155 -0
- package/dist/flows/templates.js.map +1 -0
- package/dist/flows/waitGuards.js +12 -0
- package/dist/flows/waitGuards.js.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/ios/signing.js +182 -0
- package/dist/ios/signing.js.map +1 -0
- package/dist/issues/classify.js +186 -0
- package/dist/issues/classify.js.map +1 -0
- package/dist/issues/fingerprint.js +130 -0
- package/dist/issues/fingerprint.js.map +1 -0
- package/dist/issues/index.js +240 -0
- package/dist/issues/index.js.map +1 -0
- package/dist/issues/metrics.js +155 -0
- package/dist/issues/metrics.js.map +1 -0
- package/dist/issues/recurrence.js +193 -0
- package/dist/issues/recurrence.js.map +1 -0
- package/dist/issues/report.js +110 -0
- package/dist/issues/report.js.map +1 -0
- package/dist/issues/reportBridge.js +119 -0
- package/dist/issues/reportBridge.js.map +1 -0
- package/dist/issues/schema.js +90 -0
- package/dist/issues/schema.js.map +1 -0
- package/dist/issues/sourceRevision.js +108 -0
- package/dist/issues/sourceRevision.js.map +1 -0
- package/dist/issues/store.js +155 -0
- package/dist/issues/store.js.map +1 -0
- package/dist/lib/android.js +278 -0
- package/dist/lib/android.js.map +1 -0
- package/dist/lib/commandTemplate.js +55 -0
- package/dist/lib/commandTemplate.js.map +1 -0
- package/dist/lib/coordSpace.js +23 -0
- package/dist/lib/coordSpace.js.map +1 -0
- package/dist/lib/device.js +87 -0
- package/dist/lib/device.js.map +1 -0
- package/dist/lib/gitignore.js +22 -0
- package/dist/lib/gitignore.js.map +1 -0
- package/dist/lib/image.js +97 -0
- package/dist/lib/image.js.map +1 -0
- package/dist/lib/logger.js +8 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/metroState.js +45 -0
- package/dist/lib/metroState.js.map +1 -0
- package/dist/lib/needsInput.js +201 -0
- package/dist/lib/needsInput.js.map +1 -0
- package/dist/lib/png.js +103 -0
- package/dist/lib/png.js.map +1 -0
- package/dist/lib/redact.js +21 -0
- package/dist/lib/redact.js.map +1 -0
- package/dist/lib/result.js +87 -0
- package/dist/lib/result.js.map +1 -0
- package/dist/lib/schemaHash.js +65 -0
- package/dist/lib/schemaHash.js.map +1 -0
- package/dist/lib/sensitive.js +13 -0
- package/dist/lib/sensitive.js.map +1 -0
- package/dist/lib/simctl.js +149 -0
- package/dist/lib/simctl.js.map +1 -0
- package/dist/lib/spawn.js +144 -0
- package/dist/lib/spawn.js.map +1 -0
- package/dist/lib/wda.js +347 -0
- package/dist/lib/wda.js.map +1 -0
- package/dist/lib/wdaConfig.js +69 -0
- package/dist/lib/wdaConfig.js.map +1 -0
- package/dist/lib/wdaTune.js +58 -0
- package/dist/lib/wdaTune.js.map +1 -0
- package/dist/oracle/auth.js +22 -0
- package/dist/oracle/auth.js.map +1 -0
- package/dist/oracle/failures.js +193 -0
- package/dist/oracle/failures.js.map +1 -0
- package/dist/oracle/health.js +123 -0
- package/dist/oracle/health.js.map +1 -0
- package/dist/oracle/record.js +36 -0
- package/dist/oracle/record.js.map +1 -0
- package/dist/orchestration/envelope.js +52 -0
- package/dist/orchestration/envelope.js.map +1 -0
- package/dist/orchestration/goal.js +98 -0
- package/dist/orchestration/goal.js.map +1 -0
- package/dist/plan/plan.js +124 -0
- package/dist/plan/plan.js.map +1 -0
- package/dist/prompts/index.js +63 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/report/artifactManifest.js +44 -0
- package/dist/report/artifactManifest.js.map +1 -0
- package/dist/report/coverage.js +60 -0
- package/dist/report/coverage.js.map +1 -0
- package/dist/report/evidence.js +84 -0
- package/dist/report/evidence.js.map +1 -0
- package/dist/report/export.js +447 -0
- package/dist/report/export.js.map +1 -0
- package/dist/report/flake.js +61 -0
- package/dist/report/flake.js.map +1 -0
- package/dist/report/history.js +564 -0
- package/dist/report/history.js.map +1 -0
- package/dist/report/policy.js +98 -0
- package/dist/report/policy.js.map +1 -0
- package/dist/report/qaLevel.js +96 -0
- package/dist/report/qaLevel.js.map +1 -0
- package/dist/report/readiness.js +213 -0
- package/dist/report/readiness.js.map +1 -0
- package/dist/report/summary.js +42 -0
- package/dist/report/summary.js.map +1 -0
- package/dist/report/testCatalog.js +93 -0
- package/dist/report/testCatalog.js.map +1 -0
- package/dist/report/timing.js +37 -0
- package/dist/report/timing.js.map +1 -0
- package/dist/server.js +180 -0
- package/dist/server.js.map +1 -0
- package/dist/services/automationGenerate.js +60 -0
- package/dist/services/automationGenerate.js.map +1 -0
- package/dist/services/build.js +66 -0
- package/dist/services/build.js.map +1 -0
- package/dist/services/preflight.js +51 -0
- package/dist/services/preflight.js.map +1 -0
- package/dist/services/prepareAndroid.js +183 -0
- package/dist/services/prepareAndroid.js.map +1 -0
- package/dist/services/prepareIos.js +162 -0
- package/dist/services/prepareIos.js.map +1 -0
- package/dist/services/report.js +537 -0
- package/dist/services/report.js.map +1 -0
- package/dist/services/smoke.js +120 -0
- package/dist/services/smoke.js.map +1 -0
- package/dist/services/suiteGenerate.js +123 -0
- package/dist/services/suiteGenerate.js.map +1 -0
- package/dist/services/testSuiteKnowledge.js +150 -0
- package/dist/services/testSuiteKnowledge.js.map +1 -0
- package/dist/session/attach.js +47 -0
- package/dist/session/attach.js.map +1 -0
- package/dist/session/progress.js +56 -0
- package/dist/session/progress.js.map +1 -0
- package/dist/session/store.js +329 -0
- package/dist/session/store.js.map +1 -0
- package/dist/snapshot/overlays.js +132 -0
- package/dist/snapshot/overlays.js.map +1 -0
- package/dist/snapshot/parse.js +204 -0
- package/dist/snapshot/parse.js.map +1 -0
- package/dist/snapshot/present.js +12 -0
- package/dist/snapshot/present.js.map +1 -0
- package/dist/snapshot/settle.js +36 -0
- package/dist/snapshot/settle.js.map +1 -0
- package/dist/state/profile.js +318 -0
- package/dist/state/profile.js.map +1 -0
- package/dist/suite/compile.js +176 -0
- package/dist/suite/compile.js.map +1 -0
- package/dist/suite/lint.js +45 -0
- package/dist/suite/lint.js.map +1 -0
- package/dist/suite/pom.js +285 -0
- package/dist/suite/pom.js.map +1 -0
- package/dist/suite/testcase.js +87 -0
- package/dist/suite/testcase.js.map +1 -0
- package/dist/testSuite/generator.js +222 -0
- package/dist/testSuite/generator.js.map +1 -0
- package/dist/testSuite/history.js +44 -0
- package/dist/testSuite/history.js.map +1 -0
- package/dist/testSuite/issueLinks.js +96 -0
- package/dist/testSuite/issueLinks.js.map +1 -0
- package/dist/testSuite/merge.js +190 -0
- package/dist/testSuite/merge.js.map +1 -0
- package/dist/testSuite/schema.js +86 -0
- package/dist/testSuite/schema.js.map +1 -0
- package/dist/testSuite/store.js +116 -0
- package/dist/testSuite/store.js.map +1 -0
- package/dist/testSuite/traceability.js +55 -0
- package/dist/testSuite/traceability.js.map +1 -0
- package/dist/tools/act.js +457 -0
- package/dist/tools/act.js.map +1 -0
- package/dist/tools/agent.js +225 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/appMap.js +317 -0
- package/dist/tools/appMap.js.map +1 -0
- package/dist/tools/assertVisual.js +74 -0
- package/dist/tools/assertVisual.js.map +1 -0
- package/dist/tools/automationGenerate.js +394 -0
- package/dist/tools/automationGenerate.js.map +1 -0
- package/dist/tools/capabilities.js +114 -0
- package/dist/tools/capabilities.js.map +1 -0
- package/dist/tools/clearOverlay.js +150 -0
- package/dist/tools/clearOverlay.js.map +1 -0
- package/dist/tools/detectContext.js +41 -0
- package/dist/tools/detectContext.js.map +1 -0
- package/dist/tools/doctor.js +116 -0
- package/dist/tools/doctor.js.map +1 -0
- package/dist/tools/explore.js +289 -0
- package/dist/tools/explore.js.map +1 -0
- package/dist/tools/firstRun.js +136 -0
- package/dist/tools/firstRun.js.map +1 -0
- package/dist/tools/flow.js +331 -0
- package/dist/tools/flow.js.map +1 -0
- package/dist/tools/flowGenerate.js +55 -0
- package/dist/tools/flowGenerate.js.map +1 -0
- package/dist/tools/getArtifact.js +39 -0
- package/dist/tools/getArtifact.js.map +1 -0
- package/dist/tools/health.js +41 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/ios.js +492 -0
- package/dist/tools/ios.js.map +1 -0
- package/dist/tools/jobs.js +20 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/network.js +50 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/note.js +69 -0
- package/dist/tools/note.js.map +1 -0
- package/dist/tools/plan.js +49 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/prepareIosTarget.js +99 -0
- package/dist/tools/prepareIosTarget.js.map +1 -0
- package/dist/tools/prepareTarget.js +257 -0
- package/dist/tools/prepareTarget.js.map +1 -0
- package/dist/tools/report.js +42 -0
- package/dist/tools/report.js.map +1 -0
- package/dist/tools/screenRecord.js +24 -0
- package/dist/tools/screenRecord.js.map +1 -0
- package/dist/tools/screenshot.js +63 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/smoke.js +39 -0
- package/dist/tools/smoke.js.map +1 -0
- package/dist/tools/snapshot.js +129 -0
- package/dist/tools/snapshot.js.map +1 -0
- package/dist/tools/startSession.js +169 -0
- package/dist/tools/startSession.js.map +1 -0
- package/dist/tools/state.js +87 -0
- package/dist/tools/state.js.map +1 -0
- package/dist/tools/suite.js +412 -0
- package/dist/tools/suite.js.map +1 -0
- package/dist/tools/testThis.js +774 -0
- package/dist/tools/testThis.js.map +1 -0
- package/dist/tools/wda.js +477 -0
- package/dist/tools/wda.js.map +1 -0
- package/dist/version.js +62 -0
- package/dist/version.js.map +1 -0
- package/dist/visual/ocr.js +72 -0
- package/dist/visual/ocr.js.map +1 -0
- package/dist/visual/provider.js +75 -0
- package/dist/visual/provider.js.map +1 -0
- package/docs/README.md +19 -0
- package/docs/assets/swipium-lockup-ink-on-light.png +0 -0
- package/docs/mcp-server.md +135 -0
- package/docs/tools.md +130 -0
- package/package.json +70 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Mobile app discovery for low-context roots (Developer 1, roadmap §3.1 / §3.7). When a developer
|
|
2
|
+
// or agent points Swipium at a monorepo root, a parent directory, or any folder that is not itself
|
|
3
|
+
// a mobile app, this locates the candidate app(s) so `qa_test_this` can either proceed with the one
|
|
4
|
+
// strong candidate (recording a workaround) or ask ONE disambiguation question — instead of failing
|
|
5
|
+
// with NOT_MOBILE_PROJECT at a root that does in fact contain an app.
|
|
6
|
+
//
|
|
7
|
+
// Pure + bounded + side-effect free: it scans a fixed, shallow set of conventional locations and
|
|
8
|
+
// scores each candidate from cheap file markers. It never recurses into node_modules/build output.
|
|
9
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
10
|
+
import { join, relative, basename } from 'node:path';
|
|
11
|
+
import { detectFramework } from './detect.js';
|
|
12
|
+
const SKIP_DIRS = new Set([
|
|
13
|
+
'node_modules', '.git', '.swipium', '.expo', '.gradle', '.idea', '.vscode',
|
|
14
|
+
'build', 'dist', 'out', 'DerivedData', 'Pods', 'vendor', 'coverage', '.next', '.turbo',
|
|
15
|
+
]);
|
|
16
|
+
/** Conventional parents whose immediate subdirectories may each hold a mobile app. */
|
|
17
|
+
const GLOB_PARENTS = ['apps', 'packages', 'mobile', 'examples', 'example', 'clients'];
|
|
18
|
+
/** Conventional direct app locations (relative to root). */
|
|
19
|
+
const DIRECT_DIRS = ['', 'ios', 'android', 'app', 'src-app'];
|
|
20
|
+
function readJson(p) {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(readFileSync(p, 'utf8'));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function safeMtime(p) {
|
|
29
|
+
try {
|
|
30
|
+
return statSync(p).mtimeMs;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function listDirs(dir) {
|
|
37
|
+
try {
|
|
38
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
39
|
+
.filter((e) => e.isDirectory() && !e.name.startsWith('.') && !SKIP_DIRS.has(e.name))
|
|
40
|
+
.map((e) => e.name);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Light, bounded check for a usable app config in `dir` (mirrors the framework signals). */
|
|
47
|
+
function appConfigPresent(dir, fw) {
|
|
48
|
+
switch (fw) {
|
|
49
|
+
case 'expo':
|
|
50
|
+
return ['app.json', 'app.config.js', 'app.config.ts'].some((f) => existsSync(join(dir, f)));
|
|
51
|
+
case 'bare-react-native':
|
|
52
|
+
return existsSync(join(dir, 'package.json'));
|
|
53
|
+
case 'native-android':
|
|
54
|
+
return ['app/build.gradle', 'app/build.gradle.kts', 'settings.gradle', 'settings.gradle.kts'].some((f) => existsSync(join(dir, f)));
|
|
55
|
+
case 'native-ios':
|
|
56
|
+
return readdirSafe(dir).some((f) => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace')) || existsSync(join(dir, 'Package.swift'));
|
|
57
|
+
case 'flutter':
|
|
58
|
+
return existsSync(join(dir, 'pubspec.yaml'));
|
|
59
|
+
default:
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function readdirSafe(dir) {
|
|
64
|
+
try {
|
|
65
|
+
return readdirSync(dir);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Best-effort app id from cheap config markers (no APK badging — that is too heavy here). */
|
|
72
|
+
function lightAppId(dir, fw) {
|
|
73
|
+
if (fw === 'expo') {
|
|
74
|
+
const cfg = readJson(join(dir, 'app.json'));
|
|
75
|
+
const expo = (cfg?.expo ?? cfg);
|
|
76
|
+
const id = expo?.ios?.bundleIdentifier ?? expo?.android?.package;
|
|
77
|
+
if (typeof id === 'string')
|
|
78
|
+
return id;
|
|
79
|
+
}
|
|
80
|
+
for (const g of ['app/build.gradle', 'app/build.gradle.kts', 'android/app/build.gradle', 'android/app/build.gradle.kts']) {
|
|
81
|
+
const p = join(dir, g);
|
|
82
|
+
if (!existsSync(p))
|
|
83
|
+
continue;
|
|
84
|
+
try {
|
|
85
|
+
const m = readFileSync(p, 'utf8').match(/applicationId\s*[=\s]\s*["']([^"']+)["']/);
|
|
86
|
+
if (m)
|
|
87
|
+
return m[1];
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
/* unreadable */
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/** Bounded heuristic for "an installable artifact probably exists here" (existence-only, no walk). */
|
|
96
|
+
function artifactPresent(dir) {
|
|
97
|
+
const candidates = [
|
|
98
|
+
join(dir, 'android', 'app', 'build', 'outputs', 'apk'),
|
|
99
|
+
join(dir, 'app', 'build', 'outputs', 'apk'),
|
|
100
|
+
join(dir, 'build', 'app', 'outputs', 'flutter-apk'),
|
|
101
|
+
join(dir, 'ios', 'build'),
|
|
102
|
+
join(dir, 'apps', 'android'),
|
|
103
|
+
join(dir, 'apps', 'ios'),
|
|
104
|
+
];
|
|
105
|
+
if (candidates.some((p) => existsSync(p)))
|
|
106
|
+
return true;
|
|
107
|
+
// a top-level .apk/.ipa/.app dropped in the dir
|
|
108
|
+
return readdirSafe(dir).some((f) => /\.(apk|aab|ipa)$/i.test(f) || f.endsWith('.app'));
|
|
109
|
+
}
|
|
110
|
+
const LOCKFILES = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'Podfile.lock', 'Gemfile.lock', 'pubspec.lock'];
|
|
111
|
+
const RECENT_MS = 1000 * 60 * 60 * 24 * 60; // 60 days
|
|
112
|
+
/** Score one directory as a mobile-app candidate. Returns null if no framework is recognized. */
|
|
113
|
+
function scoreDir(root, dir, nowMs) {
|
|
114
|
+
const fw = detectFramework(dir);
|
|
115
|
+
if (fw === 'unknown')
|
|
116
|
+
return null;
|
|
117
|
+
const reasons = [`framework=${fw}`];
|
|
118
|
+
let score = 50; // a recognized framework is the dominant signal
|
|
119
|
+
const appConfig = appConfigPresent(dir, fw);
|
|
120
|
+
if (appConfig) {
|
|
121
|
+
score += 15;
|
|
122
|
+
reasons.push('app config present');
|
|
123
|
+
}
|
|
124
|
+
const hasArtifact = artifactPresent(dir);
|
|
125
|
+
if (hasArtifact) {
|
|
126
|
+
score += 20;
|
|
127
|
+
reasons.push('build artifact present');
|
|
128
|
+
}
|
|
129
|
+
const appId = lightAppId(dir, fw);
|
|
130
|
+
if (appId) {
|
|
131
|
+
score += 10;
|
|
132
|
+
reasons.push(`appId=${appId}`);
|
|
133
|
+
}
|
|
134
|
+
if (LOCKFILES.some((f) => existsSync(join(dir, f)))) {
|
|
135
|
+
score += 5;
|
|
136
|
+
reasons.push('lockfile present');
|
|
137
|
+
}
|
|
138
|
+
// recency: prefer the app the developer is actually working on
|
|
139
|
+
const mtimeMs = Math.max(safeMtime(join(dir, 'package.json')), safeMtime(join(dir, 'app.json')), safeMtime(join(dir, 'pubspec.yaml')), safeMtime(dir));
|
|
140
|
+
if (mtimeMs && nowMs - mtimeMs < RECENT_MS) {
|
|
141
|
+
score += 10;
|
|
142
|
+
reasons.push('recently modified');
|
|
143
|
+
}
|
|
144
|
+
return { path: dir, rel: relative(root, dir) || '.', framework: fw, score, reasons, hasArtifact, appConfig, appId, mtimeMs };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Discover mobile-app candidates at or under `root`. Bounded: root + a curated set of conventional
|
|
148
|
+
* locations + the immediate children of apps/packages/mobile/examples. Best-first, deduped by path.
|
|
149
|
+
*
|
|
150
|
+
* `nowMs` is injected (default Date.now) so callers/tests stay deterministic.
|
|
151
|
+
*/
|
|
152
|
+
export function findMobileApps(root, nowMs = Date.now()) {
|
|
153
|
+
const searched = new Set();
|
|
154
|
+
const candidates = new Map();
|
|
155
|
+
const consider = (dir) => {
|
|
156
|
+
if (!dir || searched.has(dir))
|
|
157
|
+
return;
|
|
158
|
+
if (!existsSync(dir))
|
|
159
|
+
return;
|
|
160
|
+
try {
|
|
161
|
+
if (!statSync(dir).isDirectory())
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
searched.add(dir);
|
|
168
|
+
const c = scoreDir(root, dir, nowMs);
|
|
169
|
+
if (c && !candidates.has(c.path))
|
|
170
|
+
candidates.set(c.path, c);
|
|
171
|
+
};
|
|
172
|
+
// 1. direct conventional locations
|
|
173
|
+
for (const rel of DIRECT_DIRS)
|
|
174
|
+
consider(rel ? join(root, rel) : root);
|
|
175
|
+
// 2. immediate children of conventional parents (apps/*, packages/*, mobile/*, examples/*)
|
|
176
|
+
for (const parent of GLOB_PARENTS) {
|
|
177
|
+
const parentDir = join(root, parent);
|
|
178
|
+
for (const name of listDirs(parentDir))
|
|
179
|
+
consider(join(parentDir, name));
|
|
180
|
+
}
|
|
181
|
+
const ranked = [...candidates.values()].sort((a, b) => b.score - a.score || b.mtimeMs - a.mtimeMs || basename(a.path).localeCompare(basename(b.path)));
|
|
182
|
+
return { root, candidates: ranked, searchedLocations: [...searched].sort() };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Decide what `qa_test_this` should do with the discovery result.
|
|
186
|
+
* - 'adopt' → exactly one recognized app; proceed with it.
|
|
187
|
+
* - 'disambiguate' → ≥2 recognized apps; ask one monorepo_target question with ranked choices.
|
|
188
|
+
* - 'none' → no recognized app anywhere searched.
|
|
189
|
+
*/
|
|
190
|
+
export function classifyDiscovery(result) {
|
|
191
|
+
const choices = result.candidates;
|
|
192
|
+
if (choices.length === 0)
|
|
193
|
+
return { decision: 'none', choices };
|
|
194
|
+
if (choices.length === 1)
|
|
195
|
+
return { decision: 'adopt', chosen: choices[0], choices };
|
|
196
|
+
return { decision: 'disambiguate', choices };
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=findApps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findApps.js","sourceRoot":"","sources":["../../src/context/findApps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Resolve the project root WITHOUT trusting the server cwd (DESIGN §2, review #1).
|
|
2
|
+
// Order: explicit arg → MCP roots (the proper mechanism) → needs-input.
|
|
3
|
+
import { existsSync, statSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { isAbsolute } from 'node:path';
|
|
6
|
+
export async function resolveProjectRoot(server, explicit) {
|
|
7
|
+
// 1) explicit arg wins (must be an absolute, existing directory)
|
|
8
|
+
if (explicit && explicit.trim()) {
|
|
9
|
+
const p = explicit.trim();
|
|
10
|
+
if (!isAbsolute(p)) {
|
|
11
|
+
return { source: 'none', hint: `projectRoot must be an absolute path, got "${p}".` };
|
|
12
|
+
}
|
|
13
|
+
if (existsSync(p) && statSync(p).isDirectory()) {
|
|
14
|
+
return { root: p, source: 'arg' };
|
|
15
|
+
}
|
|
16
|
+
return { source: 'none', hint: `Path not found or not a directory: ${p}` };
|
|
17
|
+
}
|
|
18
|
+
// 2) MCP roots (workspace the client exposed)
|
|
19
|
+
try {
|
|
20
|
+
const caps = server.server.getClientCapabilities?.();
|
|
21
|
+
if (caps?.roots) {
|
|
22
|
+
const res = await server.server.listRoots();
|
|
23
|
+
const fileRoot = res.roots?.find((r) => typeof r.uri === 'string' && r.uri.startsWith('file://'));
|
|
24
|
+
if (fileRoot) {
|
|
25
|
+
const p = fileURLToPath(fileRoot.uri);
|
|
26
|
+
if (existsSync(p) && statSync(p).isDirectory()) {
|
|
27
|
+
return { root: p, source: 'mcp-roots' };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// client doesn't support roots, or the call failed — fall through to needs-input
|
|
34
|
+
}
|
|
35
|
+
// 3) ask
|
|
36
|
+
return {
|
|
37
|
+
source: 'none',
|
|
38
|
+
hint: 'No workspace root exposed by the client. Call qa_start_session with projectRoot="/absolute/path/to/app".',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=projectRoot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectRoot.js","sourceRoot":"","sources":["../../src/context/projectRoot.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,wEAAwE;AAExE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AASvC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAiB,EAAE,QAAiB;IAC3E,iEAAiE;IACjE,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACnB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,8CAA8C,CAAC,IAAI,EAAE,CAAC;QACvF,CAAC;QACD,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YAC/C,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACpC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,sCAAsC,CAAC,EAAE,EAAE,CAAC;IAC7E,CAAC;IAED,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,CAAC;QACrD,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;YAClG,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACtC,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;oBAC/C,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;gBAC1C,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iFAAiF;IACnF,CAAC;IAED,SAAS;IACT,OAAO;QACL,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,0GAA0G;KACjH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// scanProject (PHASE3-PLAN §3.1) — the data behind `swipium scan` and `.swipium/config.json`.
|
|
2
|
+
// Extends detectContext() with the higher-level signals a developer needs to answer
|
|
3
|
+
// "what can I test right now?": app id, whether Metro is likely needed, whether a fresh
|
|
4
|
+
// start is safe, whether auth is likely, install state (best-effort), a recommended budget
|
|
5
|
+
// profile, and an overall ready | partial | blocked verdict. Best-effort and side-effect free.
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { detectContext } from './detect.js';
|
|
10
|
+
import { apkPackageId, adbDevices } from '../lib/android.js';
|
|
11
|
+
import { run } from '../lib/spawn.js';
|
|
12
|
+
import { bundleIdFromApp } from '../artifacts/resolve.js';
|
|
13
|
+
const AUTH_DEP = /(^|[/@])(auth|next-auth|expo-auth-session|react-native-app-auth|amazon-cognito|@clerk|supabase|firebase)/i;
|
|
14
|
+
const HARD_BLOCKER = /adb not found|could not identify|no online device|no prebuilt (apk|app artifact|android apk|ios app artifact)|no build artifact/i;
|
|
15
|
+
function isDebugRN(fw) {
|
|
16
|
+
return fw === 'expo' || fw === 'bare-react-native';
|
|
17
|
+
}
|
|
18
|
+
function readJson(p) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(p, 'utf8'));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Best-effort appId from app config files (Expo / Android gradle) before falling back to the APK. */
|
|
27
|
+
function appIdFromSource(root, fw) {
|
|
28
|
+
if (fw === 'expo') {
|
|
29
|
+
const cfg = readJson(join(root, 'app.json'));
|
|
30
|
+
const expo = (cfg?.expo ?? cfg);
|
|
31
|
+
const pkg = expo?.ios?.bundleIdentifier ?? expo?.android?.package;
|
|
32
|
+
if (typeof pkg === 'string')
|
|
33
|
+
return { appId: pkg, source: 'app.json' };
|
|
34
|
+
}
|
|
35
|
+
// gradle applicationId (best-effort regex over the app module's build.gradle[.kts])
|
|
36
|
+
for (const g of ['app/build.gradle', 'app/build.gradle.kts', 'android/app/build.gradle', 'android/app/build.gradle.kts']) {
|
|
37
|
+
const p = join(root, g);
|
|
38
|
+
if (!existsSync(p))
|
|
39
|
+
continue;
|
|
40
|
+
const m = readFileSync(p, 'utf8').match(/applicationId\s*[=\s]\s*["']([^"']+)["']/);
|
|
41
|
+
if (m)
|
|
42
|
+
return { appId: m[1], source: 'gradle' };
|
|
43
|
+
}
|
|
44
|
+
return { appId: null, source: null };
|
|
45
|
+
}
|
|
46
|
+
function detectAuth(root) {
|
|
47
|
+
const pkg = readJson(join(root, 'package.json'));
|
|
48
|
+
const deps = pkg ? { ...pkg.dependencies, ...pkg.devDependencies } : {};
|
|
49
|
+
const signals = Object.keys(deps).filter((d) => AUTH_DEP.test(d));
|
|
50
|
+
return { likely: signals.length > 0, signals };
|
|
51
|
+
}
|
|
52
|
+
/** Is `appId` installed on `serial`? Best-effort; short timeout. */
|
|
53
|
+
async function pmInstalled(serial, appId) {
|
|
54
|
+
try {
|
|
55
|
+
const r = await run('adb', ['-s', serial, 'shell', 'pm', 'list', 'packages', appId], { timeoutMs: 6000 });
|
|
56
|
+
return r.stdout.includes(`package:${appId}`);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function hashArtifact(path) {
|
|
63
|
+
try {
|
|
64
|
+
const h = createHash('sha256');
|
|
65
|
+
const st = statSync(path);
|
|
66
|
+
if (st.isFile()) {
|
|
67
|
+
h.update(readFileSync(path));
|
|
68
|
+
}
|
|
69
|
+
else if (st.isDirectory()) {
|
|
70
|
+
const walk = (dir, prefix = '') => {
|
|
71
|
+
for (const name of readdirSync(dir).sort()) {
|
|
72
|
+
const full = join(dir, name);
|
|
73
|
+
const rel = prefix ? `${prefix}/${name}` : name;
|
|
74
|
+
const child = statSync(full);
|
|
75
|
+
if (child.isDirectory())
|
|
76
|
+
walk(full, rel);
|
|
77
|
+
else if (child.isFile()) {
|
|
78
|
+
h.update(rel);
|
|
79
|
+
h.update('\0');
|
|
80
|
+
h.update(readFileSync(full));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
walk(path);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return `sha256:${h.digest('hex')}`;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function artifactHashes(ctx) {
|
|
96
|
+
const refs = [
|
|
97
|
+
...ctx.artifacts.apks.map((path) => ({ path, type: 'apk' })),
|
|
98
|
+
...ctx.artifacts.ipas.map((path) => ({ path, type: 'ipa' })),
|
|
99
|
+
...ctx.artifacts.appBundles.map((path) => ({ path, type: 'app' })),
|
|
100
|
+
];
|
|
101
|
+
return refs.flatMap((ref) => {
|
|
102
|
+
const artifactHash = hashArtifact(ref.path);
|
|
103
|
+
return artifactHash ? [{ ...ref, artifactHash }] : [];
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export async function scanProject(root) {
|
|
107
|
+
const ctx = await detectContext(root);
|
|
108
|
+
// app id: source config first (authoritative for the project), else the prebuilt APK.
|
|
109
|
+
let { appId, source: appIdSource } = appIdFromSource(root, ctx.framework);
|
|
110
|
+
if (!appId && ctx.artifacts.apks[0]) {
|
|
111
|
+
const fromApk = await apkPackageId(ctx.artifacts.apks[0]);
|
|
112
|
+
if (fromApk) {
|
|
113
|
+
appId = fromApk;
|
|
114
|
+
appIdSource = 'apk-badging';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!appId && ctx.artifacts.appBundles[0]) {
|
|
118
|
+
const fromApp = bundleIdFromApp(ctx.artifacts.appBundles[0]);
|
|
119
|
+
if (fromApp) {
|
|
120
|
+
appId = fromApp;
|
|
121
|
+
appIdSource = 'Info.plist';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const { likely: likelyAuth, signals: authSignals } = detectAuth(root);
|
|
125
|
+
const metroNeed = isDebugRN(ctx.framework) ? 'likely' : 'no';
|
|
126
|
+
const freshStartSafe = !isDebugRN(ctx.framework);
|
|
127
|
+
// install state: only meaningful with exactly one online device + a known appId.
|
|
128
|
+
let installed = null;
|
|
129
|
+
if (appId && ctx.devices.androidOnline.length === 1) {
|
|
130
|
+
installed = await pmInstalled(ctx.devices.androidOnline[0], appId);
|
|
131
|
+
}
|
|
132
|
+
else if (appId && ctx.toolchain.adb && ctx.devices.androidOnline.length === 0) {
|
|
133
|
+
// adbDevices may have changed since detect; one cheap recheck before giving up
|
|
134
|
+
const live = await adbDevices();
|
|
135
|
+
if (live.length === 1)
|
|
136
|
+
installed = await pmInstalled(live[0], appId);
|
|
137
|
+
}
|
|
138
|
+
// missing items: detect blockers, but a known-installed app removes the "no APK" blocker.
|
|
139
|
+
const missing = ctx.blockers.filter((b) => !(installed === true && /no prebuilt apk/i.test(b)));
|
|
140
|
+
const hard = missing.filter((b) => HARD_BLOCKER.test(b) && !(installed === true && /no prebuilt apk/i.test(b)));
|
|
141
|
+
const readiness = hard.length ? 'blocked' : missing.length ? 'partial' : 'ready';
|
|
142
|
+
const needsBootOrInstall = (ctx.devices.androidOnline.length === 0 && ctx.devices.avds.length > 0) || (installed === false && ctx.artifacts.apks.length > 0);
|
|
143
|
+
const recommendedProfile = needsBootOrInstall ? 'install_smoke' : likelyAuth ? 'login_smoke' : 'guardrail';
|
|
144
|
+
return {
|
|
145
|
+
schemaVersion: 1,
|
|
146
|
+
generatedAt: new Date().toISOString(),
|
|
147
|
+
projectRoot: root,
|
|
148
|
+
framework: ctx.framework,
|
|
149
|
+
monorepo: ctx.monorepo,
|
|
150
|
+
appId,
|
|
151
|
+
appIdSource,
|
|
152
|
+
apks: ctx.artifacts.apks,
|
|
153
|
+
ipas: ctx.artifacts.ipas,
|
|
154
|
+
appBundles: ctx.artifacts.appBundles,
|
|
155
|
+
artifactHashes: artifactHashes(ctx),
|
|
156
|
+
metroNeed,
|
|
157
|
+
freshStartSafe,
|
|
158
|
+
likelyAuth,
|
|
159
|
+
authSignals,
|
|
160
|
+
installed,
|
|
161
|
+
recommendedProfile,
|
|
162
|
+
readiness,
|
|
163
|
+
missing,
|
|
164
|
+
devices: ctx.devices,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/context/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Shared target resolution (PHASE3-PLAN §2.3 core extraction). Both qa_act and the flow
|
|
2
|
+
// runner resolve a target the SAME way — extracting it here is the "one IR, no drift" rule:
|
|
3
|
+
// a ref | selector | coords becomes a tappable point with the resolved @eN (for obstruction
|
|
4
|
+
// checks) and secure-field awareness (for redaction). Selector resolution re-snapshots and
|
|
5
|
+
// updates session.lastSnapshot, exactly as before.
|
|
6
|
+
import { parseSnapshot, signature } from '../snapshot/parse.js';
|
|
7
|
+
import { isSecureNode } from '../lib/redact.js';
|
|
8
|
+
import { createHash } from 'node:crypto';
|
|
9
|
+
export function resourceIdAliases(id) {
|
|
10
|
+
const raw = id.trim();
|
|
11
|
+
const short = raw.includes('/') ? raw.slice(raw.lastIndexOf('/') + 1) : raw.replace(/^id\//, '');
|
|
12
|
+
const pkgLess = raw.includes(':id/') ? raw.slice(raw.indexOf(':id/') + 4) : short;
|
|
13
|
+
return new Set([raw, short, `id/${short}`, pkgLess].filter(Boolean));
|
|
14
|
+
}
|
|
15
|
+
export function resourceIdMatches(candidateShort, candidateFull, target) {
|
|
16
|
+
const wanted = resourceIdAliases(target);
|
|
17
|
+
const cands = [candidateShort, candidateFull].filter((x) => !!x);
|
|
18
|
+
return cands.some((c) => [...resourceIdAliases(c)].some((alias) => wanted.has(alias)));
|
|
19
|
+
}
|
|
20
|
+
export function boundsBucket(bounds) {
|
|
21
|
+
if (!bounds)
|
|
22
|
+
return undefined;
|
|
23
|
+
const bucket = (n) => Math.floor(n / 50) * 50;
|
|
24
|
+
return `${bucket(bounds[0])},${bucket(bounds[1])},${bucket(bounds[2])},${bucket(bounds[3])}`;
|
|
25
|
+
}
|
|
26
|
+
export function center(b) {
|
|
27
|
+
return { x: Math.round((b[0] + b[2]) / 2), y: Math.round((b[1] + b[3]) / 2) };
|
|
28
|
+
}
|
|
29
|
+
export function setsEqual(a, b) {
|
|
30
|
+
if (a.size !== b.size)
|
|
31
|
+
return false;
|
|
32
|
+
for (const x of a)
|
|
33
|
+
if (!b.has(x))
|
|
34
|
+
return false;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function screenSignatureFor(parsed) {
|
|
38
|
+
const sigs = parsed.elements.map(signature);
|
|
39
|
+
if (!sigs.length)
|
|
40
|
+
return undefined;
|
|
41
|
+
return createHash('sha1').update(sigs.sort().join('\n')).digest('hex').slice(0, 16);
|
|
42
|
+
}
|
|
43
|
+
function norm(value) {
|
|
44
|
+
return (value ?? '').trim().toLowerCase();
|
|
45
|
+
}
|
|
46
|
+
function applyResourceIdHints(candidates, target, rawFor) {
|
|
47
|
+
let narrowed = candidates;
|
|
48
|
+
const tryNarrow = (fn) => {
|
|
49
|
+
const next = narrowed.filter(fn);
|
|
50
|
+
if (next.length)
|
|
51
|
+
narrowed = next;
|
|
52
|
+
};
|
|
53
|
+
if (target.packageName) {
|
|
54
|
+
tryNarrow((candidate) => rawFor(candidate)?.id?.startsWith(`${target.packageName}:id/`) === true);
|
|
55
|
+
}
|
|
56
|
+
if (target.className) {
|
|
57
|
+
const wanted = norm(target.className);
|
|
58
|
+
tryNarrow((candidate) => {
|
|
59
|
+
const raw = rawFor(candidate);
|
|
60
|
+
const cls = norm(raw?.cls);
|
|
61
|
+
return cls === wanted || cls.endsWith(`.${wanted}`) || norm(candidate.role) === wanted;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (target.textHint) {
|
|
65
|
+
const wanted = norm(target.textHint);
|
|
66
|
+
tryNarrow((candidate) => {
|
|
67
|
+
const raw = rawFor(candidate);
|
|
68
|
+
return [candidate.text, candidate.label, raw?.text, raw?.desc].some((value) => norm(value) === wanted);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (target.boundsHint) {
|
|
72
|
+
tryNarrow((candidate) => boundsBucket(rawFor(candidate)?.bounds ?? candidate.bounds) === target.boundsHint);
|
|
73
|
+
}
|
|
74
|
+
return narrowed;
|
|
75
|
+
}
|
|
76
|
+
export async function resolveTarget(session, target) {
|
|
77
|
+
if (target?.x != null && target?.y != null)
|
|
78
|
+
return { x: target.x, y: target.y, via: 'coords' };
|
|
79
|
+
if (target?.ref) {
|
|
80
|
+
const node = session.lastSnapshot?.fullByRef.get(target.ref);
|
|
81
|
+
if (!node)
|
|
82
|
+
return { error: `No ${target.ref} in the latest snapshot — run qa_snapshot first (refs invalidate after navigation).` };
|
|
83
|
+
return { ...center(node.bounds), via: target.ref, ref: target.ref, textLen: node.text.length, secure: isSecureNode(node) };
|
|
84
|
+
}
|
|
85
|
+
if (target?.text || target?.id) {
|
|
86
|
+
const parsed = parseSnapshot(await session.driver.dumpXml());
|
|
87
|
+
session.lastSnapshot = { fullByRef: parsed.fullByRef, signatures: new Set(parsed.elements.map(signature)), allNodes: parsed.allNodes };
|
|
88
|
+
const screenMismatch = target.screenSignature && screenSignatureFor(parsed) !== target.screenSignature;
|
|
89
|
+
const t = target.text?.toLowerCase();
|
|
90
|
+
const idCands = target.id
|
|
91
|
+
? parsed.elements.filter((e) => {
|
|
92
|
+
const raw = e.ref ? parsed.fullByRef.get(e.ref) : undefined;
|
|
93
|
+
return resourceIdMatches(e.id, raw?.id, target.id);
|
|
94
|
+
})
|
|
95
|
+
: [];
|
|
96
|
+
const textCands = !target.id && t
|
|
97
|
+
? parsed.elements.filter((e) => e.text?.toLowerCase().includes(t) || e.label?.toLowerCase().includes(t))
|
|
98
|
+
: [];
|
|
99
|
+
let cands = target.id ? idCands : textCands;
|
|
100
|
+
if (target.id && cands.length > 1 && target.index == null) {
|
|
101
|
+
cands = applyResourceIdHints(cands, target, (candidate) => parsed.fullByRef.get(candidate.ref));
|
|
102
|
+
}
|
|
103
|
+
if (!cands.length) {
|
|
104
|
+
return { error: `No element matched selector ${JSON.stringify(target)}${screenMismatch ? ' (recorded screen signature differs from current screen)' : ''}.` };
|
|
105
|
+
}
|
|
106
|
+
if (target.id && cands.length > 1 && target.index == null) {
|
|
107
|
+
const labels = cands.map((e) => `${e.ref}:${e.role}:${e.text ?? e.label ?? e.id ?? 'unlabeled'}`).join(', ');
|
|
108
|
+
return { error: `AMBIGUOUS_SELECTOR: resource-id ${target.id} matched ${cands.length} elements (${labels}); add text/class/bounds hints or an explicit index${screenMismatch ? ' (recorded screen signature differs from current screen)' : ''}.` };
|
|
109
|
+
}
|
|
110
|
+
const el = cands[target.index ?? 0] ?? cands[0];
|
|
111
|
+
return { ...center(el.bounds), via: `selector(${el.ref})`, ref: el.ref, textLen: el.text?.length, secure: el.secure };
|
|
112
|
+
}
|
|
113
|
+
return { error: 'No target provided. Use { ref } | { text|id } | { x, y }.' };
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target.js","sourceRoot":"","sources":["../../src/core/target.ts"],"names":[],"mappings":"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"}
|