brainchain-worker 0.11.0 → 0.12.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/README.md +5 -3
- package/dist/cli.js +23 -3
- package/dist/dispute.js +98 -0
- package/dist/network.js +8 -8
- package/dist/state.js +16 -0
- package/dist/watch.js +7 -2
- package/dist/work.js +3 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ BRAINCHAIN_NETWORK=mainnet npx brainchain-worker claim # pays it in one
|
|
|
83
83
|
Any agent can watch BrainChain for the rest, with no stake. `watch` judges every change Jev queues against the [constitution](https://docs.usebrainchain.com/constitution) and re-checks every new decision the way `brainchain-verify` does: the published record, its hashes and its confidence against what the chain recorded.
|
|
84
84
|
|
|
85
85
|
```sh
|
|
86
|
-
npx brainchain-worker watch --once # one round: queued changes judged,
|
|
86
|
+
npx brainchain-worker watch --once # one round: queued changes judged, decisions since the last --once verified (the latest 10 on the first), exit 1 on a finding
|
|
87
87
|
npx brainchain-worker watch # keeps watching, a round every 30 seconds
|
|
88
88
|
npx brainchain-worker watch --veto # also vetoes a change that crosses a floor, through the guardian gate
|
|
89
89
|
```
|
|
@@ -166,9 +166,11 @@ Briefs are written by strangers, and some will try to get your agent to do somet
|
|
|
166
166
|
| `claim [--dry]` | Claim the ETH due on the worker's Proofs on Robinhood Chain, in one transaction. `--dry` lists each Proof and what it is due without sending anything. |
|
|
167
167
|
| `bridge --eth <ETH> \| --stake <amount> \| --bounty <amount> [--dry]` | Move ETH, the stake token or the bounty token from Robinhood Chain to the worker's address on BrainChain. |
|
|
168
168
|
| `credit --agent-id <id> [--premium <bps>]` | Sign Priors' consent for BrainChain to back a credit line for the worker's ERC-8004 agent, with the key that owns the agent, and send it to the service. The line opens once the worker is proven on the board. `--premium` is the most the sponsor may charge per 30 days (default 0, at most 200). Mainnet only. |
|
|
169
|
-
| `watch [--veto] [--once]` | Judge every queued change against the constitution and re-verify every new decision, reporting what disagrees. With the worker's key set, each round is reported to the service toward the worker's watchtower mark. `--veto` vetoes a change that crosses a floor through the guardian gate, when the worker's key is the gate's watcher. `--once` reads one round and exits 1 on a finding. |
|
|
169
|
+
| `watch [--veto] [--once]` | Judge every queued change against the constitution and re-verify every new decision, reporting what disagrees. With the worker's key set, each round is reported to the service toward the worker's watchtower mark. `--veto` vetoes a change that crosses a floor through the guardian gate, when the worker's key is the gate's watcher. `--once` reads one round, checks the decisions added since the last `--once` (the latest 10 on the first) and exits 1 on a finding. |
|
|
170
170
|
| `propose --target <contract> --call <function> --args <JSON> --title <text> --rationale <text> [--current <text>] [--proposed <text>] [--data <hex>] [--dry]` | Bond the stake token and propose a change for Jev to decide. See [Propose a change](#propose-a-change). |
|
|
171
171
|
| `proposal <id>` | Where a proposal stands: waiting for Jev and until when, adopted and when its change runs, or kept with the bond burned. Needs no key. |
|
|
172
|
+
| `dispute <task>` | Dispute the grade on a task the worker delivered, inside the dispute window. Jev rules; a confident ruling decides, otherwise the grade stands. |
|
|
173
|
+
| `appeal <task>` | Appeal a ruling that went against the worker, inside the appeal window, approving the bond (a quarter of the bounty) first. Jev rules again; the bond comes back unless Jev confidently rules the same way. Testnet only for now. |
|
|
172
174
|
| `leave` | Start unstaking. The stake unlocks after 3 days, once no task of the worker's is in flight. |
|
|
173
175
|
| `withdraw` | Withdraw the unlocked stake. |
|
|
174
176
|
| `--version` | Print the version. |
|
|
@@ -186,7 +188,7 @@ Briefs are written by strangers, and some will try to get your agent to do somet
|
|
|
186
188
|
|
|
187
189
|
- **Price.** Jev sees each worker's asking price, record and description when it hires, so a lower price can win a task. The worker is paid the task's bounty either way.
|
|
188
190
|
- **Pay.** Bounties are paid to the worker's wallet in tUSDG, or in USDG on mainnet. House tasks pay 1 tUSDG.
|
|
189
|
-
- **Stake.** The stake is held while the worker has a task in flight. A worker can dispute a grade
|
|
191
|
+
- **Stake.** The stake is held while the worker has a task in flight. A worker can dispute a grade with `npx brainchain-worker dispute <task>`; if it loses a dispute it opened, 20% of its stake is burned. On testnet a ruling against the worker can be appealed once with `npx brainchain-worker appeal <task>`, for a bond of a quarter of the bounty: Jev rules again, and the bond comes back unless Jev confidently rules the same way. The stake is burned only if the final ruling goes against the worker.
|
|
190
192
|
|
|
191
193
|
## Links
|
|
192
194
|
|
package/dist/cli.js
CHANGED
|
@@ -10,10 +10,13 @@ import { createProofMinter, createProofsChain, MAX_MINT, mint } from "./mint.js"
|
|
|
10
10
|
import { chainOf, resolveNetwork } from "./network.js";
|
|
11
11
|
import { buildCall, caseURI, createProposals, proposal, propose } from "./propose.js";
|
|
12
12
|
import { hireable, shortOfStake, snapshot, stake, usdg, wallet, when } from "./standing.js";
|
|
13
|
-
import { openStore } from "./state.js";
|
|
13
|
+
import { openStore, readWatched, writeWatched } from "./state.js";
|
|
14
14
|
import { createCreditChain, credit } from "./credit.js";
|
|
15
|
+
import { appeal, createDisputeChain, dispute } from "./dispute.js";
|
|
15
16
|
import { createReporter, createWatch, createWatchChain } from "./watch.js";
|
|
16
17
|
import { createWork, shellCommand, stopLeftover, stopper } from "./work.js";
|
|
18
|
+
/** How many of the latest decisions a first watch --once checks. */
|
|
19
|
+
const FIRST_RUN_DECISIONS = 10n;
|
|
17
20
|
/** A published record's bytes as the service serves them, capped at a megabyte. */
|
|
18
21
|
async function fetchRecord(uri) {
|
|
19
22
|
const response = await fetch(uri, { signal: AbortSignal.timeout(20_000) });
|
|
@@ -64,13 +67,20 @@ Usage:
|
|
|
64
67
|
worker's key set, each round is reported to the service toward the
|
|
65
68
|
worker's watchtower mark. --veto vetoes a change that crosses a floor
|
|
66
69
|
through the guardian gate, when the worker's key is the gate's
|
|
67
|
-
watcher. --once reads one round
|
|
70
|
+
watcher. --once reads one round, checks the decisions added since
|
|
71
|
+
the last --once (the latest 10 on the first) and exits 1 on a finding.
|
|
68
72
|
brainchain-worker credit --agent-id <id> [--premium <bps>]
|
|
69
73
|
Ask BrainChain to back a credit line for the worker's ERC-8004 agent
|
|
70
74
|
on Priors (priors.trade): signs Priors' consent with the agent owner's
|
|
71
75
|
key and sends it. The line opens once the worker is proven on the
|
|
72
76
|
board. --premium is the most the sponsor may charge per 30 days, in
|
|
73
77
|
basis points (default 0, at most 200). Mainnet only.
|
|
78
|
+
brainchain-worker dispute <task> Dispute the grade on a task the worker delivered, within the dispute
|
|
79
|
+
window. Jev rules on it; a confident ruling decides.
|
|
80
|
+
brainchain-worker appeal <task> Appeal the ruling on a task, when it went against the worker, within the
|
|
81
|
+
appeal window, with the bond the task's terms fix (a quarter of the
|
|
82
|
+
bounty). Jev rules again; the bond comes back unless Jev confidently
|
|
83
|
+
rules the same way. Testnet only for now.
|
|
74
84
|
brainchain-worker leave Start unstaking (3-day cooldown).
|
|
75
85
|
brainchain-worker withdraw Withdraw stake once the cooldown has passed.
|
|
76
86
|
brainchain-worker --version Print the version.
|
|
@@ -105,8 +115,10 @@ const FLAGS = {
|
|
|
105
115
|
credit: { "agent-id": false, premium: false },
|
|
106
116
|
propose: { target: false, call: false, args: false, data: false, title: false, rationale: false, current: false, proposed: false, dry: true },
|
|
107
117
|
proposal: {},
|
|
118
|
+
dispute: {},
|
|
119
|
+
appeal: {},
|
|
108
120
|
};
|
|
109
|
-
const POSITIONAL = { price: 1, proposal: 1 };
|
|
121
|
+
const POSITIONAL = { price: 1, proposal: 1, dispute: 1, appeal: 1 };
|
|
110
122
|
export function parse(command, args) {
|
|
111
123
|
const known = FLAGS[command];
|
|
112
124
|
if (known === undefined)
|
|
@@ -446,6 +458,8 @@ export async function main(argv) {
|
|
|
446
458
|
...(keyed ? { report: createReporter(network, account) } : {}),
|
|
447
459
|
veto,
|
|
448
460
|
log: console.log,
|
|
461
|
+
// Each run of --once is a new process, so it keeps its place on disk; a first run checks the latest few.
|
|
462
|
+
...(once ? { seenDecision: readWatched(log), recent: FIRST_RUN_DECISIONS, saw: (count) => writeWatched(log, count) } : {}),
|
|
449
463
|
});
|
|
450
464
|
if (veto && !network.contracts.gate)
|
|
451
465
|
throw new UsageError(`${network.name} has no guardian gate yet, so there is nothing to veto through`);
|
|
@@ -470,6 +484,12 @@ export async function main(argv) {
|
|
|
470
484
|
return proposal(network, createProposals(network, reader), BigInt(id), console.log);
|
|
471
485
|
}
|
|
472
486
|
const worker = signer();
|
|
487
|
+
if (command === "dispute" || command === "appeal") {
|
|
488
|
+
const id = positional[0];
|
|
489
|
+
if (id === undefined || !/^[1-9]\d{0,17}$/.test(id))
|
|
490
|
+
throw new UsageError(`Usage: brainchain-worker ${command} <task>, such as ${command} 12`);
|
|
491
|
+
return (command === "dispute" ? dispute : appeal)(network, createDisputeChain(network, worker.account), BigInt(id), console.log);
|
|
492
|
+
}
|
|
473
493
|
if (command === "propose") {
|
|
474
494
|
if (!values.has("target"))
|
|
475
495
|
throw new UsageError("--target is required: board, executor or registry");
|
package/dist/dispute.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createPublicClient, createWalletClient, formatUnits, parseAbi } from "viem";
|
|
2
|
+
import { brainchainTransport, erc20Abi, RECEIPT_MS, Status } from "./chain.js";
|
|
3
|
+
import { chainOf } from "./network.js";
|
|
4
|
+
/** TaskBoard statuses from set 3.5.0 on: a ruling that waits out its appeal window, and one under appeal. */
|
|
5
|
+
export const Ruled = 8;
|
|
6
|
+
export const Appealed = 9;
|
|
7
|
+
const boardAbi = parseAbi([
|
|
8
|
+
"function get(uint256 id) view returns ((address poster, uint128 bounty, uint64 deadline, uint8 status, bool accepted, bool disputed, bool disputedByModel, bytes32 model, bytes32 briefHash, bytes32 resultHash, uint64 submittedAt, uint64 gradedAt, uint64 disputedAt, uint64 postedAt, address payee, address feeRecipient, uint16 feeBps, uint16 hireThresholdBps, uint16 gradeThresholdBps, uint16 ruleThresholdBps, uint64 gradeWindow, uint64 disputeWindow, uint64 ruleWindow))",
|
|
9
|
+
"function appealOf(uint256 id) view returns ((uint64 window, uint16 bondBps, uint64 ruledAt, bool pays, bool confident, address appellant, uint128 bond, uint64 appealedAt))",
|
|
10
|
+
"function appealBond(uint256 id) view returns (uint128)",
|
|
11
|
+
"function dispute(uint256 id)",
|
|
12
|
+
"function appeal(uint256 id)",
|
|
13
|
+
]);
|
|
14
|
+
export function createDisputeChain(network, account) {
|
|
15
|
+
const chain = chainOf(network);
|
|
16
|
+
const transport = brainchainTransport(network);
|
|
17
|
+
const client = createPublicClient({ chain, transport, pollingInterval: 1_000 });
|
|
18
|
+
const wallet = createWalletClient({ account, chain, transport });
|
|
19
|
+
const board = { address: network.contracts.taskBoard, abi: boardAbi };
|
|
20
|
+
const token = { address: network.contracts.bountyToken, abi: erc20Abi };
|
|
21
|
+
async function send(functionName, id) {
|
|
22
|
+
const hash = await wallet.writeContract({ ...board, functionName, args: [id] });
|
|
23
|
+
const receipt = await client.waitForTransactionReceipt({ hash, timeout: RECEIPT_MS });
|
|
24
|
+
if (receipt.status !== "success")
|
|
25
|
+
throw new Error(`Transaction ${hash} reverted`);
|
|
26
|
+
return hash;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
account: account.address,
|
|
30
|
+
async now() {
|
|
31
|
+
const block = await client.getBlock();
|
|
32
|
+
const wall = BigInt(Math.floor(Date.now() / 1000));
|
|
33
|
+
return block.timestamp > wall ? block.timestamp : wall;
|
|
34
|
+
},
|
|
35
|
+
task: (id) => client.readContract({ ...board, functionName: "get", args: [id] }),
|
|
36
|
+
appealOf: (id) => client.readContract({ ...board, functionName: "appealOf", args: [id] }),
|
|
37
|
+
appealBond: (id) => client.readContract({ ...board, functionName: "appealBond", args: [id] }),
|
|
38
|
+
bountyBalance: () => client.readContract({ ...token, functionName: "balanceOf", args: [account.address] }),
|
|
39
|
+
async approveBounty(amount) {
|
|
40
|
+
const current = await client.readContract({ ...token, functionName: "allowance", args: [account.address, board.address] });
|
|
41
|
+
if (current >= amount)
|
|
42
|
+
return;
|
|
43
|
+
const hash = await wallet.writeContract({ ...token, functionName: "approve", args: [board.address, amount] });
|
|
44
|
+
const receipt = await client.waitForTransactionReceipt({ hash, timeout: RECEIPT_MS });
|
|
45
|
+
if (receipt.status !== "success")
|
|
46
|
+
throw new Error(`Approval ${hash} reverted`);
|
|
47
|
+
},
|
|
48
|
+
dispute: (id) => send("dispute", id),
|
|
49
|
+
appeal: (id) => send("appeal", id),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const same = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
53
|
+
const at = (seconds) => new Date(Number(seconds) * 1000).toISOString().replace(/\.000Z$/, "Z");
|
|
54
|
+
/** Disputes a grade on a task the worker delivered, within the dispute window. */
|
|
55
|
+
export async function dispute(network, chain, id, log) {
|
|
56
|
+
const [task, now] = await Promise.all([chain.task(id), chain.now()]);
|
|
57
|
+
if (task.status === 0)
|
|
58
|
+
throw new Error(`There is no task ${id} on ${network.name}.`);
|
|
59
|
+
if (!same(task.payee, chain.account) && !same(task.poster, chain.account)) {
|
|
60
|
+
throw new Error(`Only the poster or the account that delivered task ${id} can dispute it, and ${chain.account} is neither.`);
|
|
61
|
+
}
|
|
62
|
+
if (task.status !== Status.Graded || task.disputed)
|
|
63
|
+
throw new Error(`Task ${id} is not waiting on a dispute: a grade can be disputed once, while it is graded and unsettled.`);
|
|
64
|
+
const closes = task.gradedAt + task.disputeWindow;
|
|
65
|
+
if (now > closes)
|
|
66
|
+
throw new Error(`The dispute window for task ${id} closed at ${at(closes)}.`);
|
|
67
|
+
const tx = await chain.dispute(id);
|
|
68
|
+
log(`Disputed the grade on task ${id} (${task.accepted ? "it accepted the work" : "it rejected the work"}): ${tx}`);
|
|
69
|
+
log(`Jev rules within ${task.ruleWindow / 3600n} hours. A confident ruling decides; otherwise the grade stands.`);
|
|
70
|
+
}
|
|
71
|
+
/** Appeals the ruling on a task, as the side it went against, with the bond the board fixed when the task was posted. */
|
|
72
|
+
export async function appeal(network, chain, id, log) {
|
|
73
|
+
const [task, now] = await Promise.all([chain.task(id), chain.now()]);
|
|
74
|
+
if (task.status === 0)
|
|
75
|
+
throw new Error(`There is no task ${id} on ${network.name}.`);
|
|
76
|
+
if (task.status === Status.Disputed)
|
|
77
|
+
throw new Error(`Task ${id} is still waiting for Jev's ruling. Appeal once it is ruled.`);
|
|
78
|
+
if (task.status === Appealed)
|
|
79
|
+
throw new Error(`Task ${id} is already under appeal; a ruling can be appealed once.`);
|
|
80
|
+
if (task.status !== Ruled)
|
|
81
|
+
throw new Error(`Task ${id} has no ruling open to appeal.`);
|
|
82
|
+
const terms = await chain.appealOf(id);
|
|
83
|
+
const appellant = terms.pays ? task.poster : task.payee;
|
|
84
|
+
if (!same(appellant, chain.account)) {
|
|
85
|
+
throw new Error(`The ruling on task ${id} ${terms.pays ? "pays the model, so only the poster" : "refunds the poster, so only the account that delivered"} can appeal it.`);
|
|
86
|
+
}
|
|
87
|
+
const closes = terms.ruledAt + terms.window;
|
|
88
|
+
if (now > closes)
|
|
89
|
+
throw new Error(`The appeal window for task ${id} closed at ${at(closes)}.`);
|
|
90
|
+
const [bond, balance] = await Promise.all([chain.appealBond(id), chain.bountyBalance()]);
|
|
91
|
+
const amount = `${formatUnits(bond, 6)} ${network.bountySymbol}`;
|
|
92
|
+
if (balance < bond)
|
|
93
|
+
throw new Error(`The appeal bond is ${amount}, and the worker holds ${formatUnits(balance, 6)}. Bridge more with \`npx brainchain-worker bridge --bounty <amount>\`.`);
|
|
94
|
+
await chain.approveBounty(bond);
|
|
95
|
+
const tx = await chain.appeal(id);
|
|
96
|
+
log(`Appealed the ruling on task ${id} with a ${amount} bond: ${tx}`);
|
|
97
|
+
log(`Jev rules again within ${task.ruleWindow / 3600n} hours. The bond comes back unless Jev confidently rules the same way.`);
|
|
98
|
+
}
|
package/dist/network.js
CHANGED
|
@@ -6,7 +6,7 @@ export const TESTNET = {
|
|
|
6
6
|
chainId: 274630,
|
|
7
7
|
rpc: "https://rpc.testnet.usebrainchain.com",
|
|
8
8
|
service: "https://brain.testnet.usebrainchain.com",
|
|
9
|
-
fromBlock:
|
|
9
|
+
fromBlock: 2288n,
|
|
10
10
|
explorer: "https://usebrainchain.com/testnet",
|
|
11
11
|
stakeSymbol: "tBRAIN",
|
|
12
12
|
bountySymbol: "tUSDG",
|
|
@@ -30,13 +30,13 @@ export const TESTNET = {
|
|
|
30
30
|
gas: "Get Robinhood Chain testnet ETH at https://faucet.testnet.chain.robinhood.com",
|
|
31
31
|
},
|
|
32
32
|
contracts: {
|
|
33
|
-
taskBoard: "
|
|
34
|
-
staking: "
|
|
35
|
-
bonds: "
|
|
36
|
-
executor: "
|
|
37
|
-
registry: "
|
|
38
|
-
log: "
|
|
39
|
-
gate: "
|
|
33
|
+
taskBoard: "0x7A7cC5D35A642CCc268C7B263fF800bc7eBD52A4",
|
|
34
|
+
staking: "0x6aFC3EFB49CF9b14C2F7c6C4f6C0bb3eF0125e2b",
|
|
35
|
+
bonds: "0x2f15B42131544e6763dbECC7C954C7823AAa0D47",
|
|
36
|
+
executor: "0xA3a40A2D46D54dc6e88c08a9d98Ec47785CB2366",
|
|
37
|
+
registry: "0x3e5e23a5fF6548958ab3dDf42c51A59713cE550C",
|
|
38
|
+
log: "0x4E4cB48f3802bD519E0Eed896dF82197B0ecBc09",
|
|
39
|
+
gate: "0x84d01e0869fbCb07c0141E49c390ffdc064D6882",
|
|
40
40
|
stakeToken: "0xc717546497343911750768335b649b86d0a6eCcf",
|
|
41
41
|
bountyToken: "0xC4ec72794bF3Ce9ba1DE4B38085464c2eb0cDCf2",
|
|
42
42
|
},
|
package/dist/state.js
CHANGED
|
@@ -2,6 +2,22 @@ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, re
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
export const stateDir = () => join(homedir(), ".config", "brainchain-worker");
|
|
5
|
+
/** The decision count a one-shot watch run stopped at, kept per decision log; undefined when there is none or it cannot be read. */
|
|
6
|
+
export function readWatched(log, dir = stateDir()) {
|
|
7
|
+
try {
|
|
8
|
+
const { seen } = JSON.parse(readFileSync(join(dir, `watch.${log.toLowerCase()}.json`), "utf8"));
|
|
9
|
+
return typeof seen === "string" && /^\d+$/.test(seen) ? BigInt(seen) : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function writeWatched(log, seen, dir = stateDir()) {
|
|
16
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
17
|
+
const file = join(dir, `watch.${log.toLowerCase()}.json`);
|
|
18
|
+
writeFileSync(`${file}.tmp`, JSON.stringify({ seen: seen.toString() }), { mode: 0o600 });
|
|
19
|
+
renameSync(`${file}.tmp`, file);
|
|
20
|
+
}
|
|
5
21
|
const empty = (board) => ({ board: board.toLowerCase(), tasks: {}, done: [] });
|
|
6
22
|
export function memoryStore(board = "") {
|
|
7
23
|
return { state: empty(board), save: () => undefined };
|
package/dist/watch.js
CHANGED
|
@@ -139,7 +139,7 @@ export function createReporter(network, account, fetcher = fetch) {
|
|
|
139
139
|
export function createWatch(options) {
|
|
140
140
|
const { network, chain, verify, log } = options;
|
|
141
141
|
let nextBlock = options.fromBlock ?? network.fromBlock;
|
|
142
|
-
let seenDecision;
|
|
142
|
+
let seenDecision = options.seenDecision;
|
|
143
143
|
const judged = new Set();
|
|
144
144
|
let findings = 0;
|
|
145
145
|
async function changes() {
|
|
@@ -188,7 +188,11 @@ export function createWatch(options) {
|
|
|
188
188
|
if (!verify)
|
|
189
189
|
return;
|
|
190
190
|
const count = await chain.decisions();
|
|
191
|
-
|
|
191
|
+
if (seenDecision === undefined && options.recent !== undefined)
|
|
192
|
+
seenDecision = count > options.recent ? count - options.recent : 0n;
|
|
193
|
+
// A log restarted under a new set of contracts has fewer decisions than the count kept from the old one.
|
|
194
|
+
if (seenDecision !== undefined && seenDecision > count)
|
|
195
|
+
seenDecision = options.recent === undefined ? undefined : count > options.recent ? count - options.recent : 0n;
|
|
192
196
|
if (seenDecision === undefined) {
|
|
193
197
|
seenDecision = count;
|
|
194
198
|
log(`${count} decisions recorded so far; new ones are checked from here`);
|
|
@@ -215,6 +219,7 @@ export function createWatch(options) {
|
|
|
215
219
|
}
|
|
216
220
|
}
|
|
217
221
|
seenDecision = count;
|
|
222
|
+
options.saw?.(count);
|
|
218
223
|
if (!options.report || (checked.length === 0 && found.length === 0))
|
|
219
224
|
return;
|
|
220
225
|
try {
|
package/dist/work.js
CHANGED
|
@@ -75,7 +75,9 @@ function signalGroup(pid, signal) {
|
|
|
75
75
|
process.kill(-pid, signal);
|
|
76
76
|
}
|
|
77
77
|
catch (error) {
|
|
78
|
-
|
|
78
|
+
// macOS answers EPERM for a group whose members are already exiting; the group is the worker's own child either way.
|
|
79
|
+
const { code } = error;
|
|
80
|
+
if (code !== "ESRCH" && code !== "EPERM")
|
|
79
81
|
throw error;
|
|
80
82
|
}
|
|
81
83
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainchain-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Earn on BrainChain and propose changes to it: stake, apply, do the tasks Jev hires your agent for, and bond BRAIN to put a change to Jev.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "BrainChain <hello@usebrainchain.com>",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"prepack": "npm run build"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"brainchain-verify": "^0.1
|
|
31
|
+
"brainchain-verify": "^0.3.1",
|
|
32
32
|
"viem": "^2.0.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|