@nextclaw/x-harness 0.2.2 → 0.2.4

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/SOURCE.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Corresponding source
2
2
 
3
- Version 0.2.2, commit 2aaac6a777b93bca44fce2ccaa5baae01113ee13.
3
+ Version 0.2.4, commit 6dec3f53d7adf1743c1cd22857b17fc225cb4957.
4
4
 
5
- Complete source, build scripts and dependencies: https://github.com/Peiiii/x-harness/tree/2aaac6a777b93bca44fce2ccaa5baae01113ee13
5
+ Complete source, build scripts and dependencies: https://github.com/Peiiii/x-harness/tree/6dec3f53d7adf1743c1cd22857b17fc225cb4957
6
6
 
7
7
  Clone this revision, run npm ci, npm run bootstrap and npm run release:build.
8
8
 
@@ -248,12 +248,26 @@ export class DOMEditing {
248
248
  if (query.view !== "composer" ||
249
249
  result.needsStateCheck ||
250
250
  command.assets?.length !== 1)
251
- throw new Error("仅上传已观察编辑器的一张带ALT图片");
251
+ throw new Error("仅上传已观察编辑器的一张图片或一个MP4视频");
252
252
  const asset = command.assets[0];
253
- if (!asset.alt?.trim())
253
+ const isVideo = /\.mp4$/i.test(asset.path ?? "");
254
+ if (!isVideo && !asset.alt?.trim())
254
255
  throw new Error("图片须有完整ALT");
255
256
  this.#session.current = null;
256
257
  await attachImage(this.#tab, { path: asset.path });
258
+ if (isVideo) {
259
+ await this.#tab.playwright
260
+ .locator('[data-testid="videoPlayer"],video')
261
+ .first()
262
+ .waitFor({ state: "visible", timeoutMs: 60000 });
263
+ const composer = await this.#observe({ view: "composer" });
264
+ if (composer.needsStateCheck || !composer.videos?.length)
265
+ throw new Error("上传视频预览未确认");
266
+ return {
267
+ completed: true,
268
+ value: { assets: [asset], mediaEvidence: composer.videos, composer },
269
+ };
270
+ }
257
271
  await this.#tab.playwright
258
272
  .getByRole("button", {
259
273
  name: /^(?:编辑|Edit)(?:图片|照片|媒体| image| photo| media)?(?: \d+)?$/i,
@@ -355,7 +369,10 @@ export class DOMEditing {
355
369
  throw new Error("编辑后提交入口不唯一");
356
370
  await this.#locator("button", submits[0].label)
357
371
  .and(this.#tab.playwright.locator(':not([disabled]):not([aria-disabled="true"])'))
358
- .waitFor({ state: "visible", timeoutMs: 5000 });
372
+ .waitFor({
373
+ state: "visible",
374
+ timeoutMs: result.videos?.length ? 60000 : 5000,
375
+ });
359
376
  return { completed: true };
360
377
  return { completed: false, loc, submittedEditor };
361
378
  }
@@ -128,7 +128,9 @@ export class PlatformWriting {
128
128
  samePublishedText(post.body, action.text) &&
129
129
  /^\d+$/.test(post.id ?? "") &&
130
130
  (!action.assets?.length ||
131
- action.assets.every((asset) => post.images?.some((image) => image.alt === asset.alt))) &&
131
+ action.assets.every((asset) => /\.mp4$/i.test(asset.path ?? "")
132
+ ? post.media?.some((media) => media.kind === "video")
133
+ : post.images?.some((image) => image.alt === asset.alt))) &&
132
134
  !(action.beforeIds ?? []).includes(post.id) &&
133
135
  Number.isFinite(Date.parse(post.datetime)) &&
134
136
  Date.parse(post.datetime) >= Date.parse(action.intentAt) &&
@@ -1,9 +1,9 @@
1
1
  import { readComposer } from "./readers-composer.js";
2
2
  import { readPosts } from "./readers-posts.js";
3
- // Upload only through the observed image button and browser file chooser.
3
+ // Upload only through the observed media button and browser file chooser.
4
4
  export async function attachImage(tab, { path } = {}) {
5
- if (!/^\/.*\.(?:png|jpe?g|webp)$/i.test(path ?? ""))
6
- throw new Error("需要绝对图片路径");
5
+ if (!/^\/.*\.(?:png|jpe?g|webp|mp4)$/i.test(path ?? ""))
6
+ throw new Error("需要绝对图片或MP4路径");
7
7
  if (typeof tab?.playwright?.evaluate !== "function" ||
8
8
  typeof tab?.playwright?.getByRole !== "function" ||
9
9
  typeof tab?.playwright?.waitForEvent !== "function")
@@ -95,6 +95,12 @@ export function readComposer() {
95
95
  images: Array.from(scope.querySelectorAll("img"))
96
96
  .filter(visible)
97
97
  .map((el) => ({ alt: el.alt })),
98
+ videos: Array.from(scope.querySelectorAll('[data-testid="videoPlayer"],video'))
99
+ .filter(visible)
100
+ .map((el) => ({
101
+ kind: "video",
102
+ label: el.getAttribute("aria-label"),
103
+ })),
98
104
  needsStateCheck: false,
99
105
  };
100
106
  }
@@ -285,6 +285,12 @@ export const readXObservation = (query) => {
285
285
  images: Array.from(scope.querySelectorAll("img"))
286
286
  .filter(visible)
287
287
  .map((el) => ({ alt: el.alt })),
288
+ videos: Array.from(scope.querySelectorAll('[data-testid="videoPlayer"],video'))
289
+ .filter(visible)
290
+ .map((el) => ({
291
+ kind: "video",
292
+ label: el.getAttribute("aria-label"),
293
+ })),
288
294
  needsStateCheck: false,
289
295
  };
290
296
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/x-harness",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "description": "Typed X task harness with compact business results and explicit diagnostics.",
6
6
  "main": "./dist/index.js",
@@ -42,5 +42,5 @@
42
42
  "publishConfig": {
43
43
  "access": "public"
44
44
  },
45
- "gitHead": "2aaac6a777b93bca44fce2ccaa5baae01113ee13"
45
+ "gitHead": "6dec3f53d7adf1743c1cd22857b17fc225cb4957"
46
46
  }
@@ -12,7 +12,7 @@ Action APIs return status/actionId/publicURL/evidenceRef and reason/missing/resu
12
12
 
13
13
  ## Publish
14
14
 
15
- Additional fields: kind:'original', draftPath (existing .post.md), text (complete approved text), composeURL, composeEvidenceRef; optional assets:[{path,alt}], activation:'click'|'keyboard'. Every media path must exist and be reviewed.
15
+ Additional fields: kind:'original', draftPath (existing .post.md), text (complete approved text), composeURL, composeEvidenceRef; optional assets:[{path,alt}], activation:'click'|'keyboard'. Every media path must exist and be reviewed. The DOM uploader supports one absolute PNG/JPEG/WebP image with required ALT, or one absolute MP4 video. Video uploads wait for a visible preview and public settlement requires a video on the exact fresh post; image ALT controls are not applied to videos.
16
16
 
17
17
  ```js
18
18
  var result = await x.publishPost({ taskId, draftPath, preparedAction: {