@nextclaw/x-harness 0.2.39 → 0.2.41
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 +2 -2
- package/dist/draft-schedule.d.ts +2 -0
- package/dist/draft-schedule.js +21 -5
- package/dist/platform-collections.d.ts +2 -0
- package/dist/platform-collections.js +48 -13
- package/dist/types.d.ts +2 -0
- package/package.json +2 -2
- package/skill/references/writes.md +2 -0
package/SOURCE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Corresponding source
|
|
2
2
|
|
|
3
|
-
Version 0.2.
|
|
3
|
+
Version 0.2.41, commit abb1997d1e0d7208ed22a5b47d36c9b7dc3b6832.
|
|
4
4
|
|
|
5
|
-
Complete source, build scripts and dependencies: https://github.com/Peiiii/x-harness/tree/
|
|
5
|
+
Complete source, build scripts and dependencies: https://github.com/Peiiii/x-harness/tree/abb1997d1e0d7208ed22a5b47d36c9b7dc3b6832
|
|
6
6
|
|
|
7
7
|
Clone this revision, run npm ci, npm run bootstrap and npm run release:build.
|
|
8
8
|
|
package/dist/draft-schedule.d.ts
CHANGED
package/dist/draft-schedule.js
CHANGED
|
@@ -12,20 +12,36 @@ export class DraftSchedule {
|
|
|
12
12
|
action.notBefore,
|
|
13
13
|
...Array.from(metadata.matchAll(/^(?:notBefore\s*:\s*|最早发布窗口[::]\s*)([^\n;;]+)/gm), (m) => m[1].trim().replace(/^['"]|['"]$/g, "")),
|
|
14
14
|
].filter((v) => v !== undefined && v !== null);
|
|
15
|
+
const endings = [
|
|
16
|
+
action.notAfter,
|
|
17
|
+
...Array.from(metadata.matchAll(/^(?:notAfter\s*:\s*|最晚发布窗口[::]\s*)([^\n;;]+)/gm), (m) => m[1].trim().replace(/^['"]|['"]$/g, "")),
|
|
18
|
+
].filter((v) => v !== undefined && v !== null);
|
|
19
|
+
if (endings.some((v) => typeof v !== "string" ||
|
|
20
|
+
!/(?:Z|[+-]\d{2}:\d{2})$/.test(v) ||
|
|
21
|
+
!Number.isFinite(Date.parse(v))))
|
|
22
|
+
return { invalid: true, invalidAfter: true };
|
|
23
|
+
const until = endings.length
|
|
24
|
+
? new Date(Math.min(...endings.map((v) => Date.parse(v)))).toISOString()
|
|
25
|
+
: undefined;
|
|
15
26
|
if (!declared.length)
|
|
16
|
-
return {};
|
|
27
|
+
return until ? { until } : {};
|
|
17
28
|
if (declared.some((v) => typeof v !== "string" ||
|
|
18
29
|
!/(?:Z|[+-]\d{2}:\d{2})$/.test(v) ||
|
|
19
30
|
!Number.isFinite(Date.parse(v))))
|
|
20
31
|
return { invalid: true };
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
const at = new Date(Math.max(...declared.map((v) => Date.parse(v)))).toISOString();
|
|
33
|
+
if (until && Date.parse(until) <= Date.parse(at))
|
|
34
|
+
return { invalid: true, invalidAfter: true };
|
|
35
|
+
return { at, ...(until ? { until } : {}) };
|
|
24
36
|
}
|
|
25
37
|
reason(action, now) {
|
|
26
38
|
const schedule = this.resolve(action);
|
|
27
39
|
if (schedule.invalid)
|
|
28
|
-
return
|
|
40
|
+
return schedule.invalidAfter
|
|
41
|
+
? "draft-not-after-invalid"
|
|
42
|
+
: "draft-not-before-invalid";
|
|
43
|
+
if (schedule.until && now >= Date.parse(schedule.until))
|
|
44
|
+
return "draft-window-expired";
|
|
29
45
|
return schedule.at && now < Date.parse(schedule.at)
|
|
30
46
|
? "draft-not-before-not-reached"
|
|
31
47
|
: null;
|
|
@@ -9,6 +9,8 @@ export declare class PlatformCollections {
|
|
|
9
9
|
private handleCollectSources;
|
|
10
10
|
private handleReadCreator;
|
|
11
11
|
private handleReadBookmarks;
|
|
12
|
+
private bookmarkDiscovery;
|
|
13
|
+
private waitForBookmarkLabel;
|
|
12
14
|
private handleSearchSources;
|
|
13
15
|
}
|
|
14
16
|
export declare function createPlatformCollections(...args: ConstructorParameters<typeof PlatformCollections>): PlatformCollections;
|
|
@@ -174,8 +174,15 @@ export class PlatformCollections {
|
|
|
174
174
|
targetLabel: folder,
|
|
175
175
|
bookmarkNavigation: true,
|
|
176
176
|
});
|
|
177
|
+
if (discoverOnly && nav.bookmarkFolders?.length)
|
|
178
|
+
return this.bookmarkDiscovery(nav);
|
|
177
179
|
const bookmark = (c) => /^(书签|收藏|Bookmarks)$/i.test(c.label);
|
|
178
|
-
|
|
180
|
+
const matching = (nav.controls ?? []).filter(bookmark);
|
|
181
|
+
const entered = await this.#choose(nav, bookmark, {
|
|
182
|
+
activateSelected: true,
|
|
183
|
+
});
|
|
184
|
+
const openedTab = entered && matching.length === 1 && matching[0].role === "tab";
|
|
185
|
+
if (!entered &&
|
|
179
186
|
!(await this.#choose(nav, (c) => /^(历史|History)$/i.test(c.label)))) {
|
|
180
187
|
if (await this.#choose(nav, (c) => /^(更多菜单项|More menu items)$/i.test(c.label))) {
|
|
181
188
|
nav = await this.#ready({ view: "menu" });
|
|
@@ -207,13 +214,19 @@ export class PlatformCollections {
|
|
|
207
214
|
targetLabel: folder,
|
|
208
215
|
bookmarkNavigation: true,
|
|
209
216
|
});
|
|
217
|
+
if (discoverOnly &&
|
|
218
|
+
!(nav.controls ?? []).some((c) => c.role === "tab")) {
|
|
219
|
+
await this.waitForBookmarkLabel(nav, "书签", "Bookmarks");
|
|
220
|
+
nav = await this.#ready({ view: "navigation", bookmarkNavigation: true });
|
|
221
|
+
}
|
|
210
222
|
const tabs = (nav.controls ?? []).filter((c) => c.role === "tab" &&
|
|
211
223
|
(bookmark(c) ||
|
|
212
224
|
(discoverOnly &&
|
|
213
225
|
/\/i\/(?:history|bookmarks)(?:\/|$)/.test(nav.page ?? "") &&
|
|
214
226
|
c.selected &&
|
|
215
227
|
!/^(喜欢|Likes)$/i.test(c.label))));
|
|
216
|
-
if (
|
|
228
|
+
if (!openedTab &&
|
|
229
|
+
tabs.length === 1 &&
|
|
217
230
|
(!tabs[0].selected ||
|
|
218
231
|
!(nav.controls ?? []).some((c) => c.label.split("\n")[0] === folder))) {
|
|
219
232
|
await this.#choose(nav, (c) => c === tabs[0], {
|
|
@@ -226,17 +239,14 @@ export class PlatformCollections {
|
|
|
226
239
|
});
|
|
227
240
|
}
|
|
228
241
|
if (discoverOnly) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
folders,
|
|
238
|
-
missing: folders.length ? [] : ["bookmark-folder-menu-unconfirmed"],
|
|
239
|
-
};
|
|
242
|
+
if (!nav.bookmarkFolders?.length) {
|
|
243
|
+
await this.waitForBookmarkLabel(nav, "所有书签", "All bookmarks");
|
|
244
|
+
nav = await this.#ready({
|
|
245
|
+
view: "navigation",
|
|
246
|
+
bookmarkNavigation: true,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return this.bookmarkDiscovery(nav);
|
|
240
250
|
}
|
|
241
251
|
if (!(nav.controls ?? []).some((c) => c.label.split("\n")[0] === folder)) {
|
|
242
252
|
try {
|
|
@@ -281,6 +291,31 @@ export class PlatformCollections {
|
|
|
281
291
|
},
|
|
282
292
|
};
|
|
283
293
|
}
|
|
294
|
+
bookmarkDiscovery(nav) {
|
|
295
|
+
const folders = nav.bookmarkFolders ?? [];
|
|
296
|
+
return {
|
|
297
|
+
page: nav.page,
|
|
298
|
+
accountHandle: nav.accountHandle,
|
|
299
|
+
observedAt: nav.observedAt,
|
|
300
|
+
fresh: true,
|
|
301
|
+
complete: folders.length > 0 && !nav.alerts?.length,
|
|
302
|
+
pageComplete: false,
|
|
303
|
+
folders,
|
|
304
|
+
missing: folders.length ? [] : ["bookmark-folder-menu-unconfirmed"],
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
async waitForBookmarkLabel(nav, chinese, english) {
|
|
308
|
+
const folder = (nav.controls ?? []).some((c) => /^(History|Bookmarks)$/i.test(c.label))
|
|
309
|
+
? english
|
|
310
|
+
: chinese;
|
|
311
|
+
try {
|
|
312
|
+
await this.#browser.act({ operation: "waitForFolder", folder });
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
if (!/timed out|timeout/i.test(error.message))
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
284
319
|
async handleSearchSources({ query, limit = 3, maxPages = 1, processedIds = [], } = {}) {
|
|
285
320
|
await this.#browser.act({ operation: "searchSources", query });
|
|
286
321
|
const value = await this.handleCollectSources({
|
package/dist/types.d.ts
CHANGED
|
@@ -250,6 +250,8 @@ export type PreparedAction = PreparedActionBase & ({
|
|
|
250
250
|
kind: "original";
|
|
251
251
|
draftPath: string;
|
|
252
252
|
notBefore?: string;
|
|
253
|
+
/** Exclusive publication deadline; does not block unknown recovery. */
|
|
254
|
+
notAfter?: string;
|
|
253
255
|
text: string;
|
|
254
256
|
composeURL: string;
|
|
255
257
|
composeEvidenceRef: EvidenceRef;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/x-harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.41",
|
|
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": "
|
|
45
|
+
"gitHead": "abb1997d1e0d7208ed22a5b47d36c9b7dc3b6832"
|
|
46
46
|
}
|
|
@@ -14,6 +14,8 @@ Action APIs return status/actionId/publicURL/evidenceRef and reason/missing/resu
|
|
|
14
14
|
|
|
15
15
|
Original action optional notBefore is an ISO timestamp with timezone. Explicit draft header notBefore or 最早发布窗口 is also enforced; the later constraint wins. Invalid metadata blocks, and before-window independent publishing returns waiting; scoped rounds report draft-not-before-not-reached. Both preserve no intent or submission. Recovery of an already submitted post is unaffected.
|
|
16
16
|
|
|
17
|
+
Original actions may pass `notAfter` as the calendar window’s exclusive ISO end. It is rechecked at intent and immediately before activation; expiration submits nothing. Unknown verification remains allowed after expiry.
|
|
18
|
+
|
|
17
19
|
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.
|
|
18
20
|
|
|
19
21
|
```js
|