@nextclaw/x-harness 0.1.0 → 0.2.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 +231 -6
- package/README.md +17 -3
- package/SOURCE.md +9 -0
- package/cli/main.js +232 -188
- package/dist/api.d.ts +56 -2
- package/dist/api.js +178 -75
- package/dist/boundary.js +1 -0
- package/dist/browser-common.d.ts +11 -0
- package/dist/browser-common.js +34 -18
- package/dist/browser-dom-capabilities.d.ts +9 -0
- package/dist/browser-dom-capabilities.js +131 -0
- package/dist/browser-dom-dependencies.d.ts +16 -0
- package/dist/browser-dom-dependencies.js +1 -0
- package/dist/browser-dom-editing.d.ts +166 -0
- package/dist/browser-dom-editing.js +418 -0
- package/dist/browser-dom-navigation.d.ts +18 -0
- package/dist/browser-dom-navigation.js +181 -0
- package/dist/browser-dom-network.d.ts +1 -0
- package/dist/browser-dom-network.js +128 -0
- package/dist/browser-dom-submission.d.ts +7 -0
- package/dist/browser-dom-submission.js +101 -0
- package/dist/browser-dom.d.ts +14 -2
- package/dist/browser-dom.js +120 -824
- package/dist/browser-native.d.ts +14 -1
- package/dist/browser-native.js +180 -143
- package/dist/browser.js +6 -2
- package/dist/creator-native.d.ts +3 -0
- package/dist/creator-native.js +113 -0
- package/dist/creator.js +1 -1
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.js +10 -0
- package/dist/growth-batch.d.ts +8 -0
- package/dist/growth-batch.js +190 -0
- package/dist/growth-constants.d.ts +5 -0
- package/dist/growth-constants.js +13 -0
- package/dist/growth-dependencies.d.ts +33 -0
- package/dist/growth-dependencies.js +1 -0
- package/dist/growth-execution.d.ts +17 -0
- package/dist/growth-execution.js +346 -0
- package/dist/growth-parallel.d.ts +8 -0
- package/dist/growth-parallel.js +332 -0
- package/dist/growth-plan.d.ts +9 -0
- package/dist/growth-plan.js +229 -0
- package/dist/growth-policy.d.ts +18 -0
- package/dist/growth-policy.js +208 -0
- package/dist/growth-publishing.d.ts +10 -0
- package/dist/growth-publishing.js +266 -0
- package/dist/growth-reading.d.ts +8 -0
- package/dist/growth-reading.js +157 -0
- package/dist/growth-recovery.d.ts +9 -0
- package/dist/growth-recovery.js +187 -0
- package/dist/growth-state.d.ts +16 -0
- package/dist/growth-state.js +220 -0
- package/dist/growth-tasks.d.ts +20 -0
- package/dist/growth-tasks.js +164 -0
- package/dist/growth.d.ts +26 -1
- package/dist/growth.js +211 -1908
- package/dist/platform-capabilities.d.ts +8 -0
- package/dist/platform-capabilities.js +29 -0
- package/dist/platform-collections.d.ts +14 -0
- package/dist/platform-collections.js +250 -0
- package/dist/platform-dependencies.d.ts +43 -0
- package/dist/platform-dependencies.js +1 -0
- package/dist/platform-diagnostics.d.ts +14 -0
- package/dist/platform-diagnostics.js +63 -0
- package/dist/platform-observation.d.ts +16 -0
- package/dist/platform-observation.js +96 -0
- package/dist/platform-rules.d.ts +17 -0
- package/dist/platform-rules.js +41 -0
- package/dist/platform-targets.d.ts +14 -0
- package/dist/platform-targets.js +154 -0
- package/dist/platform-writing.d.ts +16 -0
- package/dist/platform-writing.js +202 -0
- package/dist/platform.d.ts +23 -2
- package/dist/platform.js +125 -678
- package/dist/readers-actions.d.ts +2 -0
- package/dist/readers-actions.js +66 -0
- package/dist/readers-ax-host.d.ts +12 -0
- package/dist/readers-ax-host.js +134 -0
- package/dist/readers-ax-parser.d.ts +1 -0
- package/dist/readers-ax-parser.js +69 -0
- package/dist/readers-ax-program.d.ts +1 -0
- package/dist/readers-ax-program.js +5 -0
- package/dist/readers-ax-tree.d.ts +19 -0
- package/dist/readers-ax-tree.js +111 -0
- package/dist/readers-ax-views.d.ts +87 -0
- package/dist/readers-ax-views.js +247 -0
- package/dist/readers-cli.d.ts +6 -0
- package/dist/readers-cli.js +79 -0
- package/dist/readers-composer.d.ts +2 -0
- package/dist/readers-composer.js +131 -0
- package/dist/readers-navigation.d.ts +2 -0
- package/dist/readers-navigation.js +66 -0
- package/dist/readers-posts.d.ts +2 -0
- package/dist/readers-posts.js +141 -0
- package/dist/readers-profile.d.ts +3 -0
- package/dist/readers-profile.js +157 -0
- package/dist/readers.d.ts +9 -20
- package/dist/readers.js +9 -1045
- package/dist/results.js +1 -1
- package/dist/round-state.js +20 -3
- package/dist/store.d.ts +22 -4
- package/dist/store.js +173 -151
- package/dist/types.d.ts +10 -0
- package/dist/types.js +1 -0
- package/generated/observation.mjs +3 -2
- package/package.json +23 -9
- package/skill/SKILL.md +17 -36
- package/skill/references/api.md +7 -34
- package/skill/references/reads.md +23 -0
- package/skill/references/recovery.md +25 -0
- package/skill/references/setup.md +23 -0
- package/skill/references/writes.md +51 -0
package/cli/main.js
CHANGED
|
@@ -1,207 +1,251 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync, renameSync, rmSync, cpSync, mkdtempSync, openSync, closeSync, unlinkSync, readdirSync, } from "node:fs";
|
|
3
|
-
import { join, resolve, dirname } from "node:path";
|
|
3
|
+
import { join, resolve, dirname, relative } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
|
-
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
|
-
const release = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
|
|
9
|
-
const args = process.argv.slice(2);
|
|
10
|
-
const command = args.shift() ?? "help";
|
|
11
|
-
const flags = new Map();
|
|
12
|
-
for (let i = 0; i < args.length; i += 2) {
|
|
13
|
-
const key = args[i], value = args[i + 1];
|
|
14
|
-
if (!["--target", "--home", "--directory", "--account"].includes(key) ||
|
|
15
|
-
!value ||
|
|
16
|
-
value.startsWith("--") ||
|
|
17
|
-
flags.has(key))
|
|
18
|
-
throw Error(`Invalid argument: ${key}`);
|
|
19
|
-
flags.set(key, value);
|
|
20
|
-
}
|
|
21
|
-
const customHome = flags.get("--home");
|
|
22
|
-
const home = resolve(customHome ?? join(homedir(), ".x-harness"));
|
|
23
7
|
const readJSON = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
24
8
|
const output = (value) => process.stdout.write(JSON.stringify(value, null, 2) + "\n");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
9
|
+
class XHarnessCLI {
|
|
10
|
+
#packageRoot;
|
|
11
|
+
#release;
|
|
12
|
+
#args;
|
|
13
|
+
#command;
|
|
14
|
+
#flags;
|
|
15
|
+
#customHome;
|
|
16
|
+
#home;
|
|
17
|
+
constructor(argv = process.argv.slice(2)) {
|
|
18
|
+
this.#packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
19
|
+
this.#release = JSON.parse(readFileSync(join(this.#packageRoot, "package.json"), "utf8"));
|
|
20
|
+
this.#args = argv.slice();
|
|
21
|
+
this.#command = this.#args.shift() ?? "help";
|
|
22
|
+
this.#flags = new Map();
|
|
23
|
+
for (let i = 0; i < this.#args.length; i += 2) {
|
|
24
|
+
const key = this.#args[i], value = this.#args[i + 1];
|
|
25
|
+
if (!["--target", "--home", "--directory", "--account"].includes(key) ||
|
|
26
|
+
!value ||
|
|
27
|
+
value.startsWith("--") ||
|
|
28
|
+
this.#flags.has(key))
|
|
29
|
+
throw Error(`Invalid argument: ${key}`);
|
|
30
|
+
this.#flags.set(key, value);
|
|
31
|
+
}
|
|
32
|
+
this.#customHome = this.#flags.get("--home");
|
|
33
|
+
this.#home = resolve(this.#customHome ?? join(homedir(), ".x-harness"));
|
|
34
|
+
}
|
|
35
|
+
marker(path, file) {
|
|
36
|
+
return (existsSync(join(path, file)) &&
|
|
37
|
+
[this.#release.name, "@peiiii/x-harness"].includes(readJSON(join(path, file)).managedBy));
|
|
38
|
+
}
|
|
39
|
+
guard(path, file) {
|
|
40
|
+
if (existsSync(path) &&
|
|
41
|
+
(lstatSync(path).isSymbolicLink() || !this.marker(path, file)))
|
|
42
|
+
throw Error(`Refusing to overwrite unmanaged directory: ${path}`);
|
|
43
|
+
}
|
|
44
|
+
skillPath(target) {
|
|
45
|
+
if (!["codex", "claude"].includes(target))
|
|
46
|
+
throw Error("Target must be codex or claude");
|
|
47
|
+
return this.#customHome
|
|
48
|
+
? join(this.#home, "skills", target, "x-harness")
|
|
49
|
+
: join(homedir(), `.${target}`, "skills", "x-harness");
|
|
50
|
+
}
|
|
51
|
+
location() {
|
|
52
|
+
return {
|
|
53
|
+
version: this.#release.version,
|
|
54
|
+
moduleURL: pathToFileURL(join(this.#packageRoot, "dist", "index.js"))
|
|
55
|
+
.href,
|
|
56
|
+
cliPath: fileURLToPath(import.meta.url),
|
|
57
|
+
home: this.#home,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
stageRuntime(stage, runtime) {
|
|
69
61
|
const stagedRuntime = join(stage, "runtime");
|
|
70
|
-
|
|
71
|
-
const installedRoot = join(stagedRuntime,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
const suffix = join("versions", this.#release.version, "node_modules", ...this.#release.name.split("/"));
|
|
63
|
+
const installedRoot = join(stagedRuntime, suffix);
|
|
64
|
+
cpSync(this.#packageRoot, installedRoot, { recursive: true });
|
|
65
|
+
if (readJSON(join(installedRoot, "package.json")).version !==
|
|
66
|
+
this.#release.version)
|
|
75
67
|
throw Error("Installed version mismatch");
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
68
|
+
const finalRoot = join(runtime, suffix);
|
|
69
|
+
const stableRoot = join(stagedRuntime, "node_modules", ...this.#release.name.split("/"));
|
|
70
|
+
const stableCLI = join(runtime, "node_modules", ...this.#release.name.split("/"), "cli", "main.js");
|
|
71
|
+
mkdirSync(join(stableRoot, "cli"), { recursive: true });
|
|
72
|
+
writeFileSync(join(stableRoot, "package.json"), JSON.stringify({ type: "module" }));
|
|
73
|
+
const entry = relative(dirname(stableCLI), join(finalRoot, "cli", "main.js"))
|
|
74
|
+
.split("\\")
|
|
75
|
+
.join("/");
|
|
76
|
+
writeFileSync(join(stableRoot, "cli", "main.js"), `#!/usr/bin/env node\nawait import(new URL(${JSON.stringify(entry)}, import.meta.url));\n`);
|
|
77
|
+
return { stagedRuntime, installedRoot, finalRoot, stableCLI };
|
|
78
|
+
}
|
|
79
|
+
setup() {
|
|
80
|
+
const target = this.#flags.get("--target") ?? "codex";
|
|
81
|
+
const stateFile = join(this.#home, "installation.json");
|
|
82
|
+
if (!existsSync(stateFile) &&
|
|
83
|
+
readdirSync(this.#home).some((name) => name !== ".installation.lock"))
|
|
84
|
+
throw Error(`Unmanaged installation home: ${this.#home}`);
|
|
85
|
+
const previous = existsSync(stateFile)
|
|
86
|
+
? readJSON(stateFile)
|
|
87
|
+
: { managedBy: this.#release.name, targets: [] };
|
|
88
|
+
if (![this.#release.name, "@peiiii/x-harness"].includes(previous.managedBy))
|
|
89
|
+
throw Error("Unmanaged installation state");
|
|
90
|
+
const targets = [
|
|
91
|
+
...new Set([...previous.targets, target]),
|
|
92
|
+
];
|
|
93
|
+
const runtime = join(this.#home, "runtime");
|
|
94
|
+
this.guard(runtime, ".x-harness-managed.json");
|
|
95
|
+
for (const t of targets)
|
|
96
|
+
this.guard(this.skillPath(t), ".x-harness-managed.json");
|
|
97
|
+
mkdirSync(this.#home, { recursive: true, mode: 0o700 });
|
|
98
|
+
const stage = mkdtempSync(join(this.#home, ".setup-"));
|
|
99
|
+
const backup = join(this.#home, "backups", `${Date.now()}-${randomUUID()}`);
|
|
100
|
+
const changes = [];
|
|
101
|
+
let completed = false;
|
|
102
|
+
try {
|
|
103
|
+
// Persist npx's already fetched runtime once; versioned URLs invalidate the whole module graph.
|
|
104
|
+
const { stagedRuntime, installedRoot, finalRoot, stableCLI } = this.stageRuntime(stage, runtime);
|
|
105
|
+
const metadata = {
|
|
106
|
+
managedBy: this.#release.name,
|
|
107
|
+
version: this.#release.version,
|
|
108
|
+
};
|
|
109
|
+
writeFileSync(join(stagedRuntime, ".x-harness-managed.json"), JSON.stringify(metadata));
|
|
110
|
+
const installs = [{ src: stagedRuntime, dest: runtime, key: "runtime" }];
|
|
111
|
+
for (const t of targets) {
|
|
112
|
+
const stagedSkill = join(stage, `skill-${t}`);
|
|
113
|
+
cpSync(join(installedRoot, "skill"), stagedSkill, { recursive: true });
|
|
114
|
+
const content = readFileSync(join(stagedSkill, "SKILL.md"), "utf8")
|
|
115
|
+
.replaceAll("{{MODULE_URL}}", pathToFileURL(join(finalRoot, "dist", "index.js")).href)
|
|
116
|
+
.replaceAll("{{CLI_PATH}}", stableCLI);
|
|
117
|
+
writeFileSync(join(stagedSkill, "SKILL.md"), content);
|
|
118
|
+
writeFileSync(join(stagedSkill, ".x-harness-managed.json"), JSON.stringify(metadata));
|
|
119
|
+
installs.push({
|
|
120
|
+
src: stagedSkill,
|
|
121
|
+
dest: this.skillPath(t),
|
|
122
|
+
key: `skill-${t}`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Install/download succeeds before replacing either managed artifact.
|
|
126
|
+
for (const item of installs) {
|
|
127
|
+
const saved = join(backup, item.key), hadOld = existsSync(item.dest);
|
|
128
|
+
mkdirSync(dirname(item.dest), { recursive: true });
|
|
129
|
+
if (hadOld) {
|
|
130
|
+
mkdirSync(backup, { recursive: true });
|
|
131
|
+
renameSync(item.dest, saved);
|
|
132
|
+
}
|
|
133
|
+
changes.push({ dest: item.dest, backup: saved, hadOld });
|
|
134
|
+
renameSync(item.src, item.dest);
|
|
135
|
+
}
|
|
136
|
+
const state = {
|
|
137
|
+
...metadata,
|
|
138
|
+
targets,
|
|
139
|
+
updatedAt: new Date().toISOString(),
|
|
140
|
+
};
|
|
141
|
+
const nextState = join(stage, "installation.json");
|
|
142
|
+
writeFileSync(nextState, JSON.stringify(state, null, 2));
|
|
143
|
+
renameSync(nextState, stateFile);
|
|
144
|
+
completed = true;
|
|
145
|
+
output({
|
|
146
|
+
status: "installed",
|
|
147
|
+
...state,
|
|
148
|
+
moduleURL: pathToFileURL(join(finalRoot, "dist", "index.js")).href,
|
|
149
|
+
cliPath: stableCLI,
|
|
150
|
+
skillPaths: targets.map(this.skillPath.bind(this)),
|
|
151
|
+
backup: changes.some((c) => c.hadOld) ? backup : null,
|
|
152
|
+
next: "Start a fresh module binding using the installed x-harness Skill.",
|
|
92
153
|
});
|
|
93
154
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
changes.push({ dest: item.dest, backup: saved, hadOld });
|
|
103
|
-
renameSync(item.src, item.dest);
|
|
155
|
+
finally {
|
|
156
|
+
if (!completed)
|
|
157
|
+
for (const change of changes.reverse()) {
|
|
158
|
+
rmSync(change.dest, { recursive: true, force: true });
|
|
159
|
+
if (change.hadOld)
|
|
160
|
+
renameSync(change.backup, change.dest);
|
|
161
|
+
}
|
|
162
|
+
rmSync(stage, { recursive: true, force: true });
|
|
104
163
|
}
|
|
105
|
-
const state = { ...metadata, targets, updatedAt: new Date().toISOString() };
|
|
106
|
-
const nextState = join(stage, "installation.json");
|
|
107
|
-
writeFileSync(nextState, JSON.stringify(state, null, 2));
|
|
108
|
-
renameSync(nextState, stateFile);
|
|
109
|
-
completed = true;
|
|
110
|
-
output({
|
|
111
|
-
status: "installed",
|
|
112
|
-
...state,
|
|
113
|
-
moduleURL: pathToFileURL(join(finalRoot, "dist", "index.js")).href,
|
|
114
|
-
cliPath: join(finalRoot, "cli", "main.js"),
|
|
115
|
-
skillPaths: targets.map(skillPath),
|
|
116
|
-
backup: changes.some((c) => c.hadOld) ? backup : null,
|
|
117
|
-
next: "Start a fresh module binding using the installed x-harness Skill.",
|
|
118
|
-
});
|
|
119
164
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
165
|
+
init() {
|
|
166
|
+
const directory = resolve(this.#flags.get("--directory") ?? "./x-workspace");
|
|
167
|
+
const account = (this.#flags.get("--account") ?? "").replace(/^@/, "");
|
|
168
|
+
if (!/^[a-zA-Z0-9_]{1,15}$/.test(account))
|
|
169
|
+
throw Error("Provide your own X handle with --account");
|
|
170
|
+
const configPath = join(directory, "x-harness.json");
|
|
171
|
+
if (existsSync(configPath)) {
|
|
172
|
+
const config = readJSON(configPath);
|
|
173
|
+
if (config.accountHandle !== account)
|
|
174
|
+
throw Error("Existing workspace belongs to a different account");
|
|
175
|
+
output({ status: "existing", configPath, ...config });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
179
|
+
const ledgerPath = join(directory, "round.md"), proofDirectory = join(directory, "proofs");
|
|
180
|
+
if (existsSync(ledgerPath))
|
|
181
|
+
throw Error("Existing ledger is unmanaged; use its configuration instead of overwriting");
|
|
182
|
+
const taskId = `x-${randomUUID()}`;
|
|
183
|
+
const state = {
|
|
184
|
+
batchId: taskId,
|
|
185
|
+
status: "RUNNING",
|
|
186
|
+
updatedAt: new Date().toISOString(),
|
|
187
|
+
accountHandle: account,
|
|
188
|
+
items: ["follow", "original", "reply"].map((kind) => ({
|
|
189
|
+
kind,
|
|
190
|
+
planned: 0,
|
|
191
|
+
verified: 0,
|
|
192
|
+
unknown: 0,
|
|
193
|
+
actionable: true,
|
|
194
|
+
})),
|
|
195
|
+
execution: { owner: "harness", exclusive: true },
|
|
196
|
+
attempts: [],
|
|
197
|
+
actions: [],
|
|
198
|
+
};
|
|
199
|
+
mkdirSync(proofDirectory);
|
|
200
|
+
writeFileSync(ledgerPath, `# @${account} · Your X workspace\n\n\`\`\`x-round-state\n${JSON.stringify(state, null, 2)}\n\`\`\`\n`, { flag: "wx", mode: 0o600 });
|
|
201
|
+
const config = {
|
|
202
|
+
accountHandle: account,
|
|
203
|
+
taskId,
|
|
204
|
+
ledgerPath,
|
|
205
|
+
proofDirectory,
|
|
206
|
+
};
|
|
207
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
208
|
+
flag: "wx",
|
|
209
|
+
mode: 0o600,
|
|
210
|
+
});
|
|
211
|
+
output({ status: "initialized", configPath, ...config });
|
|
142
212
|
}
|
|
143
|
-
|
|
144
|
-
const ledgerPath = join(directory, "round.md"), proofDirectory = join(directory, "proofs");
|
|
145
|
-
if (existsSync(ledgerPath))
|
|
146
|
-
throw Error("Existing ledger is unmanaged; use its configuration instead of overwriting");
|
|
147
|
-
const taskId = `x-${randomUUID()}`;
|
|
148
|
-
const state = {
|
|
149
|
-
batchId: taskId,
|
|
150
|
-
status: "RUNNING",
|
|
151
|
-
updatedAt: new Date().toISOString(),
|
|
152
|
-
accountHandle: account,
|
|
153
|
-
items: ["follow", "original", "reply"].map((kind) => ({
|
|
154
|
-
kind,
|
|
155
|
-
planned: 0,
|
|
156
|
-
verified: 0,
|
|
157
|
-
unknown: 0,
|
|
158
|
-
actionable: true,
|
|
159
|
-
})),
|
|
160
|
-
execution: { owner: "harness", exclusive: true },
|
|
161
|
-
attempts: [],
|
|
162
|
-
actions: [],
|
|
163
|
-
};
|
|
164
|
-
mkdirSync(proofDirectory);
|
|
165
|
-
writeFileSync(ledgerPath, `# @${account} · Your X workspace\n\n\`\`\`x-round-state\n${JSON.stringify(state, null, 2)}\n\`\`\`\n`, { flag: "wx", mode: 0o600 });
|
|
166
|
-
const config = { accountHandle: account, taskId, ledgerPath, proofDirectory };
|
|
167
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
168
|
-
flag: "wx",
|
|
169
|
-
mode: 0o600,
|
|
170
|
-
});
|
|
171
|
-
output({ status: "initialized", configPath, ...config });
|
|
172
|
-
}
|
|
173
|
-
try {
|
|
174
|
-
if (["setup", "update"].includes(command)) {
|
|
175
|
-
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
176
|
-
const lockPath = join(home, ".installation.lock");
|
|
177
|
-
const lock = openSync(lockPath, "wx", 0o600);
|
|
213
|
+
run() {
|
|
178
214
|
try {
|
|
179
|
-
setup()
|
|
215
|
+
if (["setup", "update"].includes(this.#command)) {
|
|
216
|
+
mkdirSync(this.#home, { recursive: true, mode: 0o700 });
|
|
217
|
+
const lockPath = join(this.#home, ".installation.lock");
|
|
218
|
+
const lock = openSync(lockPath, "wx", 0o600);
|
|
219
|
+
try {
|
|
220
|
+
this.setup();
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
closeSync(lock);
|
|
224
|
+
unlinkSync(lockPath);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (this.#command === "init")
|
|
228
|
+
this.init();
|
|
229
|
+
else if (this.#command === "location")
|
|
230
|
+
output(this.location());
|
|
231
|
+
else if (this.#command === "help")
|
|
232
|
+
output({
|
|
233
|
+
setup: "npx @nextclaw/x-harness@latest setup --target codex",
|
|
234
|
+
update: "npx @nextclaw/x-harness@latest update --target codex",
|
|
235
|
+
init: "node <cliPath> init --directory ./x-workspace --account YOUR_HANDLE",
|
|
236
|
+
targets: ["codex", "claude"],
|
|
237
|
+
requires: "Node 22+; compatible CUA browser host and your own X login",
|
|
238
|
+
});
|
|
239
|
+
else
|
|
240
|
+
throw Error(`Unknown command: ${this.#command}`);
|
|
180
241
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
242
|
+
catch (error) {
|
|
243
|
+
process.stderr.write(JSON.stringify({
|
|
244
|
+
status: "failed",
|
|
245
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
246
|
+
}) + "\n");
|
|
247
|
+
process.exitCode = 1;
|
|
184
248
|
}
|
|
185
249
|
}
|
|
186
|
-
else if (command === "init")
|
|
187
|
-
init();
|
|
188
|
-
else if (command === "location")
|
|
189
|
-
output(location());
|
|
190
|
-
else if (command === "help")
|
|
191
|
-
output({
|
|
192
|
-
setup: "npx @nextclaw/x-harness@latest setup --target codex",
|
|
193
|
-
update: "npx @nextclaw/x-harness@latest update --target codex",
|
|
194
|
-
init: "node <cliPath> init --directory ./x-workspace --account YOUR_HANDLE",
|
|
195
|
-
targets: ["codex", "claude"],
|
|
196
|
-
requires: "Node 22+; compatible CUA browser host and your own X login",
|
|
197
|
-
});
|
|
198
|
-
else
|
|
199
|
-
throw Error(`Unknown command: ${command}`);
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
process.stderr.write(JSON.stringify({
|
|
203
|
-
status: "failed",
|
|
204
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
205
|
-
}) + "\n");
|
|
206
|
-
process.exitCode = 1;
|
|
207
250
|
}
|
|
251
|
+
new XHarnessCLI().run();
|
package/dist/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { XGrowthHarness } from "./types.js";
|
|
1
|
+
import type { ActionResult, ReadMode, ReadResult, RoundResult, XGrowthHarness, BatchResult } from "./types.js";
|
|
2
2
|
import type { BoundaryRecord } from "./boundary.js";
|
|
3
3
|
/** Internal dependencies; only the normal X factory is exported from the root. */
|
|
4
4
|
export interface BusinessDependencies {
|
|
@@ -7,4 +7,58 @@ export interface BusinessDependencies {
|
|
|
7
7
|
clock?: () => number;
|
|
8
8
|
executor?: BoundaryRecord;
|
|
9
9
|
}
|
|
10
|
-
export declare
|
|
10
|
+
export declare class BusinessHarness implements XGrowthHarness {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(dependencies: BusinessDependencies);
|
|
13
|
+
private invoke;
|
|
14
|
+
private read;
|
|
15
|
+
private compactRound;
|
|
16
|
+
private single;
|
|
17
|
+
readCreator(args: import("./types.js").CreatorOptions): Promise<ReadResult>;
|
|
18
|
+
readBookmarks(args: import("./types.js").BookmarkOptions): Promise<ReadResult>;
|
|
19
|
+
searchSources(args: import("./types.js").SearchOptions): Promise<ReadResult>;
|
|
20
|
+
readPost({ target, mode, }: {
|
|
21
|
+
target: import("./types.js").PostTarget;
|
|
22
|
+
mode?: ReadMode;
|
|
23
|
+
}): Promise<ReadResult>;
|
|
24
|
+
readPerson({ target, mode, }: {
|
|
25
|
+
target: import("./types.js").PersonTarget;
|
|
26
|
+
mode?: ReadMode;
|
|
27
|
+
}): Promise<ReadResult>;
|
|
28
|
+
readFeedback(args: import("./types.js").FeedbackOptions): Promise<ReadResult>;
|
|
29
|
+
inspectAccount(args?: {
|
|
30
|
+
mode?: ReadMode;
|
|
31
|
+
} | undefined): Promise<ReadResult>;
|
|
32
|
+
inspectRound(args: {
|
|
33
|
+
taskId: string;
|
|
34
|
+
}): Promise<RoundResult>;
|
|
35
|
+
runGrowthRound(args: import("./types.js").RoundOptions): Promise<RoundResult>;
|
|
36
|
+
publishPost(args: import("./types.js").PublishOptions): Promise<ActionResult>;
|
|
37
|
+
replyToPost(args: import("./types.js").ReplyOptions): Promise<ActionResult>;
|
|
38
|
+
followOne(args: {
|
|
39
|
+
taskId: string;
|
|
40
|
+
handle: string;
|
|
41
|
+
}): Promise<ActionResult>;
|
|
42
|
+
verifySubmission(args: {
|
|
43
|
+
taskId: string;
|
|
44
|
+
actionId: string;
|
|
45
|
+
}): Promise<ActionResult>;
|
|
46
|
+
verifyPublishedPost(args: {
|
|
47
|
+
taskId: string;
|
|
48
|
+
draftPath: string;
|
|
49
|
+
publicURL: string;
|
|
50
|
+
evidenceRef: import("./types.js").EvidenceRef;
|
|
51
|
+
sourceLink?: import("./types.js").SourceLinkEvidence;
|
|
52
|
+
}): Promise<ActionResult>;
|
|
53
|
+
followBatch(args: import("./types.js").FollowOptions): Promise<BatchResult>;
|
|
54
|
+
verifyFollow(args: {
|
|
55
|
+
handle: string;
|
|
56
|
+
url?: string;
|
|
57
|
+
evidenceRef?: import("./types.js").EvidenceRef;
|
|
58
|
+
mode?: "receipt" | "decision";
|
|
59
|
+
}): Promise<ReadResult & {
|
|
60
|
+
handle: string;
|
|
61
|
+
verified: boolean;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
export declare function createBusinessHarness(...args: ConstructorParameters<typeof BusinessHarness>): XGrowthHarness;
|