@nextclaw/x-harness 0.1.0 → 0.1.1

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 (94) hide show
  1. package/README.md +2 -2
  2. package/cli/main.js +23 -11
  3. package/dist/api.js +53 -1
  4. package/dist/browser-dom-capabilities.d.ts +4 -0
  5. package/dist/browser-dom-capabilities.js +110 -0
  6. package/dist/browser-dom-dependencies.d.ts +16 -0
  7. package/dist/browser-dom-editing.d.ts +148 -0
  8. package/dist/browser-dom-editing.js +386 -0
  9. package/dist/browser-dom-navigation.d.ts +9 -0
  10. package/dist/browser-dom-navigation.js +165 -0
  11. package/dist/browser-dom-network.d.ts +1 -0
  12. package/dist/browser-dom-network.js +128 -0
  13. package/dist/browser-dom-submission.d.ts +2 -0
  14. package/dist/browser-dom-submission.js +82 -0
  15. package/dist/browser-dom.d.ts +1 -1
  16. package/dist/browser-dom.js +51 -802
  17. package/dist/browser-native.js +32 -28
  18. package/dist/browser.js +6 -2
  19. package/dist/creator.js +1 -1
  20. package/dist/growth-batch.d.ts +5 -0
  21. package/dist/growth-batch.js +168 -0
  22. package/dist/growth-constants.d.ts +5 -0
  23. package/dist/growth-constants.js +13 -0
  24. package/dist/growth-dependencies.d.ts +33 -0
  25. package/dist/growth-execution.d.ts +11 -0
  26. package/dist/growth-execution.js +315 -0
  27. package/dist/growth-parallel.d.ts +4 -0
  28. package/dist/growth-parallel.js +301 -0
  29. package/dist/growth-plan.d.ts +4 -0
  30. package/dist/growth-plan.js +207 -0
  31. package/dist/growth-policy.d.ts +9 -0
  32. package/dist/growth-policy.js +181 -0
  33. package/dist/growth-publishing.d.ts +5 -0
  34. package/dist/growth-publishing.js +240 -0
  35. package/dist/growth-reading.d.ts +4 -0
  36. package/dist/growth-reading.js +135 -0
  37. package/dist/growth-recovery.d.ts +6 -0
  38. package/dist/growth-recovery.js +158 -0
  39. package/dist/growth-state.d.ts +8 -0
  40. package/dist/growth-state.js +183 -0
  41. package/dist/growth-tasks.d.ts +17 -0
  42. package/dist/growth-tasks.js +101 -0
  43. package/dist/growth.d.ts +1 -0
  44. package/dist/growth.js +122 -1902
  45. package/dist/platform-capabilities.d.ts +4 -0
  46. package/dist/platform-capabilities.js +19 -0
  47. package/dist/platform-collections.d.ts +7 -0
  48. package/dist/platform-collections.js +223 -0
  49. package/dist/platform-dependencies.d.ts +43 -0
  50. package/dist/platform-diagnostics.d.ts +9 -0
  51. package/dist/platform-diagnostics.js +51 -0
  52. package/dist/platform-observation.d.ts +8 -0
  53. package/dist/platform-observation.js +75 -0
  54. package/dist/platform-rules.d.ts +17 -0
  55. package/dist/platform-rules.js +41 -0
  56. package/dist/platform-targets.d.ts +7 -0
  57. package/dist/platform-targets.js +129 -0
  58. package/dist/platform-writing.d.ts +8 -0
  59. package/dist/platform-writing.js +171 -0
  60. package/dist/platform.d.ts +1 -1
  61. package/dist/platform.js +56 -677
  62. package/dist/readers-actions.d.ts +2 -0
  63. package/dist/readers-actions.js +66 -0
  64. package/dist/readers-ax-host.d.ts +2 -0
  65. package/dist/readers-ax-host.js +125 -0
  66. package/dist/readers-ax-parser.d.ts +1 -0
  67. package/dist/readers-ax-parser.js +69 -0
  68. package/dist/readers-ax-program.d.ts +1 -0
  69. package/dist/readers-ax-tree.d.ts +19 -0
  70. package/dist/readers-ax-tree.js +110 -0
  71. package/dist/readers-ax-views.d.ts +87 -0
  72. package/dist/readers-ax-views.js +247 -0
  73. package/dist/readers-cli.d.ts +6 -0
  74. package/dist/readers-composer.d.ts +2 -0
  75. package/dist/readers-composer.js +131 -0
  76. package/dist/readers-navigation.d.ts +2 -0
  77. package/dist/readers-navigation.js +66 -0
  78. package/dist/readers-posts.d.ts +2 -0
  79. package/dist/readers-posts.js +141 -0
  80. package/dist/readers-profile.d.ts +3 -0
  81. package/dist/readers-profile.js +157 -0
  82. package/dist/readers.d.ts +9 -20
  83. package/dist/results.js +1 -1
  84. package/dist/round-state.js +2 -2
  85. package/dist/types.d.ts +10 -0
  86. package/generated/observation.mjs +2 -1
  87. package/package.json +1 -1
  88. package/skill/SKILL.md +17 -36
  89. package/skill/references/api.md +7 -34
  90. package/skill/references/reads.md +23 -0
  91. package/skill/references/recovery.md +25 -0
  92. package/skill/references/setup.md +23 -0
  93. package/skill/references/writes.md +51 -0
  94. package/dist/readers.js +0 -1045
@@ -0,0 +1,157 @@
1
+ // Profile selectors are the visible controls observed in the live X workflow.
2
+ // Missing/ambiguous counters remain unknown; rounded counts carry their precision.
3
+ export function readProfile({ handle } = {}) {
4
+ if (!/^[A-Za-z0-9_]{1,15}$/.test(handle ?? ""))
5
+ throw new Error("需要已观察的X handle");
6
+ const visible = (el) => el.getClientRects().length > 0;
7
+ const main = document.querySelector("main");
8
+ if (!main)
9
+ return {
10
+ handle,
11
+ blue: null,
12
+ relationship: "unknown",
13
+ alerts: ["missing-main"],
14
+ observedAt: new Date().toISOString(),
15
+ };
16
+ const buttons = Array.from(main.querySelectorAll("button")).filter(visible);
17
+ const labels = buttons.map((b) => b.getAttribute("aria-label") || b.innerText);
18
+ const relationship = labels.includes(`正在关注 @${handle}`) ||
19
+ labels.includes(`取消关注 @${handle}`)
20
+ ? "following"
21
+ : labels.includes(`回关 @${handle}`)
22
+ ? "follows-you"
23
+ : labels.includes(`关注 @${handle}`)
24
+ ? "not-following"
25
+ : "unknown";
26
+ const badges = buttons
27
+ .filter((b) => b.getAttribute("aria-label") === "提供已验证账号的详细信息。")
28
+ .map((b) => b.querySelector("svg"))
29
+ .filter(Boolean);
30
+ const blue = badges.length === 1
31
+ ? [
32
+ getComputedStyle(badges[0]).color,
33
+ getComputedStyle(badges[0]).fill,
34
+ ].includes("rgb(29, 155, 240)")
35
+ : null;
36
+ const links = Array.from(main.querySelectorAll("a[href]")).filter(visible);
37
+ const counter = (suffixes) => {
38
+ const matched = links.filter((a) => suffixes.some((s) => a.getAttribute("href")?.toLowerCase() ===
39
+ `/${handle}/${s}`.toLowerCase()));
40
+ const texts = [...new Set(matched.map((a) => a.innerText.trim()))];
41
+ if (texts.length !== 1)
42
+ return { value: null, text: texts, precision: "unknown" };
43
+ const match = texts[0].match(/^([\d,.]+)\s*(万|亿|[KMB])?/i);
44
+ const factors = {
45
+ 万: 1e4,
46
+ 亿: 1e8,
47
+ K: 1e3,
48
+ M: 1e6,
49
+ B: 1e9,
50
+ };
51
+ const value = match
52
+ ? Number(match[1].replaceAll(",", "")) *
53
+ (factors[match[2]?.toUpperCase()] ?? 1)
54
+ : null;
55
+ const hrefs = [...new Set(matched.map((a) => a.getAttribute("href")))];
56
+ return {
57
+ value: Number.isFinite(value) ? value : null,
58
+ text: texts[0],
59
+ precision: match?.[2] ? "rounded" : "exact",
60
+ url: hrefs.length === 1 ? "https://x.com" + hrefs[0] : null,
61
+ };
62
+ };
63
+ return {
64
+ handle,
65
+ blue,
66
+ relationship,
67
+ relationshipControls: labels
68
+ .filter((label) => label.includes("@" + handle) || /取消关注|订阅/.test(label))
69
+ .slice(0, 5),
70
+ biography: main.querySelector('[data-testid="UserDescription"]')
71
+ ?.innerText ?? null,
72
+ following: counter(["following"]),
73
+ followers: counter(["followers", "verified_followers"]),
74
+ alerts: Array.from(document.querySelectorAll('[role="alert"]'))
75
+ .filter(visible)
76
+ .map((a) => a.innerText),
77
+ observedAt: new Date().toISOString(),
78
+ };
79
+ }
80
+ // Bounded visible person cells for a currently loaded follower/following list.
81
+ export function readPeople({ offset = 0, limit = 5 } = {}) {
82
+ if (!Number.isInteger(offset) ||
83
+ offset < 0 ||
84
+ !Number.isInteger(limit) ||
85
+ limit < 1 ||
86
+ limit > 5)
87
+ throw new Error("人物窗口须为非负offset及1–5 limit");
88
+ const visible = (el) => el.getClientRects().length > 0;
89
+ const cells = Array.from(document.querySelectorAll('main [data-testid="UserCell"]')).filter(visible);
90
+ const people = cells.slice(offset, offset + limit).map((cell) => {
91
+ const links = Array.from(cell.querySelectorAll("a[href]"))
92
+ .filter(visible)
93
+ .map((a) => ({ text: a.innerText, href: a.getAttribute("href") }));
94
+ const buttons = Array.from(cell.querySelectorAll("button"))
95
+ .filter(visible)
96
+ .map((b) => ({
97
+ label: b.getAttribute("aria-label") || b.innerText,
98
+ disabled: b.disabled,
99
+ }));
100
+ return { text: cell.innerText, links, buttons };
101
+ });
102
+ return {
103
+ page: location.href,
104
+ people,
105
+ offset,
106
+ renderedPeople: cells.length,
107
+ moreInDOM: offset + limit < cells.length,
108
+ pageComplete: false,
109
+ needsStateCheck: !people.length,
110
+ missing: people.length ? [] : ["people-cells-not-found"],
111
+ alerts: Array.from(document.querySelectorAll('[role="alert"]'))
112
+ .filter(visible)
113
+ .map((a) => a.innerText),
114
+ observedAt: new Date().toISOString(),
115
+ };
116
+ }
117
+ // This ranks observed clues, never a predicted follow-back probability.
118
+ export function screenCandidates(profiles, { minRatio = 0.8 } = {}) {
119
+ if (!Array.isArray(profiles) || !Number.isFinite(minRatio) || minRatio <= 0)
120
+ throw new Error("profiles数组和正minRatio必填");
121
+ const seen = new Set();
122
+ return profiles
123
+ .map((p) => {
124
+ const key = String(p.handle ?? "").toLowerCase();
125
+ const duplicate = seen.has(key);
126
+ seen.add(key);
127
+ const following = p.following?.value, followers = p.followers?.value;
128
+ const ratio = Number.isFinite(following) &&
129
+ Number.isFinite(followers) &&
130
+ followers > 0
131
+ ? following / followers
132
+ : null;
133
+ const clues = [];
134
+ if (typeof p.mutualEvidence === "string" && p.mutualEvidence.trim())
135
+ clues.push({ kind: "explicit-mutual", evidence: p.mutualEvidence });
136
+ if (ratio !== null && ratio >= minRatio)
137
+ clues.push({
138
+ kind: "following-ratio",
139
+ ratio,
140
+ following: p.following,
141
+ followers: p.followers,
142
+ evidence: "关注/粉丝比例线索,非回关概率",
143
+ });
144
+ const rejected = [
145
+ duplicate && "duplicate",
146
+ p.blue !== true && "blue-unconfirmed",
147
+ p.relationship !== "not-following" && "relationship-ineligible",
148
+ p.alerts?.length && "page-alert",
149
+ !clues.length && "no-followback-clue",
150
+ !/^[A-Za-z0-9_]{1,15}$/.test(p.handle ?? "") && "invalid-handle",
151
+ ].filter(Boolean);
152
+ return { ...p, ratio, clues, eligible: rejected.length === 0, rejected };
153
+ })
154
+ .sort((a, b) => Number(b.eligible) - Number(a.eligible) ||
155
+ Number(b.clues.some((c) => c.kind === "explicit-mutual")) -
156
+ Number(a.clues.some((c) => c.kind === "explicit-mutual")));
157
+ }
package/dist/readers.d.ts CHANGED
@@ -1,20 +1,9 @@
1
- export declare function readPosts({ offset, limit, targetId, }?: any): any;
2
- export declare function changes(result: any, cache: any, page: any, fresh?: any): any;
3
- export declare function readAX(state: any, { mode, offset, limit, targetId, targetHandle, }?: any): any;
4
- export declare function createAXTabHarness(tab: any): any;
5
- export declare function createAXHarness(app: any): any;
6
- export declare function setupCode(): any;
7
- export declare function readProfile({ handle }?: any): any;
8
- export declare function readPeople({ offset, limit }?: any): any;
9
- export declare function readComposer(): any;
10
- export declare function readMenu(): any;
11
- export declare function readNavigation({ targetLabel }?: any): any;
12
- export declare function readMediaEditor(): any;
13
- export declare function attachImage(tab: any, { path }?: any): Promise<any>;
14
- export declare function openReply(tab: any, { postId }?: any): Promise<any>;
15
- export declare function screenCandidates(profiles: any, { minRatio }?: any): any;
16
- export declare function profileCode({ tab, handle }?: any): any;
17
- export declare function profilesCode(entries: any): any;
18
- export declare function browserCode({ handle, offset, limit, targetId, fresh, }?: any): any;
19
- export declare function axCode({ handle, mode, offset, limit, targetId, targetHandle, fresh, }?: any): any;
20
- export declare function runReaderCLI(): void;
1
+ /** Compatibility exports only. Implementation owners are task-specific modules. */
2
+ export { readPosts, changes } from "./readers-posts.js";
3
+ export { readAX } from "./readers-ax-parser.js";
4
+ export { createAXTabHarness, createAXHarness } from "./readers-ax-host.js";
5
+ export { readProfile, readPeople, screenCandidates, } from "./readers-profile.js";
6
+ export { readComposer, readMediaEditor } from "./readers-composer.js";
7
+ export { readMenu, readNavigation } from "./readers-navigation.js";
8
+ export { attachImage, openReply } from "./readers-actions.js";
9
+ export { setupCode, profileCode, profilesCode, browserCode, axCode, runReaderCLI, } from "./readers-cli.js";
package/dist/results.js CHANGED
@@ -21,7 +21,7 @@ export function diagnostic(value) {
21
21
  if (typeof raw.code !== "string")
22
22
  return undefined;
23
23
  return {
24
- ...pick(raw, ["operation", "category"]),
24
+ ...pick(raw, ["operation", "category", "next", "evidenceRef"]),
25
25
  code: stableReason(raw.code),
26
26
  };
27
27
  }
@@ -109,8 +109,8 @@ export function checkRound(state, { now = Date.now(), evidenceExists = existsSyn
109
109
  reply.unknown, "未提交欠量仍在;未知提交不能冻结新对象");
110
110
  if (exit.reason === "outside-window") {
111
111
  const shanghaiHour = (new Date(now).getUTCHours() + 8) % 24;
112
- require(shanghaiHour < 7 ||
113
- shanghaiHour >= 22, "仍在上海07至22点执行窗口,不能以时段截止结束");
112
+ require(shanghaiHour <
113
+ 7, "仍在上海07至24点评论执行窗口,不能以时段截止结束");
114
114
  }
115
115
  if (exit.reason === "user-stop")
116
116
  require(typeof exit.instructionEvidenceRef === "string" &&
package/dist/types.d.ts CHANGED
@@ -75,6 +75,8 @@ export interface Diagnostic {
75
75
  operation?: string;
76
76
  category?: string;
77
77
  code: string;
78
+ next?: "inspect-local-proof" | "restore-proof-store";
79
+ evidenceRef?: EvidenceRef;
78
80
  }
79
81
  export interface ReadResult {
80
82
  status: "observed" | "needsDecision" | "needsCapability";
@@ -196,6 +198,7 @@ export interface ActionResult {
196
198
  diagnostic?: Diagnostic;
197
199
  }
198
200
  export interface RoundResult {
201
+ diagnostic?: Diagnostic;
199
202
  status: string;
200
203
  taskId: string;
201
204
  mayEnd: boolean;
@@ -220,6 +223,7 @@ export interface RoundResult {
220
223
  };
221
224
  }
222
225
  export interface BatchResult {
226
+ diagnostic?: Diagnostic;
223
227
  status: string;
224
228
  taskId: string;
225
229
  batchId?: string;
@@ -265,6 +269,12 @@ export interface IndependentExecution {
265
269
  tabId: string;
266
270
  evidenceRef: EvidenceRef;
267
271
  scopeEvidenceRef: EvidenceRef;
272
+ windowAuthorization?: {
273
+ actionId: string;
274
+ userAuthorized: true;
275
+ expiresAt: string;
276
+ evidenceRef: EvidenceRef;
277
+ };
268
278
  }
269
279
  export interface PublishOptions {
270
280
  taskId: string;
@@ -487,7 +487,8 @@ export const readXObservation = (query) => {
487
487
  const read = (node) => {
488
488
  if (node.nodeType === 3)
489
489
  return node.textContent ?? "";
490
- if (node.nodeType === 1 && node.tagName === "A") {
490
+ if (node.nodeType === 1 &&
491
+ node.tagName === "A") {
491
492
  const href = node.getAttribute("href") ?? "";
492
493
  if (/^\/\w{1,15}\/status\/\d+$/.test(href))
493
494
  return `https://x.com${href}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/x-harness",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "X tasks for browser-enabled AI agents: compact results, complete evidence and verified writes.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
package/skill/SKILL.md CHANGED
@@ -1,52 +1,33 @@
1
1
  ---
2
2
  name: x-harness
3
- version: 0.1.0
4
- description: Use X through named task APIs with compact receipts, complete target evidence and verified writes. Requires a compatible CUA browser host and the user's own logged-in X account. Install and update with the npm-distributed X Harness runtime.
3
+ version: 0.1.1
4
+ description: Use X through task APIs with compact receipts, complete target evidence and verified writes; requires compatible CUA and the user's X login.
5
5
  ---
6
6
 
7
7
  # X Harness
8
8
 
9
- Use the installed runtime; normal tasks call its root business API. Do not recreate browser selectors, print full DOM/AX, or call lower layers for normal work. Keep observations local and only return the relevant complete evidence, receipt and actionable warnings.
9
+ Use the root business API. Load only the current task reference; reuse unchanged instructions and a valid binding. Normal use needs no implementation source or type-file reading.
10
10
 
11
- ## First use
11
+ ## Bind once
12
12
 
13
- Installed module: `{{MODULE_URL}}`
14
- CLI: `{{CLI_PATH}}`
13
+ Read current host CUA documentation; bind a real logged-in `tab` or native `app`, exactly one. Preserve other tasks' tabs/editors.
15
14
 
16
- If these placeholders are unresolved, install first:
17
- `npx @nextclaw/x-harness@latest setup --target codex` (or `--target claude`).
18
-
19
- Requires Node 22+, a host that exposes compatible CUA `tab` or native `app` methods, and the user's own X login. Codex desktop CUA is validated. Installing this Skill into Claude only installs instructions; confirm that host's browser methods before claiming it can execute. If browser access/login is absent, report the actual missing condition; never fabricate execution or borrow the publisher's account.
20
-
21
- Read the host's CUA documentation before using its controls. Bind/create a task tab through the host's documented API, preserving the user's session. A compatible tab has `url()`, `playwright.evaluate()`, locator methods and optional navigation/AX/screenshot methods; native apps have `getAXState()`, click/key/paste methods. Use the provided connection; do not launch an unrelated automation driver.
22
-
23
- Initialize the user's own workspace once with `node <CLI> init --directory <user workspace> --account <user handle>`. It returns `ledgerPath`, `proofDirectory`, `taskId` and account. Existing project configurations and ledgers remain the owner; reuse those instead of creating duplicate state. Never import the publisher's operations directories, policies or credentials.
24
-
25
- In the host's Node-capable REPL:
15
+ Installed module: `{{MODULE_URL}}`; CLI: `{{CLI_PATH}}`.
16
+ In this repository use the absolute file URL of `<repository>/packages/x-harness/dist/index.js` with original ledger/proof paths. Unresolved placeholders do not require reinstalling an existing repository runtime. Only absent runtime/workspace or an update needs [setup](references/setup.md).
26
17
 
27
18
  ```js
28
- const { createXGrowthHarness } = await import("MODULE_URL_FROM_SETUP");
29
- const x = createXGrowthHarness({ tab, ledgerPath, proofDirectory });
30
- const result = await x.readCreator({
31
- handle: "USER_HANDLE",
32
- target: 5,
33
- mode: "decision",
34
- });
35
- // Print only the needed result; keep the full browser observation in the REPL.
19
+ var { createXGrowthHarness } = await import(moduleURL);
20
+ var x = createXGrowthHarness({ tab, ledgerPath, proofDirectory });
36
21
  ```
37
22
 
38
- Substitute the stable installed URL and initialized paths. Never assume an old imported module hot-reloads after updating. Read `references/api.md` only for the next operation's arguments and the installed `.d.ts` for detailed types.
39
-
40
- ## Execution
41
-
42
- Choose the single named task: readCreator/readBookmarks/searchSources/readPost/readPerson/readFeedback/inspectAccount; publishPost/replyToPost/followOne/followBatch; inspectRound/runGrowthRound; verifySubmission/verifyPublishedPost/verifyFollow. `receipt` is the default compact result; `decision` includes full relevant evidence; `delta` rereads and returns changed complete evidence plus unchanged IDs. Do not trim the target's body to save tokens.
43
-
44
- Writing needs explicit authorization from the current user, verified identity, their own ledger plan, reviewed prepared actions and existing evidence files. Initialization plans zero writes. Set the authorized planned count in the original ledger before execution; never expand an authorized scope yourself. Check the installed types for action, quality and executor fields; retain one owner for the ledger, do not invent approvals or evidence. The current runtime writes only 07:00–22:00 Asia/Shanghai and follow candidate gates require reviewed Chinese-language, blue-check candidates; disclose these conditions when relevant.
45
-
46
- Interpret returned status literally: `verified` requires public proof; `unknown` is not success. For an unknown submission, call verifySubmission on the same action before any new submission. `needsCapability`, `needsDecision`, `waiting` and partial windows must be reported, with the next required condition. Capture visual media before making claims about its contents.
23
+ ## Load by task
47
24
 
48
- ## Updates
25
+ | Current need | Read only |
26
+ | --- | --- |
27
+ | Creator/bookmarks/search/post/person/feedback/account | [reads](references/reads.md) |
28
+ | Publish/reply/follow or a round | [writes](references/writes.md), preamble + common review + matching operation heading |
29
+ | Unknown submission, missing capability or connection failure | [recovery](references/recovery.md) |
49
30
 
50
- Run `npx @nextclaw/x-harness@latest update --target codex` (or claude). It stages the exact fetched release, replaces only managed runtime/Skill directories, saves the previous version and retains user workspaces/configurations. It refuses unfamiliar directories. Start a fresh module binding afterward and verify the next real read operation. A release's distributed Skill and runtime use the same version.
31
+ Default `receipt` is for status; use `decision` on the first call needing content, `delta` for changed evidence with valid prior context. Keep observations in REPL/proof; return complete relevant evidence and actionable warnings. Never truncate target text or print full DOM/AX. Root calls own capability checks, intent, submission, verification and settlement; verified results need no extra preflight/reload/verify.
51
32
 
52
- Free use under the bundled proprietary license; the distributed JS is readable, the TS source repository remains private. Website: https://x-harness.gemigo.app/
33
+ Writes require user authorization, verified identity, reviewed evidence and the original ledger plan; project rules own policy. Unknown submission: verify the same action before any new submission. Follow recovery for other failures; read source only to repair a concrete defect/unsupported capability, verify, then return to root calls.
@@ -1,37 +1,10 @@
1
- # Root API usage
1
+ # API navigation
2
2
 
3
- Bind once: `createXGrowthHarness({ tab, ledgerPath, proofDirectory })`. Exactly one `tab` or native `app`. Read operations can use the initialized zero-write plan.
3
+ Compatibility index; normal tasks route directly from the Skill. Load one current reference, not all files.
4
4
 
5
- ```js
6
- await x.readCreator({
7
- handle: "USER_HANDLE",
8
- target: 10,
9
- maxScrolls: 8,
10
- mode: "decision",
11
- });
12
- await x.searchSources({ query: "AI agents", limit: 5, mode: "decision" });
13
- await x.readBookmarks({ folder: "FOLDER_NAME", limit: 5, mode: "decision" });
14
- await x.inspectAccount({ mode: "decision" });
15
- await x.readPost({ target: { postId, url, evidenceRef }, mode: "decision" });
16
- await x.readPerson({ target: { handle, url, evidenceRef }, mode: "decision" });
17
- await x.readFeedback({
18
- targets: [{ postId, url, evidenceRef }],
19
- mode: "decision",
20
- });
21
- await x.inspectRound({ taskId });
22
- ```
5
+ - Reading: [reads](reads.md).
6
+ - Reviewed writes/rounds: [writes](writes.md), matching operation heading.
7
+ - Failure/unknown submission: [recovery](recovery.md).
8
+ - Missing installation/workspace or update: [setup](setup.md).
23
9
 
24
- Targets/evidence come from prior observed results. Exact option types are in the installed `dist/types.d.ts`. `readFeedback` may need your accountURL as documented there. Preserve full relevant content and expose missing fields rather than inventing them.
25
-
26
- ```js
27
- await x.publishPost({ taskId, draftPath, preparedAction: originalAction });
28
- await x.replyToPost({ taskId, postId, text, preparedAction: replyAction });
29
- await x.followOne({ taskId, handle }); // action already prepared in the original ledger
30
- await x.followBatch({ taskId, handles, preparedActions });
31
- await x.verifySubmission({ taskId, actionId });
32
- await x.verifyPublishedPost({ taskId, draftPath, publicURL, evidenceRef });
33
- await x.verifyFollow({ handle, url, evidenceRef });
34
- await x.runGrowthRound({ taskId, maxActions: 3 });
35
- ```
36
-
37
- Before preparing writes, read the appropriate installed PreparedAction and operation option types. Required reviewed evidence cannot be replaced with a made-up reference. Original posts include actual draftPath, complete text, observed compose URL/proof, author proof and approvedText/draftFingerprint quality. Reply/follow targets require observed evidence. Concurrent task tabs need an explicitly evidenced executor scope; see HarnessOptions/Executor. Business root calls own capability checks, intent journaling, single submission, public verification and ledger settlement.
10
+ Public declarations are the compatibility contract for custom integrations, not a prerequisite for documented calls.
@@ -0,0 +1,23 @@
1
+ # Read tasks
2
+
3
+ Load for reading only. A valid root binding `x` with existing ledger/proof paths suffices; no write plan needed. `mode` defaults to `receipt`; use `decision` immediately when drafting/reviewing needs text. `delta` omits only unchanged evidence previously returned by this instance; after context loss use `decision`.
4
+
5
+ | API | Required | Optional |
6
+ | --- | --- | --- |
7
+ | readCreator | handle | target (default 10, 1–50), maxScrolls, navigate, expand, mode |
8
+ | readBookmarks | folder (observed name) | processedIds, limit, maxPages, mode |
9
+ | searchSources | query | processedIds, limit, maxPages, mode |
10
+ | readPost | target: {postId,url,evidenceRef} | mode |
11
+ | readPerson | target: {handle,url,evidenceRef} | mode |
12
+ | readFeedback | targets: [{postId,url,evidenceRef}] | replyLimit, mode |
13
+ | inspectAccount | none | mode |
14
+ | inspectRound | taskId (original batch ID) | none |
15
+
16
+ ```js
17
+ var result = await x.readCreator({ handle: selectedHandle, target: 5, mode: 'decision' });
18
+ var postResult = await x.readPost({ target: observedPost, mode: 'decision' });
19
+ ```
20
+
21
+ Targets/proof come from actual observations, never guessed URLs or made-up references. API owns navigation/expansion and bounded collection. Native creator collection may return `needsCapability`; inspect that signal before changing adapter.
22
+
23
+ Results include status/evidenceRef/missing/IDs/completeness. In `decision`, creator/bookmark/search use `sources`, post `post`, person `person`, feedback `feedback`; account includes accountHandle/accountURL. `observed` alone does not prove every requested field exists. `pageComplete:false` is a collection boundary, not proof of truncated target text. View media before describing it. Return complete chosen evidence; keep other observations local. Only on failure load recovery.
@@ -0,0 +1,25 @@
1
+ # Recovery by signal
2
+
3
+ Load on failure/unknown only. Keep operation/status/missing/code/proof/error; never infer success or repeat identical failures.
4
+
5
+ | Signal | Next action and exit condition |
6
+ | --- | --- |
7
+ | unknown submission | `await x.verifySubmission({taskId,actionId})`, same IDs; no replacement action or new submission before resolution. |
8
+ | Existing public post needs recovery | `await x.verifyPublishedPost({taskId,draftPath,publicURL,evidenceRef})`; observed URL/proof. Optional sourceLink: {href,resolvedURL,evidenceRef,renderedText?}. |
9
+ | Follow needs independent recovery | `await x.verifyFollow({handle,url,evidenceRef,mode:'decision'})`; honor returned verified. Normal followBatch already verifies. |
10
+ | needsDecision | Resolve returned missing review/proof in original action, resume same ID. Current AI performs internal review under project contract. |
11
+ | waiting | Honor resumeAt/reason, continue independent permitted work; never bypass time/identity/claim gates. |
12
+ | needsCapability | Inspect exact missing method/operation. If a supported binding supplies it, rebind once and retry a necessary read; otherwise repair reported owner with failing sample and targeted validation. |
13
+ | diagnostic.next: inspect-local-proof | Read only `{operation,error}` from diagnostic.evidenceRef; resolve via this reference, keep full proof local. Unknown/intent needs same-ID verification. Unsupported: report code/error and update/support; no private source. |
14
+ | diagnostic.next: restore-proof-store | Restore the workspace's writable proof directory, then resume the same action. Do not replace its ledger or infer submission failure. |
15
+ | Missing fields/partial window | Narrow to target, expand/advance documented bounded window; preserve text. No whole-page output or automatic reload. |
16
+
17
+ ## Host connection or native focus
18
+
19
+ Failure to list/bind Chrome is a host-connection failure before reaching X, not proof of logged-out X. Per current host documentation rediscover the task tab once; validate navigation plus a necessary visible read. Native focus rejection instead needs latest AX URL/window confirmation and one supported focus correction. Do not repeat discovery or restart editors blindly.
20
+
21
+ Lost tab: reuse a live owned tab or create a task tab in the same actual browser; preserve other editors. Native fallback changes binding to app, not business calls; unsupported native tasks remain gaps. Network/TUN observations do not authorize setting changes.
22
+
23
+ ## Repair boundary
24
+
25
+ Normal use/known recovery need no source. Unresolved runtime defect/unsupported capability permits diagnostics: maintainers repair the owner, verify the failed boundary and return to root; consumers report gap/update/support, without private TS. Never scan adapters for arguments, add parsers, bypass refusal or resubmit unknown.
@@ -0,0 +1,23 @@
1
+ # Setup and updates
2
+
3
+ Load only when runtime/workspace is absent, binding needs refreshing after update, or adapter scope changes.
4
+
5
+ ## Repository runtime
6
+
7
+ Use `<repository>/packages/x-harness/dist/index.js` as an absolute file URL; if absent run `pnpm --filter @ai-content-studio/x-harness build`. Reuse original ledger/proofs and runtime-context entry; do not initialize a second ledger or download another runtime. Global Skill links to this repository owner.
8
+
9
+ ## Independent installation
10
+
11
+ Requires Node 22+, compatible CUA and own X login. Run `npx @nextclaw/x-harness@latest setup --target codex` (or `--target claude`); use returned moduleURL/cliPath. Setup substitutes placeholders. Skill text does not add browser access. Missing host/login is a prerequisite, not permission to invent execution.
12
+
13
+ Initialize once: `node <cliPath> init --directory <user workspace> --account <user handle>`. Reuse returned ledgerPath/proofDirectory/taskId; initialization plans zero writes. Existing project state remains its owner.
14
+
15
+ ## Adapter and parallel scope
16
+
17
+ Host owns tab/app acquisition: read its current documentation once and reuse the valid handle. Tab needs url(), playwright.evaluate() and locators; native app AX/click/key/paste. Do not launch a separate driver.
18
+
19
+ Native binding: `{app,ledgerPath,proofDirectory}` instead of tab. Independent task tabs may add `executor:{id:taskId,scopeKinds:['original'],evidenceRef:scopeProof}` using actual authorized scope/proof; kinds are original/reply/follow. Never borrow another executor's editor or claim.
20
+
21
+ ## Update
22
+
23
+ Independent installs run `npx @nextclaw/x-harness@latest update --target codex` (or claude); it backs up managed runtime/Skill and preserves workspaces. Repository users update the original repository owner. Rebind using the new versioned moduleURL returned by update or written into this Skill; it refreshes the whole dependency graph in a persistent REPL. Never reuse the previous URL or add a root-only query string. Preserve pending actions/editors; reacquire the task tab after a necessary host reset. Verify one necessary read with the new module, not all documents. Distributed Skill/runtime share a version. Free use follows bundled proprietary license; TS source remains private.
@@ -0,0 +1,51 @@
1
+ # Writes and rounds
2
+
3
+ Load common review plus only the operation heading needed. Existing project contract owns authorization, identity, budgets, policy and original ledger plan. Independent initialization has zero planned writes; set only authorized counts in that ledger before executing. Current runtime window is 07:00–24:00 Asia/Shanghai for replies and 07:00–22:00 for other writes; follow gates require reviewed Chinese-language blue-check candidates. Do not expand scope. Examples below use existing real observations/proof; they are not executable authorization.
4
+
5
+ ## Common review
6
+
7
+ A prepared action has id (stable original ID), status:'prepared', accountHandle, accountURL, accountEvidenceRef and quality. All proof references are existing files; URLs/identity must be actually observed. Already prepared ledger actions need not be prepared again.
8
+
9
+ Content quality: `{approved:true,approvedText:text,evidenceRef:reviewProof,draftFingerprint}`; original fingerprint is SHA-256 of exact draft file bytes. Use Node crypto/createHash and fs/readFileSync locally; this requires no runtime source. Reply quality needs approvedText/proof; original also needs draftFingerprint. Any changed text/file needs a fresh review. Follow quality: `{approved:true,handle,blue:true,chinese:true,evidenceRef:reviewProof}`; verify evidence, never infer blue from generic certification.
10
+
11
+ Action APIs return status/actionId/publicURL/evidenceRef and reason/missing/resumeAt when relevant. Verified requires public proof. Unknown loads recovery, never resubmits. Business calls own intent, submit once, fresh verification and original settlement.
12
+
13
+ ## Publish
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.
16
+
17
+ ```js
18
+ var result = await x.publishPost({ taskId, draftPath, preparedAction: {
19
+ id: actionId, status: 'prepared', kind: 'original',
20
+ accountHandle, accountURL, accountEvidenceRef,
21
+ quality: { approved: true, approvedText: text, evidenceRef: reviewProof, draftFingerprint },
22
+ draftPath, text, composeURL, composeEvidenceRef,
23
+ } });
24
+ ```
25
+
26
+ Optional independentExecution:{tabId,evidenceRef,scopeEvidenceRef} requires real independent task-tab/scope proof; binding executor scope is covered in setup only if needed. Existing original action can omit preparedAction; draftPath still required.
27
+
28
+ ## Reply
29
+
30
+ Additional fields: kind:'reply', postId, url, evidenceRef (observed parent), text; optional assets/activation as above. Common account/quality fields still required. The exact parent is verified by the API.
31
+
32
+ ```js
33
+ var result = await x.replyToPost({ taskId, postId, text, preparedAction: {
34
+ id: actionId, status: 'prepared', kind: 'reply',
35
+ accountHandle, accountURL, accountEvidenceRef,
36
+ quality: { approved: true, approvedText: text, evidenceRef: reviewProof },
37
+ postId, url: parentURL, evidenceRef: parentProof, text,
38
+ } });
39
+ ```
40
+
41
+ ## Follow
42
+
43
+ Additional fields: kind:'follow', handle, url, evidenceRef (observed person), plus common account fields and follow quality. Batch uses `{taskId,handles,preparedActions?}`; original contract determines allowed batch sizes. Each preparedAction corresponds to its handle. If already prepared, omit preparedActions. `await x.followOne({taskId,handle})` requires its original prepared action and project-permitted single-action scope. A preexisting relationship is not a new verified follow; honor result counts.
44
+
45
+ ## Round
46
+
47
+ `await x.inspectRound({taskId})` inspects original state; `await x.runGrowthRound({taskId,maxActions})` runs/resumes it. maxActions bounds this call, not original planned total. Preparation uses decision:{type:'prepareActions',accountHandle,ownershipEvidenceRef,actions}; actions are the reviewed structures above.
48
+
49
+ Optional decision fields: attemptsCoverage:{complete,since,evidenceRef}, sharedAttempts:[{kind,at}], nextScheduledCheckAt. For follow budgets, coverage must actually cover the required window; unknown history cannot be recorded as zero. Missing data remains a decision gap.
50
+
51
+ Review an existing action with decision:{actionId,approved,evidenceRef,approvedText?,draftFingerprint?,blue?,chinese?}; use content fields for content, blue/chinese for follow. approved:false skips that candidate without filling original planned count. Preserve intent/unknown/verified states and IDs. Read returned gaps/decision/mayEnd; partial success is not whole-round completion.