@testin-official/xagent 0.3.13 → 0.3.14

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.
Files changed (68) hide show
  1. package/README.md +71 -10
  2. package/dist/assets/contracts/build-create.schema.json +17 -0
  3. package/dist/assets/contracts/build-list.schema.json +18 -0
  4. package/dist/assets/contracts/build.schema.json +76 -0
  5. package/dist/assets/contracts/execution-target.schema.json +12 -4
  6. package/dist/assets/contracts/fixtures/execution-target.concurrency-twenty-one.invalid.json +4 -0
  7. package/dist/assets/contracts/fixtures/execution-target.concurrency-two.invalid.json +1 -1
  8. package/dist/assets/contracts/fixtures/execution-target.concurrency-two.valid.json +4 -0
  9. package/dist/assets/contracts/fixtures/execution-target.count-eleven.invalid.json +6 -0
  10. package/dist/assets/contracts/fixtures/execution-target.count-two.invalid.json +1 -1
  11. package/dist/assets/contracts/fixtures/execution-target.count-two.valid.json +6 -0
  12. package/dist/assets/contracts/fixtures/execution-target.multiple-devices.invalid.json +23 -1
  13. package/dist/assets/contracts/fixtures/execution-target.multiple-devices.valid.json +4 -0
  14. package/dist/assets/contracts/fixtures/execution-target.multiple-filters.invalid.json +66 -2
  15. package/dist/assets/contracts/fixtures/execution-target.multiple-filters.valid.json +7 -0
  16. package/dist/assets/contracts/fixtures/target-hash-cases.json +52 -1
  17. package/dist/assets/contracts/fixtures/target-preview.success.json +4 -1
  18. package/dist/assets/contracts/fixtures/target-preview.unavailable.json +4 -1
  19. package/dist/assets/contracts/run-create-result.schema.json +18 -0
  20. package/dist/assets/contracts/run-create-v2.schema.json +85 -19
  21. package/dist/assets/contracts/run.schema.json +179 -34
  22. package/dist/assets/contracts/target-preview.schema.json +89 -19
  23. package/dist/cli.d.ts.map +1 -1
  24. package/dist/cli.js +264 -18
  25. package/dist/cli.js.map +1 -1
  26. package/dist/core/version.d.ts +1 -1
  27. package/dist/core/version.js +1 -1
  28. package/dist/project/target-matrix.d.ts +4 -0
  29. package/dist/project/target-matrix.d.ts.map +1 -0
  30. package/dist/project/target-matrix.js +26 -0
  31. package/dist/project/target-matrix.js.map +1 -0
  32. package/dist/run/batch.d.ts +44 -0
  33. package/dist/run/batch.d.ts.map +1 -0
  34. package/dist/run/batch.js +261 -0
  35. package/dist/run/batch.js.map +1 -0
  36. package/dist/run/builds.d.ts +20 -0
  37. package/dist/run/builds.d.ts.map +1 -0
  38. package/dist/run/builds.js +70 -0
  39. package/dist/run/builds.js.map +1 -0
  40. package/dist/run/create-result.d.ts +6 -0
  41. package/dist/run/create-result.d.ts.map +1 -0
  42. package/dist/run/create-result.js +18 -0
  43. package/dist/run/create-result.js.map +1 -0
  44. package/dist/run/exit-codes.d.ts +8 -0
  45. package/dist/run/exit-codes.d.ts.map +1 -0
  46. package/dist/run/exit-codes.js +47 -0
  47. package/dist/run/exit-codes.js.map +1 -0
  48. package/dist/run/idempotency.d.ts +2 -0
  49. package/dist/run/idempotency.d.ts.map +1 -0
  50. package/dist/run/idempotency.js +13 -0
  51. package/dist/run/idempotency.js.map +1 -0
  52. package/dist/run/junit-suite.d.ts +17 -0
  53. package/dist/run/junit-suite.d.ts.map +1 -0
  54. package/dist/run/junit-suite.js +98 -0
  55. package/dist/run/junit-suite.js.map +1 -0
  56. package/dist/run/junit.d.ts +19 -0
  57. package/dist/run/junit.d.ts.map +1 -0
  58. package/dist/run/junit.js +218 -0
  59. package/dist/run/junit.js.map +1 -0
  60. package/dist/run/scripts.d.ts +9 -0
  61. package/dist/run/scripts.d.ts.map +1 -0
  62. package/dist/run/scripts.js +156 -0
  63. package/dist/run/scripts.js.map +1 -0
  64. package/dist/sdk/client.d.ts +6 -0
  65. package/dist/sdk/client.d.ts.map +1 -1
  66. package/dist/sdk/client.js +45 -0
  67. package/dist/sdk/client.js.map +1 -1
  68. package/package.json +2 -2
package/README.md CHANGED
@@ -50,7 +50,7 @@ Project CI flow:
50
50
  xagent init --base-url https://api.example --upload-base-url https://files.example --token-type project_ci --name ci --token-env XAGENT_CI_TOKEN
51
51
  xagent --json --profile ci --config .\xagent.yaml targets preview android-single --require-available
52
52
  xagent --json --profile ci apps upload .\app-debug.apk
53
- xagent --json --profile ci --config .\xagent.yaml run .\tests\smoke.yaml --target android-single --app-upload-id APP_UPLOAD_ID --idempotency-key JOB_KEY --timeout-ms 600000 --wait --wait-timeout 720 --run-id-file .xagent\run_id --output .xagent\run.json
53
+ xagent --json --profile ci --config .\xagent.yaml run .\tests\smoke.yaml --target android-single --app-upload-id APP_UPLOAD_ID --idempotency-key JOB_KEY --timeout-ms 600000 --device-wait-timeout-ms 600000 --wait --wait-timeout 1500 --run-id-file .xagent\run_id --output .xagent\run.json --junit .xagent\junit.xml
54
54
  # cleanup (after_script / finally)
55
55
  xagent --json --profile ci runs cancel RUN_ID --if-active
56
56
  xagent --json --profile ci apps release APP_UPLOAD_ID
@@ -60,19 +60,58 @@ A CI job must set both `--base-url` and `--upload-base-url` explicitly; the
60
60
  defaults point to the production service.
61
61
 
62
62
  - `targets preview --require-available` exits with `device_unavailable` (5)
63
- when no previewed device is available, so the job can stop before building.
63
+ when fewer devices are currently available than the Target requests, so the
64
+ job can stop before building. The preview lists the devices that would be
65
+ assigned, plus `requested_count`, `matched_count`, and `available_count`.
64
66
  - Derive the idempotency key from the CI job ID (for example
65
67
  `gitlab-$CI_PROJECT_ID-$CI_JOB_ID`, or
66
68
  `github-$GITHUB_REPOSITORY_ID-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT-$GITHUB_JOB`).
67
69
  A retried job gets a new ID and a new Run. A key reused with a new
68
70
  `app_upload_id` is rejected with `idempotency_conflict` (2).
69
- - Keep the timeouts ordered: `--timeout-ms` (cloud Run) is shorter than
70
- `--wait-timeout` (CLI wait), and both fit inside the CI job timeout. `run --wait`
71
- warns when the wait is not longer than the Run timeout.
71
+ - Keep the timeouts ordered: `--device-wait-timeout-ms` (queue for a busy
72
+ device; default 10 minutes, `0` to fail immediately) plus `--timeout-ms`
73
+ (cloud execution) should be shorter than `--wait-timeout` (CLI wait), and
74
+ all of that should fit inside the CI job timeout. `run --wait` warns when
75
+ the wait is not longer than the device wait plus the Run timeout. A
76
+ device-wait timeout finishes as `infrastructure_failed` with
77
+ `reason=device_wait_timeout` and exit code 5. `targets preview
78
+ --require-available` still fails before compile when the Target cannot
79
+ start immediately, including a matrix that does not have enough free devices.
72
80
  - `--run-id-file` is written right after the Run is created, so cleanup can
73
81
  cancel it with `runs cancel --if-active`, which skips Runs that are already
74
82
  terminal or cancelling. `--output` holds the last known Run JSON, including when
75
- the wait times out.
83
+ the wait times out. `--junit` writes a standard JUnit XML report for the same
84
+ Run (one script = one testcase). Failed cases include the assertion step
85
+ message. The file is written from the last known Run, including when the wait
86
+ times out.
87
+
88
+ GitLab can publish the report on the merge request test page:
89
+
90
+ ```yaml
91
+ artifacts:
92
+ when: always
93
+ paths:
94
+ - .xagent/run.json
95
+ - .xagent/junit.xml
96
+ reports:
97
+ junit: .xagent/junit.xml
98
+ ```
99
+
100
+ GitHub Actions can upload the same file and pass it to a JUnit report action:
101
+
102
+ ```yaml
103
+ - name: Upload XAgent JUnit report
104
+ if: always()
105
+ uses: actions/upload-artifact@v4
106
+ with:
107
+ name: xagent-junit
108
+ path: .xagent/junit.xml
109
+ - name: Publish XAgent JUnit report
110
+ if: always()
111
+ uses: EnricoMi/publish-unit-test-result-action@v2
112
+ with:
113
+ files: .xagent/junit.xml
114
+ ```
76
115
 
77
116
  File-service upload requests default to a 15-minute timeout. Override it when needed with
78
117
  `xagent apps upload .\app-debug.apk --upload-timeout 900` or
@@ -82,15 +121,37 @@ File-service upload requests default to a 15-minute timeout. Override it when ne
82
121
  the `app:upload` capability. One upload can be bound to one Run. Release is
83
122
  idempotent and should run from CI cleanup (`after_script` / `finally`).
84
123
 
85
- To run several scripts against one build, promote the upload and pass its
86
- `package_id` to every Run instead of `--app-upload-id`:
124
+ To run several scripts in one CI job, promote the upload once, then pass a
125
+ directory (or multiple YAML files) to `run`. The CLI starts a Build when you
126
+ pass `--build auto`, attaches each script as a Run, and reuses the same
127
+ `package_id` so the APK is not uploaded again. Derived idempotency keys are
128
+ `<job-key>:<relative-path>:<target>` (the suffix is hashed if the key would
129
+ exceed 255 characters). `--max-parallel` defaults to 1 and only limits how
130
+ many scripts the CLI submits at once. A Target may name up to 20 fixed
131
+ devices or up to 10 filters (`count` 1-10, `concurrency` 1-20). The service
132
+ expands that matrix into one single-device Run per device under the same
133
+ build; `concurrency` is how many of those Runs may occupy a device at the
134
+ same time, and the rest wait using the device-wait queue. A matrix Target requires
135
+ `--package-id` (or `--app-upload-id --promote`) and a build; `run` starts
136
+ one automatically when you omit `--build-id` and `--build auto`.
87
137
 
88
138
  ```powershell
89
139
  xagent --json --profile ci apps upload .\app-debug.apk --promote
90
- xagent --json --profile ci --config .\xagent.yaml run .\tests\login.yaml --target android-single --package-id PACKAGE_ID --idempotency-key JOB_KEY-login --wait
91
- xagent --json --profile ci --config .\xagent.yaml run .\tests\checkout.yaml --target android-single --package-id PACKAGE_ID --idempotency-key JOB_KEY-checkout --wait
140
+ xagent --json --profile ci --config .\xagent.yaml run .\tests --target android-single --package-id PACKAGE_ID --build auto --idempotency-key JOB_KEY --timeout-ms 600000 --device-wait-timeout-ms 600000 --wait --wait-timeout 1500 --output .xagent\run.json --junit .xagent\junit.xml
141
+ # cleanup (after_script / finally)
142
+ xagent --json --profile ci builds cancel BUILD_ID --if-active
92
143
  ```
93
144
 
145
+ `--build auto` infers the build name, commit, and pipeline URL from GitLab CI
146
+ or GitHub Actions environment variables. You can also pass an existing
147
+ `--build-id`, or manage the same resource with `builds start`, `builds show`,
148
+ `builds wait`, and `builds cancel`. A multi-script `--junit` (or
149
+ `builds wait --junit`) writes a suite report: one `testsuites`, one
150
+ `testsuite` per target, one `testcase` per script and device. A single-script, single-device
151
+ `run --wait --junit` is unchanged. The command's exit code follows the D3
152
+ priority 3 > 2 > 6 > 1 > 7 > 5 > 4 > 0. `--output` for a batch is
153
+ `{ "build_id", "runs": [...] }`.
154
+
94
155
  `apps promote APP_UPLOAD_ID` does the same for an existing upload. A promoted
95
156
  package is kept in the project until it is deleted on the platform; `apps release`
96
157
  on a promoted upload leaves it in place. A promoted upload can no longer be
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/build-create.schema.json",
4
+ "title": "XAgent Build Create v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version"],
8
+ "properties": {
9
+ "schema_version": {"const": "xagent-build-create/v1"},
10
+ "name": {"type": "string", "minLength": 1, "maxLength": 255},
11
+ "external_id": {"type": "string", "minLength": 1, "maxLength": 255},
12
+ "source": {"enum": ["gitlab", "github", "other"]},
13
+ "commit_sha": {"type": "string", "minLength": 1, "maxLength": 64},
14
+ "ref": {"type": "string", "minLength": 1, "maxLength": 255},
15
+ "pipeline_url": {"type": "string", "format": "uri", "maxLength": 1024}
16
+ }
17
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/build-list.schema.json",
4
+ "title": "XAgent Build List v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "builds", "page", "page_size", "total"],
8
+ "properties": {
9
+ "schema_version": {"const": "xagent-build-list/v1"},
10
+ "builds": {
11
+ "type": "array",
12
+ "items": {"$ref": "build.schema.json"}
13
+ },
14
+ "page": {"type": "integer", "minimum": 1},
15
+ "page_size": {"type": "integer", "minimum": 1, "maximum": 100},
16
+ "total": {"type": "integer", "minimum": 0}
17
+ }
18
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/build.schema.json",
4
+ "title": "XAgent Build v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "id",
10
+ "status",
11
+ "name",
12
+ "closed",
13
+ "cancel_requested",
14
+ "created_at",
15
+ "runs"
16
+ ],
17
+ "properties": {
18
+ "schema_version": {"const": "xagent-build/v1"},
19
+ "id": {"type": "string", "pattern": "^bld_[a-f0-9]{32}$"},
20
+ "status": {
21
+ "enum": [
22
+ "running",
23
+ "passed",
24
+ "failed",
25
+ "infrastructure_failed",
26
+ "cancelled",
27
+ "timed_out"
28
+ ]
29
+ },
30
+ "name": {"type": "string", "minLength": 1, "maxLength": 255},
31
+ "external_id": {"type": "string", "minLength": 1, "maxLength": 255},
32
+ "source": {"enum": ["gitlab", "github", "other"]},
33
+ "commit_sha": {"type": "string", "minLength": 1, "maxLength": 64},
34
+ "ref": {"type": "string", "minLength": 1, "maxLength": 255},
35
+ "pipeline_url": {"type": "string", "format": "uri", "maxLength": 1024},
36
+ "closed": {"type": "boolean"},
37
+ "cancel_requested": {"type": "boolean"},
38
+ "created_at": {"type": "string", "format": "date-time"},
39
+ "closed_at": {"type": "string", "format": "date-time"},
40
+ "runs": {
41
+ "type": "array",
42
+ "items": {"$ref": "#/$defs/run_summary"}
43
+ },
44
+ "cancel_results": {
45
+ "type": "array",
46
+ "items": {"$ref": "#/$defs/cancel_result"}
47
+ }
48
+ },
49
+ "$defs": {
50
+ "run_summary": {
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": ["id", "status"],
54
+ "properties": {
55
+ "id": {"type": "string", "minLength": 1},
56
+ "script_name": {"type": "string"},
57
+ "target_name": {"type": "string"},
58
+ "device_id": {"type": "string"},
59
+ "device_model": {"type": "string"},
60
+ "status": {"type": "string", "minLength": 1},
61
+ "exit_code": {"type": ["integer", "null"], "minimum": 0, "maximum": 7}
62
+ }
63
+ },
64
+ "cancel_result": {
65
+ "type": "object",
66
+ "additionalProperties": false,
67
+ "required": ["id", "ok"],
68
+ "properties": {
69
+ "id": {"type": "string", "minLength": 1},
70
+ "ok": {"type": "boolean"},
71
+ "status": {"type": "string"},
72
+ "error": {"type": "string"}
73
+ }
74
+ }
75
+ }
76
+ }
@@ -9,17 +9,21 @@
9
9
  "device_ids": {
10
10
  "type": "array",
11
11
  "minItems": 1,
12
- "maxItems": 1,
12
+ "maxItems": 20,
13
13
  "uniqueItems": true,
14
14
  "items": {"type": "string", "minLength": 1, "maxLength": 128}
15
15
  },
16
16
  "device_filters": {
17
17
  "type": "array",
18
18
  "minItems": 1,
19
- "maxItems": 1,
19
+ "maxItems": 10,
20
20
  "items": {"$ref": "#/$defs/device_filter"}
21
21
  },
22
- "concurrency": {"const": 1}
22
+ "concurrency": {
23
+ "type": "integer",
24
+ "minimum": 1,
25
+ "maximum": 20
26
+ }
23
27
  },
24
28
  "oneOf": [
25
29
  {
@@ -41,7 +45,11 @@
41
45
  "brand": {"type": "string", "minLength": 1, "maxLength": 128},
42
46
  "model": {"type": "string", "minLength": 1, "maxLength": 128},
43
47
  "resolution": {"type": "string", "minLength": 1, "maxLength": 64},
44
- "count": {"const": 1},
48
+ "count": {
49
+ "type": "integer",
50
+ "minimum": 1,
51
+ "maximum": 10
52
+ },
45
53
  "strategy": {"enum": ["random", "weight"]}
46
54
  },
47
55
  "anyOf": [
@@ -0,0 +1,4 @@
1
+ {
2
+ "device_ids": ["DEVICE_001"],
3
+ "concurrency": 21
4
+ }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "device_ids": ["DEVICE_001"],
3
- "concurrency": 2
3
+ "concurrency": 21
4
4
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "device_ids": ["DEVICE_001"],
3
+ "concurrency": 2
4
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "device_filters": [
3
+ {"os": "Android", "count": 11, "strategy": "random"}
4
+ ],
5
+ "concurrency": 1
6
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "device_filters": [
3
- {"os": "Android", "count": 2, "strategy": "random"}
3
+ {"os": "Android", "count": 11, "strategy": "random"}
4
4
  ],
5
5
  "concurrency": 1
6
6
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "device_filters": [
3
+ {"os": "Android", "count": 2, "strategy": "random"}
4
+ ],
5
+ "concurrency": 2
6
+ }
@@ -1,4 +1,26 @@
1
1
  {
2
- "device_ids": ["DEVICE_001", "DEVICE_002"],
2
+ "device_ids": [
3
+ "DEVICE_001",
4
+ "DEVICE_002",
5
+ "DEVICE_003",
6
+ "DEVICE_004",
7
+ "DEVICE_005",
8
+ "DEVICE_006",
9
+ "DEVICE_007",
10
+ "DEVICE_008",
11
+ "DEVICE_009",
12
+ "DEVICE_010",
13
+ "DEVICE_011",
14
+ "DEVICE_012",
15
+ "DEVICE_013",
16
+ "DEVICE_014",
17
+ "DEVICE_015",
18
+ "DEVICE_016",
19
+ "DEVICE_017",
20
+ "DEVICE_018",
21
+ "DEVICE_019",
22
+ "DEVICE_020",
23
+ "DEVICE_021"
24
+ ],
3
25
  "concurrency": 1
4
26
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "device_ids": ["DEVICE_001", "DEVICE_002"],
3
+ "concurrency": 1
4
+ }
@@ -1,7 +1,71 @@
1
1
  {
2
2
  "device_filters": [
3
- {"os": "Android", "brand": "Huawei", "count": 1, "strategy": "weight"},
4
- {"os": "Android", "brand": "Samsung", "count": 1, "strategy": "weight"}
3
+ {
4
+ "os": "Android",
5
+ "brand": "Brand1",
6
+ "count": 1,
7
+ "strategy": "weight"
8
+ },
9
+ {
10
+ "os": "Android",
11
+ "brand": "Brand2",
12
+ "count": 1,
13
+ "strategy": "weight"
14
+ },
15
+ {
16
+ "os": "Android",
17
+ "brand": "Brand3",
18
+ "count": 1,
19
+ "strategy": "weight"
20
+ },
21
+ {
22
+ "os": "Android",
23
+ "brand": "Brand4",
24
+ "count": 1,
25
+ "strategy": "weight"
26
+ },
27
+ {
28
+ "os": "Android",
29
+ "brand": "Brand5",
30
+ "count": 1,
31
+ "strategy": "weight"
32
+ },
33
+ {
34
+ "os": "Android",
35
+ "brand": "Brand6",
36
+ "count": 1,
37
+ "strategy": "weight"
38
+ },
39
+ {
40
+ "os": "Android",
41
+ "brand": "Brand7",
42
+ "count": 1,
43
+ "strategy": "weight"
44
+ },
45
+ {
46
+ "os": "Android",
47
+ "brand": "Brand8",
48
+ "count": 1,
49
+ "strategy": "weight"
50
+ },
51
+ {
52
+ "os": "Android",
53
+ "brand": "Brand9",
54
+ "count": 1,
55
+ "strategy": "weight"
56
+ },
57
+ {
58
+ "os": "Android",
59
+ "brand": "Brand10",
60
+ "count": 1,
61
+ "strategy": "weight"
62
+ },
63
+ {
64
+ "os": "Android",
65
+ "brand": "Brand11",
66
+ "count": 1,
67
+ "strategy": "weight"
68
+ }
5
69
  ],
6
70
  "concurrency": 1
7
71
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "device_filters": [
3
+ {"os": "Android", "brand": "Huawei", "count": 1, "strategy": "weight"},
4
+ {"os": "Android", "brand": "Samsung", "count": 1, "strategy": "weight"}
5
+ ],
6
+ "concurrency": 2
7
+ }
@@ -3,7 +3,9 @@
3
3
  {
4
4
  "name": "fixed_device",
5
5
  "target": {
6
- "device_ids": ["DEVICE_001"],
6
+ "device_ids": [
7
+ "DEVICE_001"
8
+ ],
7
9
  "concurrency": 1
8
10
  },
9
11
  "canonical_json": "{\"concurrency\":1,\"device_ids\":[\"DEVICE_001\"]}",
@@ -25,6 +27,55 @@
25
27
  },
26
28
  "canonical_json": "{\"concurrency\":1,\"device_filters\":[{\"brand\":\"Huawei\",\"count\":1,\"model\":\"P30\",\"os\":\"Android\",\"strategy\":\"weight\"}]}",
27
29
  "target_hash": "b57ee824993af6958955bdc74793c256efb47dcc26f4338dd2f265b3b9b7d901"
30
+ },
31
+ {
32
+ "name": "multiple_devices",
33
+ "target": {
34
+ "device_ids": [
35
+ "DEVICE_001",
36
+ "DEVICE_002"
37
+ ],
38
+ "concurrency": 1
39
+ },
40
+ "canonical_json": "{\"concurrency\":1,\"device_ids\":[\"DEVICE_001\",\"DEVICE_002\"]}",
41
+ "target_hash": "80870e64fa03e302a192ccfc933272a764ee1ec62f5d7fad517e70562d7d213a"
42
+ },
43
+ {
44
+ "name": "count_two",
45
+ "target": {
46
+ "device_filters": [
47
+ {
48
+ "os": "Android",
49
+ "count": 2,
50
+ "strategy": "random"
51
+ }
52
+ ],
53
+ "concurrency": 2
54
+ },
55
+ "canonical_json": "{\"concurrency\":2,\"device_filters\":[{\"count\":2,\"os\":\"Android\",\"strategy\":\"random\"}]}",
56
+ "target_hash": "42c254c3600ba7d4fedb10eb33110964149ed630f967c470a8c711532afdd6e1"
57
+ },
58
+ {
59
+ "name": "multiple_filters",
60
+ "target": {
61
+ "device_filters": [
62
+ {
63
+ "os": "Android",
64
+ "brand": "Huawei",
65
+ "count": 1,
66
+ "strategy": "weight"
67
+ },
68
+ {
69
+ "os": "Android",
70
+ "brand": "Samsung",
71
+ "count": 1,
72
+ "strategy": "weight"
73
+ }
74
+ ],
75
+ "concurrency": 2
76
+ },
77
+ "canonical_json": "{\"concurrency\":2,\"device_filters\":[{\"brand\":\"Huawei\",\"count\":1,\"os\":\"Android\",\"strategy\":\"weight\"},{\"brand\":\"Samsung\",\"count\":1,\"os\":\"Android\",\"strategy\":\"weight\"}]}",
78
+ "target_hash": "aa07c6fe0cba4ed2feda2982988278304820a0e8069d75bb2d9a27941ba0ccad"
28
79
  }
29
80
  ]
30
81
  }
@@ -14,5 +14,8 @@
14
14
  }
15
15
  ],
16
16
  "previewed_at": "2026-07-31T08:00:00Z",
17
- "reserved": false
17
+ "reserved": false,
18
+ "requested_count": 1,
19
+ "matched_count": 1,
20
+ "available_count": 1
18
21
  }
@@ -14,5 +14,8 @@
14
14
  }
15
15
  ],
16
16
  "previewed_at": "2026-07-31T08:00:00Z",
17
- "reserved": false
17
+ "reserved": false,
18
+ "requested_count": 1,
19
+ "matched_count": 1,
20
+ "available_count": 0
18
21
  }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/run-create-result.schema.json",
4
+ "title": "XAgent Run Create Result v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "build_id", "runs"],
8
+ "properties": {
9
+ "schema_version": {"const": "xagent-run-create-result/v1"},
10
+ "build_id": {"type": "string", "pattern": "^bld_[a-f0-9]{32}$"},
11
+ "runs": {
12
+ "type": "array",
13
+ "minItems": 2,
14
+ "maxItems": 100,
15
+ "items": {"$ref": "run.schema.json"}
16
+ }
17
+ }
18
+ }