@nextclaw/x-harness 0.1.1 → 0.2.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.
- package/LICENSE +231 -6
- package/README.md +15 -1
- package/SOURCE.md +9 -0
- package/cli/main.js +232 -200
- package/dist/api.d.ts +56 -2
- package/dist/api.js +130 -79
- 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 +7 -2
- package/dist/browser-dom-capabilities.js +123 -102
- package/dist/browser-dom-dependencies.js +1 -0
- package/dist/browser-dom-editing.d.ts +20 -2
- package/dist/browser-dom-editing.js +148 -116
- package/dist/browser-dom-navigation.d.ts +11 -2
- package/dist/browser-dom-navigation.js +66 -50
- package/dist/browser-dom-submission.d.ts +6 -1
- package/dist/browser-dom-submission.js +37 -18
- package/dist/browser-dom.d.ts +13 -1
- package/dist/browser-dom.js +113 -66
- package/dist/browser-native.d.ts +14 -1
- package/dist/browser-native.js +161 -128
- package/dist/creator-native.d.ts +3 -0
- package/dist/creator-native.js +113 -0
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.js +10 -0
- package/dist/growth-batch.d.ts +6 -3
- package/dist/growth-batch.js +157 -135
- package/dist/growth-dependencies.js +1 -0
- package/dist/growth-execution.d.ts +8 -2
- package/dist/growth-execution.js +68 -37
- package/dist/growth-parallel.d.ts +6 -2
- package/dist/growth-parallel.js +78 -47
- package/dist/growth-plan.d.ts +8 -3
- package/dist/growth-plan.js +45 -23
- package/dist/growth-policy.d.ts +11 -2
- package/dist/growth-policy.js +80 -53
- package/dist/growth-publishing.d.ts +7 -2
- package/dist/growth-publishing.js +72 -46
- package/dist/growth-reading.d.ts +6 -2
- package/dist/growth-reading.js +47 -25
- package/dist/growth-recovery.d.ts +7 -4
- package/dist/growth-recovery.js +177 -148
- package/dist/growth-state.d.ts +10 -2
- package/dist/growth-state.js +67 -30
- package/dist/growth-tasks.d.ts +19 -16
- package/dist/growth-tasks.js +153 -90
- package/dist/growth.d.ts +25 -1
- package/dist/growth.js +207 -124
- package/dist/platform-capabilities.d.ts +6 -2
- package/dist/platform-capabilities.js +18 -8
- package/dist/platform-collections.d.ts +10 -3
- package/dist/platform-collections.js +62 -35
- package/dist/platform-dependencies.js +1 -0
- package/dist/platform-diagnostics.d.ts +7 -2
- package/dist/platform-diagnostics.js +31 -19
- package/dist/platform-observation.d.ts +10 -2
- package/dist/platform-observation.js +44 -23
- package/dist/platform-targets.d.ts +10 -3
- package/dist/platform-targets.js +39 -14
- package/dist/platform-writing.d.ts +10 -2
- package/dist/platform-writing.js +63 -32
- package/dist/platform.d.ts +22 -1
- package/dist/platform.js +120 -52
- package/dist/readers-ax-host.d.ts +11 -1
- package/dist/readers-ax-host.js +108 -99
- package/dist/readers-ax-program.js +5 -0
- package/dist/readers-ax-tree.js +1 -0
- package/dist/readers-cli.js +79 -0
- package/dist/readers.js +9 -0
- package/dist/round-state.js +18 -1
- package/dist/store.d.ts +22 -4
- package/dist/store.js +173 -151
- package/dist/types.js +1 -0
- package/generated/observation.mjs +1 -1
- package/package.json +23 -9
- package/skill/SKILL.md +2 -2
- package/skill/references/reads.md +1 -1
- package/skill/references/recovery.md +4 -4
- package/skill/references/setup.md +1 -1
package/dist/growth-policy.js
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import { xURL } from "./platform-rules.js";
|
|
2
2
|
import { kinds, followWindows, operations, same } from "./growth-constants.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
3
|
+
export class GrowthPolicy {
|
|
4
|
+
#executor;
|
|
5
|
+
#clock;
|
|
6
|
+
#evidenceExists;
|
|
7
|
+
#platform;
|
|
8
|
+
#store;
|
|
9
|
+
authorizedWindow;
|
|
10
|
+
scope;
|
|
11
|
+
project;
|
|
12
|
+
capability;
|
|
13
|
+
waitBudget;
|
|
14
|
+
guardInput;
|
|
15
|
+
constructor(dependencies) {
|
|
16
|
+
const { executor, clock, evidenceExists, platform, store } = dependencies;
|
|
17
|
+
this.#executor = executor;
|
|
18
|
+
this.#clock = clock;
|
|
19
|
+
this.#evidenceExists = evidenceExists;
|
|
20
|
+
this.#platform = platform;
|
|
21
|
+
this.#store = store;
|
|
22
|
+
this.authorizedWindow = this.handleAuthorizedWindow.bind(this);
|
|
23
|
+
this.scope = this.handleScope.bind(this);
|
|
24
|
+
this.project = this.handleProject.bind(this);
|
|
25
|
+
this.capability = this.handleCapability.bind(this);
|
|
26
|
+
this.waitBudget = this.handleWaitBudget.bind(this);
|
|
27
|
+
this.guardInput = this.handleGuardInput.bind(this);
|
|
28
|
+
}
|
|
29
|
+
handleCapability(operation, input = {}) {
|
|
28
30
|
const kind = input.kind ??
|
|
29
31
|
{
|
|
30
32
|
followOne: "follow",
|
|
@@ -44,17 +46,18 @@ export function createGrowthPolicy(dependencies) {
|
|
|
44
46
|
];
|
|
45
47
|
if (kind === "follow")
|
|
46
48
|
required.push("readPerson");
|
|
47
|
-
const missing = required.filter((name) => typeof platform?.[name] !== "function");
|
|
48
|
-
const storage = ["load", "save", "withLock", "recordProof"].filter((name) => typeof store?.[name] !==
|
|
49
|
+
const missing = required.filter((name) => typeof this.#platform?.[name] !== "function");
|
|
50
|
+
const storage = ["load", "save", "withLock", "recordProof"].filter((name) => typeof this.#store?.[name] !==
|
|
49
51
|
"function");
|
|
50
|
-
if (operation === "followBatch" &&
|
|
52
|
+
if (operation === "followBatch" &&
|
|
53
|
+
typeof this.#store?.transaction !== "function")
|
|
51
54
|
storage.push("transaction");
|
|
52
55
|
if (kind === "original" &&
|
|
53
56
|
operation !== "read" &&
|
|
54
|
-
typeof store?.fingerprint !== "function")
|
|
57
|
+
typeof this.#store?.fingerprint !== "function")
|
|
55
58
|
storage.push("fingerprint");
|
|
56
|
-
const lower = typeof platform?.preflight === "function"
|
|
57
|
-
? platform.preflight({
|
|
59
|
+
const lower = typeof this.#platform?.preflight === "function"
|
|
60
|
+
? this.#platform.preflight({
|
|
58
61
|
operation: operation === "read" ? "read" : operations[kind],
|
|
59
62
|
input,
|
|
60
63
|
})
|
|
@@ -73,8 +76,8 @@ export function createGrowthPolicy(dependencies) {
|
|
|
73
76
|
: lower.owner,
|
|
74
77
|
};
|
|
75
78
|
}
|
|
76
|
-
|
|
77
|
-
const now = clock();
|
|
79
|
+
handleWaitBudget(state) {
|
|
80
|
+
const now = this.#clock();
|
|
78
81
|
const hour = Number(new Intl.DateTimeFormat("en-GB", {
|
|
79
82
|
timeZone: "Asia/Shanghai",
|
|
80
83
|
hour: "2-digit",
|
|
@@ -89,7 +92,7 @@ export function createGrowthPolicy(dependencies) {
|
|
|
89
92
|
if (!coverage?.complete ||
|
|
90
93
|
!Number.isFinite(Date.parse(coverage.since)) ||
|
|
91
94
|
Date.parse(coverage.since) > now - 3600000 ||
|
|
92
|
-
!evidenceExists(coverage.evidenceRef))
|
|
95
|
+
!this.#evidenceExists(coverage.evidenceRef))
|
|
93
96
|
return {
|
|
94
97
|
status: "needsDecision",
|
|
95
98
|
reason: "shared-attempt-history-incomplete",
|
|
@@ -118,14 +121,15 @@ export function createGrowthPolicy(dependencies) {
|
|
|
118
121
|
};
|
|
119
122
|
return null;
|
|
120
123
|
}
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
handleGuardInput(state, action, independentExecution = undefined) {
|
|
125
|
+
const executor = this.#executor;
|
|
126
|
+
if (!independentExecution && !this.handleScope(state).includes(action.kind))
|
|
123
127
|
return "kind-outside-round-scope";
|
|
124
128
|
if (independentExecution) {
|
|
125
129
|
if (action.kind !== "original" ||
|
|
126
130
|
!independentExecution.tabId ||
|
|
127
|
-
!evidenceExists(independentExecution.evidenceRef) ||
|
|
128
|
-
!evidenceExists(independentExecution.scopeEvidenceRef))
|
|
131
|
+
!this.#evidenceExists(independentExecution.evidenceRef) ||
|
|
132
|
+
!this.#evidenceExists(independentExecution.scopeEvidenceRef))
|
|
129
133
|
return "independent-execution-proof-missing";
|
|
130
134
|
}
|
|
131
135
|
else if (!executor &&
|
|
@@ -133,49 +137,72 @@ export function createGrowthPolicy(dependencies) {
|
|
|
133
137
|
return "exclusive-account-execution-unconfirmed";
|
|
134
138
|
if (!same(state.accountHandle, action.accountHandle))
|
|
135
139
|
return "account-identity-mismatch";
|
|
136
|
-
if (!evidenceExists(action.accountEvidenceRef) ||
|
|
140
|
+
if (!this.#evidenceExists(action.accountEvidenceRef) ||
|
|
137
141
|
xURL(action.accountURL).pathname.toLowerCase() !==
|
|
138
142
|
`/${state.accountHandle}`.toLowerCase())
|
|
139
143
|
return "own-profile-proof-missing";
|
|
140
144
|
if (!action.quality?.approved ||
|
|
141
|
-
!evidenceExists(action.quality.evidenceRef))
|
|
145
|
+
!this.#evidenceExists(action.quality.evidenceRef))
|
|
142
146
|
return "quality-review-required";
|
|
143
147
|
if (action.kind === "follow") {
|
|
144
148
|
if (!same(action.quality.handle, action.handle) ||
|
|
145
149
|
action.quality.blue !== true ||
|
|
146
150
|
action.quality.chinese !== true)
|
|
147
151
|
return "follow-qualification-required";
|
|
148
|
-
if (!evidenceExists(action.evidenceRef))
|
|
152
|
+
if (!this.#evidenceExists(action.evidenceRef))
|
|
149
153
|
return "person-observation-proof-missing";
|
|
150
154
|
}
|
|
151
155
|
else {
|
|
152
156
|
if (!action.text || action.quality.approvedText !== action.text)
|
|
153
157
|
return "exact-text-quality-review-required";
|
|
154
158
|
if (action.kind === "original" &&
|
|
155
|
-
(!action.draftPath || !evidenceExists(action.draftPath)))
|
|
159
|
+
(!action.draftPath || !this.#evidenceExists(action.draftPath)))
|
|
156
160
|
return "draft-source-missing";
|
|
157
161
|
if (action.kind === "original" &&
|
|
158
162
|
(!action.draftPath.endsWith(".post.md") ||
|
|
159
163
|
action.quality.draftFingerprint !==
|
|
160
|
-
store.fingerprint(action.draftPath)))
|
|
164
|
+
this.#store.fingerprint(action.draftPath)))
|
|
161
165
|
return "draft-changed-since-quality-review";
|
|
162
166
|
if (action.kind === "original" &&
|
|
163
|
-
(!evidenceExists(action.composeEvidenceRef) ||
|
|
167
|
+
(!this.#evidenceExists(action.composeEvidenceRef) ||
|
|
164
168
|
xURL(action.composeURL).pathname !== "/compose/post"))
|
|
165
169
|
return "observed-composer-proof-missing";
|
|
166
170
|
if (action.kind === "reply" &&
|
|
167
171
|
(!/^\d+$/.test(action.postId ?? "") ||
|
|
168
|
-
!evidenceExists(action.evidenceRef)))
|
|
172
|
+
!this.#evidenceExists(action.evidenceRef)))
|
|
169
173
|
return "reply-target-proof-missing";
|
|
170
174
|
}
|
|
171
175
|
return null;
|
|
172
176
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
handleAuthorizedWindow(state, kind) {
|
|
178
|
+
const executor = this.#executor;
|
|
179
|
+
const permit = state.executions?.[executor?.id]?.windowAuthorization;
|
|
180
|
+
const now = this.#clock();
|
|
181
|
+
return Boolean(permit &&
|
|
182
|
+
permit.kind === kind &&
|
|
183
|
+
permit.executorId === executor?.id &&
|
|
184
|
+
permit.userAuthorized === true &&
|
|
185
|
+
this.#evidenceExists(permit.evidenceRef) &&
|
|
186
|
+
Date.parse(permit.startsAt) <= now &&
|
|
187
|
+
now < Date.parse(permit.expiresAt));
|
|
188
|
+
}
|
|
189
|
+
handleScope(state) {
|
|
190
|
+
const executor = this.#executor;
|
|
191
|
+
return executor?.scopeKinds ?? state.scopeKinds ?? kinds;
|
|
192
|
+
}
|
|
193
|
+
handleProject(state) {
|
|
194
|
+
const executor = this.#executor;
|
|
195
|
+
return executor
|
|
196
|
+
? {
|
|
197
|
+
...state,
|
|
198
|
+
scopeKinds: executor.scopeKinds,
|
|
199
|
+
scopeEvidenceRef: executor.evidenceRef,
|
|
200
|
+
status: state.executions?.[executor.id]?.status ?? "RUNNING",
|
|
201
|
+
stop: state.executions?.[executor.id]?.stop,
|
|
202
|
+
}
|
|
203
|
+
: state;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export function createGrowthPolicy(...args) {
|
|
207
|
+
return new GrowthPolicy(...args);
|
|
181
208
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { GrowthDependencies } from "./growth-dependencies.js";
|
|
2
|
-
export declare
|
|
2
|
+
export declare class IndependentPublishing {
|
|
3
|
+
#private;
|
|
3
4
|
publishIndependent: ({ taskId, draftPath, preparedAction, independentExecution, }: any) => Promise<any>;
|
|
4
5
|
prepareActionsForCall: (taskId: string, actions?: any[]) => Promise<void>;
|
|
5
|
-
|
|
6
|
+
constructor(dependencies: Pick<GrowthDependencies, "clock" | "evidenceExists" | "capability" | "store" | "load" | "guardInput" | "save" | "platform" | "stamp" | "settleProof">);
|
|
7
|
+
private handlePublishIndependent;
|
|
8
|
+
private handlePrepareActionsForCall;
|
|
9
|
+
}
|
|
10
|
+
export declare function createIndependentPublishing(...args: ConstructorParameters<typeof IndependentPublishing>): IndependentPublishing;
|
|
@@ -1,30 +1,55 @@
|
|
|
1
1
|
import { kinds, same } from "./growth-constants.js";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
export class IndependentPublishing {
|
|
3
|
+
#clock;
|
|
4
|
+
#evidenceExists;
|
|
5
|
+
#capability;
|
|
6
|
+
#store;
|
|
7
|
+
#load;
|
|
8
|
+
#guardInput;
|
|
9
|
+
#save;
|
|
10
|
+
#platform;
|
|
11
|
+
#stamp;
|
|
12
|
+
#settleProof;
|
|
13
|
+
publishIndependent;
|
|
14
|
+
prepareActionsForCall;
|
|
15
|
+
constructor(dependencies) {
|
|
16
|
+
const { clock, evidenceExists, capability, store, load, guardInput, save, platform, stamp, settleProof, } = dependencies;
|
|
17
|
+
this.#clock = clock;
|
|
18
|
+
this.#evidenceExists = evidenceExists;
|
|
19
|
+
this.#capability = capability;
|
|
20
|
+
this.#store = store;
|
|
21
|
+
this.#load = load;
|
|
22
|
+
this.#guardInput = guardInput;
|
|
23
|
+
this.#save = save;
|
|
24
|
+
this.#platform = platform;
|
|
25
|
+
this.#stamp = stamp;
|
|
26
|
+
this.#settleProof = settleProof;
|
|
27
|
+
this.publishIndependent = this.handlePublishIndependent.bind(this);
|
|
28
|
+
this.prepareActionsForCall = this.handlePrepareActionsForCall.bind(this);
|
|
29
|
+
}
|
|
30
|
+
async handlePublishIndependent({ taskId, draftPath, preparedAction, independentExecution, }) {
|
|
6
31
|
let action, token, submissionStarted = false;
|
|
7
32
|
const inWindow = () => {
|
|
8
33
|
const hour = Number(new Intl.DateTimeFormat("en-GB", {
|
|
9
34
|
timeZone: "Asia/Shanghai",
|
|
10
35
|
hour: "2-digit",
|
|
11
36
|
hourCycle: "h23",
|
|
12
|
-
}).format(clock()));
|
|
37
|
+
}).format(this.#clock()));
|
|
13
38
|
const permit = independentExecution?.windowAuthorization;
|
|
14
39
|
return ((hour >= 7 && hour < 22) ||
|
|
15
40
|
Boolean(permit?.userAuthorized === true &&
|
|
16
41
|
permit.actionId === (action?.id ?? preparedAction?.id) &&
|
|
17
|
-
evidenceExists(permit.evidenceRef) &&
|
|
18
|
-
clock() < Date.parse(permit.expiresAt)));
|
|
42
|
+
this.#evidenceExists(permit.evidenceRef) &&
|
|
43
|
+
this.#clock() < Date.parse(permit.expiresAt)));
|
|
19
44
|
};
|
|
20
45
|
try {
|
|
21
|
-
const caps = capability("submitPost", preparedAction ?? {});
|
|
46
|
+
const caps = this.#capability("submitPost", preparedAction ?? {});
|
|
22
47
|
if (!caps.covered)
|
|
23
48
|
return { status: "needsCapability", ...caps };
|
|
24
49
|
if (!inWindow())
|
|
25
50
|
throw new Error("outside-contract-write-window");
|
|
26
|
-
await store.withLock(async () => {
|
|
27
|
-
const snapshot = await load(taskId), state = structuredClone(snapshot.state);
|
|
51
|
+
await this.#store.withLock(async () => {
|
|
52
|
+
const snapshot = await this.#load(taskId), state = structuredClone(snapshot.state);
|
|
28
53
|
state.actions ??= [];
|
|
29
54
|
action = state.actions.find((a) => a.kind === "original" && a.draftPath === draftPath);
|
|
30
55
|
if (!action) {
|
|
@@ -34,47 +59,47 @@ export function createIndependentPublishing(dependencies) {
|
|
|
34
59
|
preparedAction.draftPath !== draftPath ||
|
|
35
60
|
state.actions.some((a) => a.id === preparedAction.id))
|
|
36
61
|
throw new Error("unique-prepared-original-required");
|
|
37
|
-
const error = guardInput(state, preparedAction, independentExecution);
|
|
62
|
+
const error = this.#guardInput(state, preparedAction, independentExecution);
|
|
38
63
|
if (error)
|
|
39
64
|
throw new Error(error);
|
|
40
65
|
action = structuredClone(preparedAction);
|
|
41
66
|
state.actions.push(action);
|
|
42
|
-
await save(snapshot, state);
|
|
67
|
+
await this.#save(snapshot, state);
|
|
43
68
|
}
|
|
44
69
|
if (action.status !== "prepared")
|
|
45
70
|
throw new Error("existing-submission-do-not-resubmit");
|
|
46
|
-
if (action.claim && store.ownerAlive(action.claim.pid))
|
|
71
|
+
if (action.claim && this.#store.ownerAlive(action.claim.pid))
|
|
47
72
|
throw new Error("same-action-claimed");
|
|
48
|
-
const error = guardInput(state, action, independentExecution);
|
|
73
|
+
const error = this.#guardInput(state, action, independentExecution);
|
|
49
74
|
if (error)
|
|
50
75
|
throw new Error(error);
|
|
51
76
|
});
|
|
52
|
-
const own = await platform.readAccount();
|
|
77
|
+
const own = await this.#platform.readAccount();
|
|
53
78
|
if (own.needsStateCheck ||
|
|
54
79
|
own.alerts?.length ||
|
|
55
80
|
!same(own.accountHandle, action.accountHandle))
|
|
56
81
|
throw new Error("fresh-account-unconfirmed");
|
|
57
|
-
const baseline = await platform.findSubmission({
|
|
82
|
+
const baseline = await this.#platform.findSubmission({
|
|
58
83
|
...action,
|
|
59
|
-
intentAt: stamp(),
|
|
84
|
+
intentAt: this.#stamp(),
|
|
60
85
|
});
|
|
61
86
|
if (baseline.needsStateCheck || baseline.alerts?.length)
|
|
62
87
|
throw new Error("publication-baseline-unconfirmed");
|
|
63
|
-
token = `${action.id}:${stamp()}:${Math.random()}`;
|
|
64
|
-
await store.withLock(async () => {
|
|
65
|
-
const snapshot = await load(taskId), state = structuredClone(snapshot.state);
|
|
88
|
+
token = `${action.id}:${this.#stamp()}:${Math.random()}`;
|
|
89
|
+
await this.#store.withLock(async () => {
|
|
90
|
+
const snapshot = await this.#load(taskId), state = structuredClone(snapshot.state);
|
|
66
91
|
const target = state.actions.find((a) => a.id === action.id), item = state.items.find((i) => i.kind === "original");
|
|
67
92
|
if (target?.status !== "prepared" ||
|
|
68
|
-
(target.claim && store.ownerAlive(target.claim.pid)))
|
|
93
|
+
(target.claim && this.#store.ownerAlive(target.claim.pid)))
|
|
69
94
|
throw new Error("action-already-claimed-do-not-resubmit");
|
|
70
|
-
const error = guardInput(state, target, independentExecution);
|
|
95
|
+
const error = this.#guardInput(state, target, independentExecution);
|
|
71
96
|
if (error)
|
|
72
97
|
throw new Error(error);
|
|
73
98
|
if (!inWindow() || item.verified + item.unknown >= item.planned)
|
|
74
99
|
throw new Error("window-or-original-capacity-unavailable");
|
|
75
100
|
Object.assign(target, {
|
|
76
101
|
status: "intent",
|
|
77
|
-
intentAt: stamp(),
|
|
102
|
+
intentAt: this.#stamp(),
|
|
78
103
|
beforeIds: (baseline.posts ?? [])
|
|
79
104
|
.map((p) => p.id)
|
|
80
105
|
.filter(Boolean),
|
|
@@ -83,9 +108,9 @@ export function createIndependentPublishing(dependencies) {
|
|
|
83
108
|
claim: {
|
|
84
109
|
actionId: target.id,
|
|
85
110
|
token,
|
|
86
|
-
pid: store.processId,
|
|
111
|
+
pid: this.#store.processId,
|
|
87
112
|
tabId: independentExecution.tabId,
|
|
88
|
-
acquiredAt: stamp(),
|
|
113
|
+
acquiredAt: this.#stamp(),
|
|
89
114
|
},
|
|
90
115
|
});
|
|
91
116
|
item.unknown += 1;
|
|
@@ -95,26 +120,26 @@ export function createIndependentPublishing(dependencies) {
|
|
|
95
120
|
kind: "original",
|
|
96
121
|
at: target.intentAt,
|
|
97
122
|
});
|
|
98
|
-
await save(snapshot, state);
|
|
123
|
+
await this.#save(snapshot, state);
|
|
99
124
|
action = structuredClone(target);
|
|
100
125
|
});
|
|
101
126
|
let observation;
|
|
102
127
|
try {
|
|
103
|
-
await platform.submitPost({
|
|
128
|
+
await this.#platform.submitPost({
|
|
104
129
|
...action,
|
|
105
130
|
beforeSubmit: async () => {
|
|
106
|
-
const latest = await load(taskId), target = latest.state.actions.find((a) => a.id === action.id);
|
|
131
|
+
const latest = await this.#load(taskId), target = latest.state.actions.find((a) => a.id === action.id);
|
|
107
132
|
if (target?.status !== "intent" ||
|
|
108
133
|
target.claimToken !== token ||
|
|
109
134
|
!inWindow())
|
|
110
135
|
throw new Error("action-claim-or-window-changed");
|
|
111
|
-
const error = guardInput(latest.state, target, independentExecution);
|
|
136
|
+
const error = this.#guardInput(latest.state, target, independentExecution);
|
|
112
137
|
if (error)
|
|
113
138
|
throw new Error(error);
|
|
114
139
|
submissionStarted = true;
|
|
115
140
|
},
|
|
116
141
|
});
|
|
117
|
-
observation = await platform.findSubmission(action);
|
|
142
|
+
observation = await this.#platform.findSubmission(action);
|
|
118
143
|
}
|
|
119
144
|
catch (error) {
|
|
120
145
|
if (!submissionStarted)
|
|
@@ -125,11 +150,11 @@ export function createIndependentPublishing(dependencies) {
|
|
|
125
150
|
error: error.message.slice(0, 240),
|
|
126
151
|
};
|
|
127
152
|
}
|
|
128
|
-
return await store.withLock(async () => {
|
|
129
|
-
const snapshot = await load(taskId), target = snapshot.state.actions.find((a) => a.id === action.id);
|
|
153
|
+
return await this.#store.withLock(async () => {
|
|
154
|
+
const snapshot = await this.#load(taskId), target = snapshot.state.actions.find((a) => a.id === action.id);
|
|
130
155
|
if (target?.claimToken !== token || target.status !== "intent")
|
|
131
156
|
throw new Error("action-settlement-conflict");
|
|
132
|
-
await settleProof(snapshot, target, observation, {
|
|
157
|
+
await this.#settleProof(snapshot, target, observation, {
|
|
133
158
|
preserveStatus: true,
|
|
134
159
|
});
|
|
135
160
|
return {
|
|
@@ -137,25 +162,25 @@ export function createIndependentPublishing(dependencies) {
|
|
|
137
162
|
taskId,
|
|
138
163
|
actionId: action.id,
|
|
139
164
|
publicId: observation.publicId,
|
|
140
|
-
evidenceRef: store.path,
|
|
165
|
+
evidenceRef: this.#store.path,
|
|
141
166
|
mayEnd: false,
|
|
142
167
|
};
|
|
143
168
|
});
|
|
144
169
|
}
|
|
145
170
|
catch (error) {
|
|
146
171
|
if (token && !submissionStarted)
|
|
147
|
-
await store.withLock(async () => {
|
|
148
|
-
const snapshot = await load(taskId), state = structuredClone(snapshot.state), target = state.actions.find((a) => a.id === action.id);
|
|
172
|
+
await this.#store.withLock(async () => {
|
|
173
|
+
const snapshot = await this.#load(taskId), state = structuredClone(snapshot.state), target = state.actions.find((a) => a.id === action.id);
|
|
149
174
|
if (target?.claimToken !== token || target.status !== "intent")
|
|
150
175
|
return;
|
|
151
176
|
target.status = "prepared";
|
|
152
|
-
target.preparationEvidence = await store.recordProof({
|
|
153
|
-
observedAt: stamp(),
|
|
177
|
+
target.preparationEvidence = await this.#store.recordProof({
|
|
178
|
+
observedAt: this.#stamp(),
|
|
154
179
|
submissionStarted: false,
|
|
155
180
|
error: error.message.slice(0, 240),
|
|
156
181
|
});
|
|
157
182
|
state.items.find((i) => i.kind === "original").unknown -= 1;
|
|
158
|
-
await save(snapshot, state);
|
|
183
|
+
await this.#save(snapshot, state);
|
|
159
184
|
});
|
|
160
185
|
return {
|
|
161
186
|
status: "needsDecision",
|
|
@@ -172,7 +197,7 @@ export function createIndependentPublishing(dependencies) {
|
|
|
172
197
|
}
|
|
173
198
|
finally {
|
|
174
199
|
if (token)
|
|
175
|
-
await store.transaction((snapshot) => {
|
|
200
|
+
await this.#store.transaction((snapshot) => {
|
|
176
201
|
if (snapshot.state.batchId !== taskId)
|
|
177
202
|
return null;
|
|
178
203
|
const next = structuredClone(snapshot.state), target = next.actions.find((a) => a.id === action.id);
|
|
@@ -183,11 +208,10 @@ export function createIndependentPublishing(dependencies) {
|
|
|
183
208
|
});
|
|
184
209
|
}
|
|
185
210
|
}
|
|
186
|
-
|
|
187
|
-
async function prepareActionsForCall(taskId, actions = []) {
|
|
211
|
+
async handlePrepareActionsForCall(taskId, actions = []) {
|
|
188
212
|
if (!actions.length)
|
|
189
213
|
return;
|
|
190
|
-
await store.transaction(async (snapshot) => {
|
|
214
|
+
await this.#store.transaction(async (snapshot) => {
|
|
191
215
|
if (snapshot.state.batchId !== taskId)
|
|
192
216
|
throw new Error("original-task-id-required");
|
|
193
217
|
const next = structuredClone(snapshot.state);
|
|
@@ -200,7 +224,7 @@ export function createIndependentPublishing(dependencies) {
|
|
|
200
224
|
!kinds.includes(action.kind))
|
|
201
225
|
throw new Error("unique-prepared-action-required");
|
|
202
226
|
ids.add(action.id);
|
|
203
|
-
const inputError = guardInput(next, action);
|
|
227
|
+
const inputError = this.#guardInput(next, action);
|
|
204
228
|
if (inputError)
|
|
205
229
|
throw new Error(inputError);
|
|
206
230
|
const existing = next.actions.find((a) => a.id === action.id);
|
|
@@ -232,9 +256,11 @@ export function createIndependentPublishing(dependencies) {
|
|
|
232
256
|
throw new Error("prepared-actions-exceed-original-target");
|
|
233
257
|
next.actions.push(structuredClone(action));
|
|
234
258
|
}
|
|
235
|
-
next.updatedAt = stamp();
|
|
259
|
+
next.updatedAt = this.#stamp();
|
|
236
260
|
return next;
|
|
237
261
|
});
|
|
238
262
|
}
|
|
239
|
-
|
|
263
|
+
}
|
|
264
|
+
export function createIndependentPublishing(...args) {
|
|
265
|
+
return new IndependentPublishing(...args);
|
|
240
266
|
}
|
package/dist/growth-reading.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { GrowthDependencies } from "./growth-dependencies.js";
|
|
2
|
-
export declare
|
|
2
|
+
export declare class GrowthReading {
|
|
3
|
+
#private;
|
|
3
4
|
readTask: (operation: any, { mode, ...input }?: any) => Promise<any>;
|
|
4
|
-
|
|
5
|
+
constructor(dependencies: Pick<GrowthDependencies, "platform" | "executor" | "store" | "runtimeId" | "stamp" | "releaseSession" | "session">);
|
|
6
|
+
private handleReadTask;
|
|
7
|
+
}
|
|
8
|
+
export declare function createGrowthReading(...args: ConstructorParameters<typeof GrowthReading>): GrowthReading;
|
package/dist/growth-reading.js
CHANGED
|
@@ -1,57 +1,77 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export class GrowthReading {
|
|
2
|
+
#platform;
|
|
3
|
+
#executor;
|
|
4
|
+
#store;
|
|
5
|
+
#runtimeId;
|
|
6
|
+
#stamp;
|
|
7
|
+
#releaseSession;
|
|
8
|
+
#session;
|
|
9
|
+
#readCache;
|
|
10
|
+
readTask;
|
|
11
|
+
constructor(dependencies) {
|
|
12
|
+
const { platform, executor, store, runtimeId, stamp, releaseSession, session, } = dependencies;
|
|
13
|
+
this.#platform = platform;
|
|
14
|
+
this.#executor = executor;
|
|
15
|
+
this.#store = store;
|
|
16
|
+
this.#runtimeId = runtimeId;
|
|
17
|
+
this.#stamp = stamp;
|
|
18
|
+
this.#releaseSession = releaseSession;
|
|
19
|
+
this.#session = session;
|
|
20
|
+
this.#readCache = new Map();
|
|
21
|
+
this.readTask = this.handleReadTask.bind(this);
|
|
22
|
+
}
|
|
23
|
+
async handleReadTask(operation, { mode = "receipt", ...input } = {}) {
|
|
24
|
+
const executor = this.#executor;
|
|
5
25
|
if (!["decision", "receipt", "delta"].includes(mode))
|
|
6
26
|
throw new Error("读取模式须为decision/receipt/delta");
|
|
7
|
-
if (session.running)
|
|
27
|
+
if (this.#session.running)
|
|
8
28
|
return {
|
|
9
29
|
status: "needsDecision",
|
|
10
30
|
complete: false,
|
|
11
31
|
reason: "same-tab-operation-running",
|
|
12
32
|
};
|
|
13
|
-
const caps = platform.preflight({
|
|
33
|
+
const caps = this.#platform.preflight({
|
|
14
34
|
operation: ["readAccount", "readPerson", "readPost"].includes(operation)
|
|
15
35
|
? "read"
|
|
16
36
|
: operation,
|
|
17
37
|
input,
|
|
18
38
|
});
|
|
19
|
-
if (!caps.covered || typeof platform[operation] !== "function")
|
|
39
|
+
if (!caps.covered || typeof this.#platform[operation] !== "function")
|
|
20
40
|
return {
|
|
21
41
|
status: "needsCapability",
|
|
22
42
|
...caps,
|
|
23
43
|
missing: [
|
|
24
44
|
...(caps.missing ?? []),
|
|
25
|
-
...(typeof platform[operation] === "function"
|
|
45
|
+
...(typeof this.#platform[operation] === "function"
|
|
26
46
|
? []
|
|
27
47
|
: [`platform.${operation}`]),
|
|
28
48
|
],
|
|
29
49
|
};
|
|
30
|
-
session.running = true;
|
|
50
|
+
this.#session.running = true;
|
|
31
51
|
try {
|
|
32
52
|
if (executor)
|
|
33
|
-
await store.transaction((latest) => {
|
|
53
|
+
await this.#store.transaction((latest) => {
|
|
34
54
|
const next = structuredClone(latest.state);
|
|
35
55
|
next.executions ??= {};
|
|
36
56
|
if (Object.values(next.executions).some((e) => (e.tabId === executor.tabId || e.id === executor.id) &&
|
|
37
|
-
e.runtimeId !== runtimeId &&
|
|
57
|
+
e.runtimeId !== this.#runtimeId &&
|
|
38
58
|
(e.activity || e.actionId) &&
|
|
39
|
-
store.ownerAlive(e.pid)))
|
|
59
|
+
this.#store.ownerAlive(e.pid)))
|
|
40
60
|
throw new Error("same-tab-or-executor-claimed");
|
|
41
61
|
next.executions[executor.id] = {
|
|
42
62
|
...next.executions[executor.id],
|
|
43
63
|
...executor,
|
|
44
|
-
runtimeId,
|
|
45
|
-
pid: store.processId,
|
|
64
|
+
runtimeId: this.#runtimeId,
|
|
65
|
+
pid: this.#store.processId,
|
|
46
66
|
activity: operation,
|
|
47
67
|
status: next.executions[executor.id]?.status ?? "RUNNING",
|
|
48
|
-
updatedAt: stamp(),
|
|
68
|
+
updatedAt: this.#stamp(),
|
|
49
69
|
};
|
|
50
|
-
next.updatedAt = stamp();
|
|
70
|
+
next.updatedAt = this.#stamp();
|
|
51
71
|
return next;
|
|
52
72
|
});
|
|
53
|
-
const value = await platform[operation](input);
|
|
54
|
-
const evidenceRef = await store.recordProof(await platform.captureProof({ observation: value }));
|
|
73
|
+
const value = await this.#platform[operation](input);
|
|
74
|
+
const evidenceRef = await this.#store.recordProof(await this.#platform.captureProof({ observation: value }));
|
|
55
75
|
const status = value.status ??
|
|
56
76
|
(value.complete === false ||
|
|
57
77
|
value.needsStateCheck ||
|
|
@@ -86,11 +106,11 @@ export function createGrowthReading(dependencies) {
|
|
|
86
106
|
for (const entry of value[collection]) {
|
|
87
107
|
const key = JSON.stringify([operation, input, entry.id]);
|
|
88
108
|
const normalized = JSON.stringify(entry, (name, val) => name === "observedAt" ? undefined : val);
|
|
89
|
-
if (readCache.get(key) === normalized)
|
|
109
|
+
if (this.#readCache.get(key) === normalized)
|
|
90
110
|
unchangedIds.push(entry.id);
|
|
91
111
|
else
|
|
92
112
|
changed.push(entry);
|
|
93
|
-
readCache.set(key, normalized);
|
|
113
|
+
this.#readCache.set(key, normalized);
|
|
94
114
|
}
|
|
95
115
|
return { ...receipt, [collection]: changed, unchangedIds };
|
|
96
116
|
}
|
|
@@ -108,8 +128,8 @@ export function createGrowthReading(dependencies) {
|
|
|
108
128
|
? "connection-failed"
|
|
109
129
|
: "observation-unconfirmed";
|
|
110
130
|
const diagnostic = { operation, category, code };
|
|
111
|
-
const evidenceRef = await store.recordProof({
|
|
112
|
-
capturedAt: stamp(),
|
|
131
|
+
const evidenceRef = await this.#store.recordProof({
|
|
132
|
+
capturedAt: this.#stamp(),
|
|
113
133
|
diagnostic: { ...diagnostic, error: error.message },
|
|
114
134
|
});
|
|
115
135
|
return {
|
|
@@ -124,12 +144,14 @@ export function createGrowthReading(dependencies) {
|
|
|
124
144
|
}
|
|
125
145
|
finally {
|
|
126
146
|
try {
|
|
127
|
-
await releaseSession();
|
|
147
|
+
await this.#releaseSession();
|
|
128
148
|
}
|
|
129
149
|
finally {
|
|
130
|
-
session.running = false;
|
|
150
|
+
this.#session.running = false;
|
|
131
151
|
}
|
|
132
152
|
}
|
|
133
153
|
}
|
|
134
|
-
|
|
154
|
+
}
|
|
155
|
+
export function createGrowthReading(...args) {
|
|
156
|
+
return new GrowthReading(...args);
|
|
135
157
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { GrowthDependencies } from "./growth-dependencies.js";
|
|
2
|
-
export declare
|
|
2
|
+
export declare class GrowthRecovery {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(dependencies: Pick<GrowthDependencies, "capability" | "platform" | "load" | "evidenceExists" | "clock" | "store" | "settleProof" | "executor" | "parallelRound" | "round" | "readTask">);
|
|
3
5
|
verifyPublishedPost({ taskId, draftPath, publicURL, evidenceRef, sourceLink, }: any): Promise<any>;
|
|
4
|
-
verifySubmission
|
|
5
|
-
verifyFollow
|
|
6
|
-
}
|
|
6
|
+
verifySubmission({ taskId, actionId }: any): Promise<any>;
|
|
7
|
+
verifyFollow(target: any): Promise<any>;
|
|
8
|
+
}
|
|
9
|
+
export declare function createGrowthRecovery(...args: ConstructorParameters<typeof GrowthRecovery>): GrowthRecovery;
|