@dopamint-fun/open-sdk 0.2.0-dev.4 → 0.2.0-dev.6
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 +3 -3
- package/dist/agentHttp.js +1 -1
- package/dist/bytes.js +1 -1
- package/dist/claim.js +1 -1
- package/dist/cli.js +36 -50
- package/dist/identity.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/offer.js +3 -3
- package/dist/openTournament.js +3 -3
- package/dist/room.js +2 -2
- package/dist/seatTurn.js +1 -1
- package/dist/session.js +8 -8
- package/dist/sessionWire.js +1 -1
- package/dist/settlement.d.ts +5 -0
- package/dist/settlement.js +6 -1
- package/dist/tour.js +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# `libs/dopa-open
|
|
1
|
+
# `libs/dopa-open/client-ts`
|
|
2
2
|
|
|
3
3
|
Status: implemented
|
|
4
4
|
Ownership: Optional official TypeScript SDK for DOPA-OPEN self-custody agents:
|
|
@@ -36,8 +36,8 @@ signatures, not whether this package is used. Allowed dependencies:
|
|
|
36
36
|
## Parity
|
|
37
37
|
|
|
38
38
|
Preimages and signatures are pinned byte-identical to the Rust signer by
|
|
39
|
-
`libs/dopa-open
|
|
40
|
-
`libs/dopa-open
|
|
39
|
+
`libs/dopa-open/client-rs/vectors/ts-signer-parity.json`, generated by
|
|
40
|
+
`libs/dopa-open/client-rs/tests/ts_parity_vectors.rs` and replayed by
|
|
41
41
|
`src/parity.test.ts` here. Regenerate with
|
|
42
42
|
`WRITE_TS_PARITY_VECTORS=1 cargo test -p dopa-open-client-rs --test
|
|
43
43
|
ts_parity_vectors`; a wire change updates both sides together.
|
package/dist/agentHttp.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* tour or table request without ever holding a bearer credential.
|
|
3
3
|
*
|
|
4
4
|
* Byte-identical to `dopa_open_api::agent_http`, pinned by
|
|
5
|
-
* `libs/dopa-open
|
|
5
|
+
* `libs/dopa-open/api/vectors/agent_http_capability_v1.json`. A bearer token
|
|
6
6
|
* is a secret presented per request; a signature is not, so each capability is
|
|
7
7
|
* bound to the method, the request target, a digest of the exact body bytes,
|
|
8
8
|
* and a single-use nonce inside a bounded window. One minted to enter the
|
package/dist/bytes.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* These helpers exist so every framing in this package spells a number the
|
|
6
6
|
* same way; a preimage that drifts from the Rust client by one byte produces
|
|
7
7
|
* signatures the authority silently rejects, which is why the parity vectors
|
|
8
|
-
* in `libs/dopa-open
|
|
8
|
+
* in `libs/dopa-open/client-rs/vectors/` pin all of it.
|
|
9
9
|
*/
|
|
10
10
|
export function toHex(bytes) {
|
|
11
11
|
let out = "";
|
package/dist/claim.js
CHANGED
|
@@ -146,5 +146,5 @@ export function decodeClaimInvite(token) {
|
|
|
146
146
|
/** Where the owner goes to accept: the agent's claim page with the token. */
|
|
147
147
|
export function claimInviteLink(arenaOrigin, agentIdHex, token) {
|
|
148
148
|
const id = agentIdHex.startsWith("0x") ? agentIdHex : `0x${agentIdHex}`;
|
|
149
|
-
return `${arenaOrigin.replace(/\/$/, "")}/
|
|
149
|
+
return `${arenaOrigin.replace(/\/$/, "")}/open/agents/${id}/claim?invite=${token}`;
|
|
150
150
|
}
|
package/dist/cli.js
CHANGED
|
@@ -24,13 +24,19 @@ import { acceptAndAwaitAdmission } from "./offer.js";
|
|
|
24
24
|
import { playTour, queueUntilSeated } from "./tour.js";
|
|
25
25
|
import { joinRoomWhenComposed, MIN_ROOM_SEATS, openRoom, roomInvitePrompt, } from "./room.js";
|
|
26
26
|
import { disputeHolding, JoinRefused, joinTransaction, leaveTransaction, listTournaments, matchmakingOverLine, planJoin, presentToTournament, readAgentEntry, readAgentEntrySettled, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, } from "./openTournament.js";
|
|
27
|
-
import { authorityOriginFromSessionBase, buildConsentRequest, digestForPrompt, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
|
|
27
|
+
import { authorityOriginFromSessionBase, buildConsentRequest, DEFAULT_OPEN_API_BASE_URL, digestForPrompt, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
|
|
28
28
|
import { actionSigningBytes, joinSigningBytes, requireSessionVersion, resumeSigningBytes, SESSION_VERSION, } from "./sessionWire.js";
|
|
29
29
|
import { describeNext, refusalMessageFromText } from "./refusal.js";
|
|
30
30
|
function fail(message) {
|
|
31
31
|
console.error(`dopa-open: ${message}`);
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
|
+
/** Resolves `--product-url`, falling back to `DOPA_OPEN_PRODUCT_URL` and then
|
|
35
|
+
* to the deployed default — the same precedence and env name the Rust CLI
|
|
36
|
+
* uses, so a script that sets one variable configures both binaries. */
|
|
37
|
+
function resolveProductUrl(explicit) {
|
|
38
|
+
return explicit ?? process.env.DOPA_OPEN_PRODUCT_URL ?? DEFAULT_OPEN_API_BASE_URL;
|
|
39
|
+
}
|
|
34
40
|
const hex = (value, field) => {
|
|
35
41
|
if (typeof value !== "string")
|
|
36
42
|
fail(`${field} must be a hex string`);
|
|
@@ -116,9 +122,7 @@ async function commandRegister(args) {
|
|
|
116
122
|
"dry-run": { type: "boolean", default: false },
|
|
117
123
|
},
|
|
118
124
|
});
|
|
119
|
-
const productUrl = values["product-url"];
|
|
120
|
-
if (!productUrl && !values["dry-run"])
|
|
121
|
-
fail("--product-url is required (or pass --dry-run to print the request)");
|
|
125
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
122
126
|
const agent = loadKeypair(values.key);
|
|
123
127
|
const versions = (raw, flag) => {
|
|
124
128
|
const list = raw.split(",").map((piece) => Number.parseInt(piece, 10));
|
|
@@ -162,7 +166,7 @@ async function commandRegister(args) {
|
|
|
162
166
|
console.log(JSON.stringify(request, null, 2));
|
|
163
167
|
return;
|
|
164
168
|
}
|
|
165
|
-
const response = await fetch(`${productUrl.replace(/\/$/, "")}/
|
|
169
|
+
const response = await fetch(`${productUrl.replace(/\/$/, "")}/v1/agents`, {
|
|
166
170
|
method: "POST",
|
|
167
171
|
headers: { "content-type": "application/json" },
|
|
168
172
|
body: JSON.stringify(request),
|
|
@@ -225,9 +229,7 @@ async function commandName(args) {
|
|
|
225
229
|
bio: { type: "string" },
|
|
226
230
|
},
|
|
227
231
|
});
|
|
228
|
-
const productUrl = values["product-url"];
|
|
229
|
-
if (!productUrl)
|
|
230
|
-
fail("--product-url is required");
|
|
232
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
231
233
|
if (!values["agent-id"])
|
|
232
234
|
fail("--agent-id is required");
|
|
233
235
|
if (!values.name && !values.handle && !values.bio)
|
|
@@ -258,7 +260,7 @@ async function commandName(args) {
|
|
|
258
260
|
* against what they believed, and the refusal for getting it wrong is a bare
|
|
259
261
|
* `stale_generation` that names neither. */
|
|
260
262
|
async function readKeyState(productUrl, agentId) {
|
|
261
|
-
const response = await fetch(`${productUrl}/
|
|
263
|
+
const response = await fetch(`${productUrl}/v1/agents/${encodeURIComponent(agentId)}`);
|
|
262
264
|
if (!response.ok)
|
|
263
265
|
fail(`could not read agent ${agentId} (${response.status})`);
|
|
264
266
|
const wire = (await response.json());
|
|
@@ -291,9 +293,7 @@ async function commandKey(args) {
|
|
|
291
293
|
"dry-run": { type: "boolean", default: false },
|
|
292
294
|
},
|
|
293
295
|
});
|
|
294
|
-
const productUrl = values["product-url"]
|
|
295
|
-
if (!productUrl)
|
|
296
|
-
fail("--product-url is required");
|
|
296
|
+
const productUrl = resolveProductUrl(values["product-url"]).replace(/\/$/, "");
|
|
297
297
|
const agentId = values["agent-id"];
|
|
298
298
|
if (!agentId)
|
|
299
299
|
fail("--agent-id is required");
|
|
@@ -356,7 +356,7 @@ async function commandKey(args) {
|
|
|
356
356
|
return;
|
|
357
357
|
}
|
|
358
358
|
const path = retire ? "revocations" : "key-rotations";
|
|
359
|
-
const response = await fetch(`${productUrl}/
|
|
359
|
+
const response = await fetch(`${productUrl}/v1/agents/${encodeURIComponent(agentId)}/${path}`, {
|
|
360
360
|
method: "POST",
|
|
361
361
|
headers: { "content-type": "application/json" },
|
|
362
362
|
body: JSON.stringify(request),
|
|
@@ -486,9 +486,7 @@ async function commandPlay(args) {
|
|
|
486
486
|
"disconnect-after-actions": { type: "string" },
|
|
487
487
|
},
|
|
488
488
|
});
|
|
489
|
-
const productUrl = values["product-url"];
|
|
490
|
-
if (!productUrl)
|
|
491
|
-
fail("--product-url is required");
|
|
489
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
492
490
|
if (!values["agent-id"])
|
|
493
491
|
fail("--agent-id is required");
|
|
494
492
|
/* A tour seat is played over the Product API, so it needs none of the
|
|
@@ -581,7 +579,7 @@ function runRecordPath(keyFile) {
|
|
|
581
579
|
}
|
|
582
580
|
function writeRunRecord(keyFile, record) {
|
|
583
581
|
const path = runRecordPath(keyFile);
|
|
584
|
-
const watchUrl = `${record.productUrl.replace(/\/$/, "")}/
|
|
582
|
+
const watchUrl = `${record.productUrl.replace(/\/$/, "")}/open/matches/0x${record.executionId.replace(/^0x/i, "")}`;
|
|
585
583
|
writeFileSync(path, `${JSON.stringify({ ...record, watchUrl, at: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 });
|
|
586
584
|
return path;
|
|
587
585
|
}
|
|
@@ -642,7 +640,7 @@ async function signedGet(productUrl, target, agent, agentIdHex) {
|
|
|
642
640
|
headers: { [AGENT_HTTP_CAPABILITY_HEADER]: header },
|
|
643
641
|
});
|
|
644
642
|
}
|
|
645
|
-
/** `dopa-open me`:
|
|
643
|
+
/** `dopa-open me`: Open's record of this agent, signed for.
|
|
646
644
|
*
|
|
647
645
|
* Both agents that played on 2026-09-06 wrote this request by hand, against
|
|
648
646
|
* the skill's own warning that a hand-written signature drifts by a byte. */
|
|
@@ -657,13 +655,13 @@ async function signedGet(productUrl, target, agent, agentIdHex) {
|
|
|
657
655
|
async function resolveAgentId(productUrl, ownerAddressHex, given) {
|
|
658
656
|
if (given)
|
|
659
657
|
return given;
|
|
660
|
-
const response = await fetch(`${productUrl.replace(/\/$/, "")}/
|
|
658
|
+
const response = await fetch(`${productUrl.replace(/\/$/, "")}/v1/agents?owner=${ownerAddressHex}&limit=60`);
|
|
661
659
|
if (!response.ok)
|
|
662
660
|
fail(`--agent-id was not given and the roster could not be read (${response.status})`);
|
|
663
661
|
const body = (await response.json());
|
|
664
662
|
const agents = body.agents ?? [];
|
|
665
663
|
if (agents.length === 0)
|
|
666
|
-
fail(`no agent on this
|
|
664
|
+
fail(`no agent on this Open deployment is claimed by ${ownerAddressHex}. The roster lists an agent under the wallet that ` +
|
|
667
665
|
"claimed it, so one that registered itself is not here until it is claimed; pass --agent-id (register printed it)");
|
|
668
666
|
if (agents.length > 1)
|
|
669
667
|
fail(`this key owns ${agents.length} agents; pass --agent-id to say which:\n ${agents
|
|
@@ -680,16 +678,14 @@ async function commandMe(args) {
|
|
|
680
678
|
"agent-id": { type: "string" },
|
|
681
679
|
},
|
|
682
680
|
});
|
|
683
|
-
const productUrl = values["product-url"];
|
|
684
|
-
if (!productUrl)
|
|
685
|
-
fail("--product-url is required");
|
|
681
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
686
682
|
const agent = loadKeypair(values.key);
|
|
687
683
|
/* Without `--agent-id`, ask the roster which agent this key owns. */
|
|
688
684
|
const agentId = await resolveAgentId(productUrl, agent.ownerAddressHex, values["agent-id"]);
|
|
689
|
-
const response = await signedGet(productUrl, "/
|
|
685
|
+
const response = await signedGet(productUrl, "/v1/agent/me", agent, agentId);
|
|
690
686
|
const body = await response.text();
|
|
691
687
|
if (!response.ok)
|
|
692
|
-
fail(refusalMessageFromText(`agent/me at ${productUrl}/
|
|
688
|
+
fail(refusalMessageFromText(`agent/me at ${productUrl}/v1/agent/me`, response.status, body));
|
|
693
689
|
try {
|
|
694
690
|
console.log(JSON.stringify(JSON.parse(body), null, 2));
|
|
695
691
|
}
|
|
@@ -857,7 +853,7 @@ async function commandAct(args) {
|
|
|
857
853
|
* this, so it is worth a caller of its own rather than a flag on the play
|
|
858
854
|
* loop. Prints what the authority recorded. */
|
|
859
855
|
async function consentToTerminal(options) {
|
|
860
|
-
const offerResponse = await fetch(`${options.productUrl.replace(/\/$/, "")}/
|
|
856
|
+
const offerResponse = await fetch(`${options.productUrl.replace(/\/$/, "")}/v1/playground/matches/${options.offerId}`);
|
|
861
857
|
const offerBody = await offerResponse.text();
|
|
862
858
|
if (!offerResponse.ok)
|
|
863
859
|
fail(`offer read failed (${offerResponse.status}): ${offerBody}`);
|
|
@@ -1007,9 +1003,7 @@ async function commandConsent(args) {
|
|
|
1007
1003
|
"agent-id": { type: "string" },
|
|
1008
1004
|
},
|
|
1009
1005
|
});
|
|
1010
|
-
const productUrl = values["product-url"];
|
|
1011
|
-
if (!productUrl)
|
|
1012
|
-
fail("--product-url is required");
|
|
1006
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
1013
1007
|
if (!values.offer)
|
|
1014
1008
|
fail("--offer is required");
|
|
1015
1009
|
if (!values.seat)
|
|
@@ -1045,9 +1039,7 @@ async function commandQueue(args) {
|
|
|
1045
1039
|
play: { type: "boolean", default: false },
|
|
1046
1040
|
},
|
|
1047
1041
|
});
|
|
1048
|
-
const productUrl = values["product-url"];
|
|
1049
|
-
if (!productUrl)
|
|
1050
|
-
fail("--product-url is required");
|
|
1042
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
1051
1043
|
if (!values["agent-id"])
|
|
1052
1044
|
fail("--agent-id is required");
|
|
1053
1045
|
const tour = values.tour;
|
|
@@ -1091,7 +1083,7 @@ async function commandQueue(args) {
|
|
|
1091
1083
|
Labelled `agent_page`, never `watch`. It was `watch` for one release, and
|
|
1092
1084
|
an agent reading that line handed its operator a profile under the word
|
|
1093
1085
|
WATCH; `watch` is the table, printed below once there is one. */
|
|
1094
|
-
console.log(`agent_page ${client.productUrl.replace(/\/$/, "")}/
|
|
1086
|
+
console.log(`agent_page ${client.productUrl.replace(/\/$/, "")}/open/agents/0x${values["agent-id"].replace(/^0x/i, "")}`);
|
|
1095
1087
|
let lastWaitingLine = "";
|
|
1096
1088
|
const seated = await queueUntilSeated(client, tour, {
|
|
1097
1089
|
minAgents,
|
|
@@ -1146,7 +1138,7 @@ async function commandQueue(args) {
|
|
|
1146
1138
|
executionId: admitted.executionId,
|
|
1147
1139
|
};
|
|
1148
1140
|
const recordPath = writeRunRecord(values.key, runRecord);
|
|
1149
|
-
console.log(`watch ${client.productUrl.replace(/\/$/, "")}/
|
|
1141
|
+
console.log(`watch ${client.productUrl.replace(/\/$/, "")}/open/matches/0x${admitted.executionId.replace(/^0x/i, "")}`);
|
|
1150
1142
|
console.log(`run_record ${recordPath}`);
|
|
1151
1143
|
console.log(`reconnect ${reconnectCommand(values.key, runRecord)}`);
|
|
1152
1144
|
takeSeatLock(values.key, values["agent-id"]);
|
|
@@ -1238,9 +1230,7 @@ async function commandRoom(args) {
|
|
|
1238
1230
|
"timeout-ms": { type: "string", default: String(30 * 60_000) },
|
|
1239
1231
|
},
|
|
1240
1232
|
});
|
|
1241
|
-
const productUrl = values["product-url"];
|
|
1242
|
-
if (!productUrl)
|
|
1243
|
-
fail("--product-url is required");
|
|
1233
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
1244
1234
|
if (!values["agent-id"])
|
|
1245
1235
|
fail("--agent-id is required");
|
|
1246
1236
|
const client = {
|
|
@@ -1279,7 +1269,7 @@ async function commandRoom(args) {
|
|
|
1279
1269
|
that word is the match link on every door, and `room join` prints it
|
|
1280
1270
|
once the room composes, so two different links under one label was a
|
|
1281
1271
|
thing an agent had to be warned about. */
|
|
1282
|
-
console.log(`room_page ${client.productUrl.replace(/\/$/, "")}/
|
|
1272
|
+
console.log(`room_page ${client.productUrl.replace(/\/$/, "")}/open/tours/private-room/tables/${room.tableId}`);
|
|
1283
1273
|
return;
|
|
1284
1274
|
}
|
|
1285
1275
|
const tableId = values["table-id"];
|
|
@@ -1328,7 +1318,7 @@ async function commandRoom(args) {
|
|
|
1328
1318
|
executionId: admitted.executionId,
|
|
1329
1319
|
};
|
|
1330
1320
|
const recordPath = writeRunRecord(values.key, runRecord);
|
|
1331
|
-
console.log(`watch ${client.productUrl.replace(/\/$/, "")}/
|
|
1321
|
+
console.log(`watch ${client.productUrl.replace(/\/$/, "")}/open/matches/0x${admitted.executionId.replace(/^0x/i, "")}`);
|
|
1332
1322
|
console.log(`run_record ${recordPath}`);
|
|
1333
1323
|
console.log(`reconnect ${reconnectCommand(values.key, runRecord)}`);
|
|
1334
1324
|
takeSeatLock(values.key, values["agent-id"]);
|
|
@@ -1395,9 +1385,7 @@ async function commandTournament(args) {
|
|
|
1395
1385
|
"until-out": { type: "boolean", default: false },
|
|
1396
1386
|
},
|
|
1397
1387
|
});
|
|
1398
|
-
const productUrl = values["product-url"];
|
|
1399
|
-
if (!productUrl)
|
|
1400
|
-
fail("--product-url is required");
|
|
1388
|
+
const productUrl = resolveProductUrl(values["product-url"]);
|
|
1401
1389
|
const agent = loadKeypair(values.key);
|
|
1402
1390
|
const tournamentId = await tournamentArg(productUrl, values.tournament);
|
|
1403
1391
|
const overview = await readTournament(productUrl, tournamentId);
|
|
@@ -1408,7 +1396,7 @@ async function commandTournament(args) {
|
|
|
1408
1396
|
this agent holds now is on the owner's side of the book. */
|
|
1409
1397
|
const playTakenBackNext = () => describeNext({
|
|
1410
1398
|
action: "read",
|
|
1411
|
-
route: `/
|
|
1399
|
+
route: `/v1/tournaments/${tournamentId}/owners/${owner ?? "{owner}"}`,
|
|
1412
1400
|
});
|
|
1413
1401
|
const held = async () => owner ? await playsHeldBy(productUrl, tournamentId, owner, chip) : [];
|
|
1414
1402
|
let plays = await held();
|
|
@@ -1642,7 +1630,7 @@ async function commandTournament(args) {
|
|
|
1642
1630
|
console.log(`offer ${offerId}`);
|
|
1643
1631
|
console.log(`seat ${seated.seat}`);
|
|
1644
1632
|
if (seated.executionId)
|
|
1645
|
-
console.log(`watch ${client.productUrl.replace(/\/$/, "")}/
|
|
1633
|
+
console.log(`watch ${client.productUrl.replace(/\/$/, "")}/open/matches/${seated.executionId}`);
|
|
1646
1634
|
if (!values.play)
|
|
1647
1635
|
return;
|
|
1648
1636
|
const seat = seated.seat;
|
|
@@ -1684,7 +1672,7 @@ async function commandTournament(args) {
|
|
|
1684
1672
|
}
|
|
1685
1673
|
/** `dopa-open claim-invite`: the link a wallet needs to claim this agent.
|
|
1686
1674
|
*
|
|
1687
|
-
* A claim is two consents. The wallet signs on
|
|
1675
|
+
* A claim is two consents. The wallet signs on Open's claim page; you, holding
|
|
1688
1676
|
* the agent's key, sign the invitation that lets it. Name the wallet with
|
|
1689
1677
|
* `--owner` to make the link good for that wallet alone; leave it out and the
|
|
1690
1678
|
* link is good for whoever opens it, for as long as `--hours` says. */
|
|
@@ -1700,9 +1688,7 @@ async function commandClaimInvite(args) {
|
|
|
1700
1688
|
hours: { type: "string", default: "24" },
|
|
1701
1689
|
},
|
|
1702
1690
|
});
|
|
1703
|
-
const productUrl = values["product-url"]
|
|
1704
|
-
if (!productUrl)
|
|
1705
|
-
fail("--product-url is required");
|
|
1691
|
+
const productUrl = resolveProductUrl(values["product-url"]).replace(/\/$/, "");
|
|
1706
1692
|
if (!values["agent-id"])
|
|
1707
1693
|
fail("--agent-id is required");
|
|
1708
1694
|
const hours = Number(values.hours);
|
|
@@ -1720,7 +1706,7 @@ async function commandClaimInvite(args) {
|
|
|
1720
1706
|
expects the old form. Both decode to the same 192 bytes. */
|
|
1721
1707
|
const token = encodeClaimInvite(invite);
|
|
1722
1708
|
const linkToken = encodeClaimInviteCompact(invite);
|
|
1723
|
-
/*
|
|
1709
|
+
/* Open's pages and its API share an origin on a deployment; a local
|
|
1724
1710
|
stack serves them apart, which is what --arena-url is for. */
|
|
1725
1711
|
const arena = (values["arena-url"] ?? productUrl).replace(/\/$/, "");
|
|
1726
1712
|
console.log(`invite ${token}`);
|
|
@@ -1732,7 +1718,7 @@ const USAGE = `usage: dopa-open <command>
|
|
|
1732
1718
|
|
|
1733
1719
|
keygen generate a keypair into .dopa-keypair (Sui suiprivkey format)
|
|
1734
1720
|
address print the owner address and public key of an existing key file
|
|
1735
|
-
me print
|
|
1721
|
+
me print Open's record of this agent, signed for (agent/me)
|
|
1736
1722
|
claim-invite
|
|
1737
1723
|
mint the claim link a wallet needs to claim this agent
|
|
1738
1724
|
register self-allocate and register the agent with a product deployment
|
package/dist/identity.js
CHANGED
|
@@ -56,7 +56,7 @@ export async function nameAgent(args) {
|
|
|
56
56
|
expiresAtMs,
|
|
57
57
|
}));
|
|
58
58
|
const agentId = `0x${args.agentId.replace(/^0x/, "")}`;
|
|
59
|
-
const response = await fetchImpl(`${args.productUrl.replace(/\/$/, "")}/
|
|
59
|
+
const response = await fetchImpl(`${args.productUrl.replace(/\/$/, "")}/v1/agents/${agentId}/identity`, {
|
|
60
60
|
method: "PUT",
|
|
61
61
|
headers: { "content-type": "application/json" },
|
|
62
62
|
body: JSON.stringify({
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { actionSigningBytes, encodeActionFrame, encodeJoinFrame, encodeResumeFra
|
|
|
5
5
|
export { encodeAckFrame, SESSION_ERROR_NAMES, MAX_PREDICTION_PACING_MS, admitAuthorityEvent, applyPredictionGateStatus, decodeAuthorityMessage, freshSessionBoundary, predictionGateMessage, predictionGatePreparationOf, predictionGateTargetReceipt, sessionErrorHint, sessionErrorName, PredictionGateConflictError, SessionBoundaryError, type AcceptedSessionBoundary, type AuthorityMessage, type PredictionGateMessage, type PredictionGateOpening, type PredictionGatePreparation, type PredictionGateRelease, type PredictionGateStatus, type PredictionWindowRef, type ReceiptRef, type ResumeCursor, type StateRef, type ViewSnapshot, } from "./sessionCodec.js";
|
|
6
6
|
export { type OpenTableView, type PlayReport, type SeatDecision, type SeatDecisionResult, type SeatPosition, SessionClient, SessionRefusal, chooseSeatAction, playSeat, } from "./session.js";
|
|
7
7
|
export { cardFromByte, decodeLegalActions, decodeParticipantView, encodeAction, pickAction, type Card, type Rank, type Suit, type TexasAction, type TexasLegalActions, type TexasSeatView, } from "./texas.js";
|
|
8
|
-
export { authorityOriginFromSessionBase, buildConsentRequest, digestForPrompt, recomputeSettlementDigest, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
|
|
8
|
+
export { authorityOriginFromSessionBase, buildConsentRequest, DEFAULT_OPEN_API_BASE_URL, digestForPrompt, recomputeSettlementDigest, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
|
|
9
9
|
export { PACKAGE_NAME, RELEASE_CHANNELS, channelDistTag, installCommand, installSpec, resolveChannel, versionMatchesChannel, type ReleaseChannel, } from "./channel.js";
|
|
10
10
|
export { fromHex, toHex } from "./bytes.js";
|
|
11
11
|
export { AGENT_HTTP_CAPABILITY_HEADER, AGENT_HTTP_CAPABILITY_NONCE_BYTES, MAX_AGENT_HTTP_CAPABILITY_WINDOW_MS, agentHttpCanonicalPayload, agentHttpSigningBytes, decodeAgentHttpHeader, encodeAgentHttpHeader, mintAgentHttpCapability, type AgentHttpBinding, type AgentHttpCapability, } from "./agentHttp.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Generate and hold an ed25519 keypair (Sui format, `.dopa-keypair`), produce
|
|
4
4
|
* the canonical signatures, and drive the Participant Session against
|
|
5
5
|
* `session_base_url`. Byte parity with the Rust client is pinned by
|
|
6
|
-
* `libs/dopa-open
|
|
6
|
+
* `libs/dopa-open/client-rs/vectors/ts-signer-parity.json`.
|
|
7
7
|
*/
|
|
8
8
|
export { DEFAULT_KEY_FILE, generateKeypair, keypairFromSeed, loadKeypair, saveKeypair, signOwnerAuthenticator, signRaw, } from "./keypair.js";
|
|
9
9
|
export { deriveAgentId, registerCanonicalPayload, registerPayloadDigest, } from "./registration.js";
|
|
@@ -12,7 +12,7 @@ export { actionSigningBytes, encodeActionFrame, encodeJoinFrame, encodeResumeFra
|
|
|
12
12
|
export { encodeAckFrame, SESSION_ERROR_NAMES, MAX_PREDICTION_PACING_MS, admitAuthorityEvent, applyPredictionGateStatus, decodeAuthorityMessage, freshSessionBoundary, predictionGateMessage, predictionGatePreparationOf, predictionGateTargetReceipt, sessionErrorHint, sessionErrorName, PredictionGateConflictError, SessionBoundaryError, } from "./sessionCodec.js";
|
|
13
13
|
export { SessionClient, SessionRefusal, chooseSeatAction, playSeat, } from "./session.js";
|
|
14
14
|
export { cardFromByte, decodeLegalActions, decodeParticipantView, encodeAction, pickAction, } from "./texas.js";
|
|
15
|
-
export { authorityOriginFromSessionBase, buildConsentRequest, digestForPrompt, recomputeSettlementDigest, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
|
|
15
|
+
export { authorityOriginFromSessionBase, buildConsentRequest, DEFAULT_OPEN_API_BASE_URL, digestForPrompt, recomputeSettlementDigest, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
|
|
16
16
|
export { PACKAGE_NAME, RELEASE_CHANNELS, channelDistTag, installCommand, installSpec, resolveChannel, versionMatchesChannel, } from "./channel.js";
|
|
17
17
|
export { fromHex, toHex } from "./bytes.js";
|
|
18
18
|
export { AGENT_HTTP_CAPABILITY_HEADER, AGENT_HTTP_CAPABILITY_NONCE_BYTES, MAX_AGENT_HTTP_CAPABILITY_WINDOW_MS, agentHttpCanonicalPayload, agentHttpSigningBytes, decodeAgentHttpHeader, encodeAgentHttpHeader, mintAgentHttpCapability, } from "./agentHttp.js";
|
package/dist/offer.js
CHANGED
|
@@ -52,7 +52,7 @@ function record(raw) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
export async function readOffer(productUrl, offerId) {
|
|
55
|
-
const { status, json } = await readJson(`${productUrl.replace(/\/$/, "")}/
|
|
55
|
+
const { status, json } = await readJson(`${productUrl.replace(/\/$/, "")}/v1/playground/matches/${offerId}`);
|
|
56
56
|
if (status !== 200)
|
|
57
57
|
throw new Error(refusalMessage("offer read", status, json));
|
|
58
58
|
return record(json);
|
|
@@ -81,7 +81,7 @@ export async function acceptOffer(productUrl, offerId, seat, agent) {
|
|
|
81
81
|
signature: Array.from(signed.signature),
|
|
82
82
|
seat_possession_signature: Array.from(signed.seatPossessionSignature),
|
|
83
83
|
});
|
|
84
|
-
const { status, json } = await readJson(`${productUrl.replace(/\/$/, "")}/
|
|
84
|
+
const { status, json } = await readJson(`${productUrl.replace(/\/$/, "")}/v1/playground/matches/${offerId}/acceptances`, { method: "POST", headers: { "content-type": "application/json" }, body });
|
|
85
85
|
if (status === 200 || status === 201)
|
|
86
86
|
return;
|
|
87
87
|
if (status === 409)
|
|
@@ -89,7 +89,7 @@ export async function acceptOffer(productUrl, offerId, seat, agent) {
|
|
|
89
89
|
throw new Error(refusalMessage("acceptance", status, json));
|
|
90
90
|
}
|
|
91
91
|
async function admit(productUrl, offerId, agentId, agent) {
|
|
92
|
-
const target = `/
|
|
92
|
+
const target = `/v1/playground/matches/${offerId}/admit`;
|
|
93
93
|
const { header } = await mintAgentHttpCapability(agent, agentId, {
|
|
94
94
|
method: "POST",
|
|
95
95
|
requestTarget: target,
|
package/dist/openTournament.js
CHANGED
|
@@ -26,7 +26,7 @@ function base(productUrl) {
|
|
|
26
26
|
return productUrl.replace(/\/$/, "");
|
|
27
27
|
}
|
|
28
28
|
function tournamentPath(tournamentId, rest = "") {
|
|
29
|
-
return `/
|
|
29
|
+
return `/v1/tournaments/${tournamentId}${rest}`;
|
|
30
30
|
}
|
|
31
31
|
async function readJson(fetchImpl, url) {
|
|
32
32
|
const response = await fetchImpl(url);
|
|
@@ -57,7 +57,7 @@ export async function readTournament(productUrl, tournamentId, fetchImpl = fetch
|
|
|
57
57
|
}
|
|
58
58
|
/** The open-entry tournaments the product runs. */
|
|
59
59
|
export async function listTournaments(productUrl, fetchImpl = fetch) {
|
|
60
|
-
const { status, json } = await readJson(fetchImpl, `${base(productUrl)}/
|
|
60
|
+
const { status, json } = await readJson(fetchImpl, `${base(productUrl)}/v1/tournaments`);
|
|
61
61
|
if (status === 404)
|
|
62
62
|
return [];
|
|
63
63
|
if (status !== 200)
|
|
@@ -209,7 +209,7 @@ plays, owner) {
|
|
|
209
209
|
if (entry && entry.state !== "idle")
|
|
210
210
|
throw new JoinRefused(`this agent is already ${entry.state}${entry.state === "seated" ? " at a table" : ""}; an agent plays one table at a time`, {
|
|
211
211
|
action: "wait",
|
|
212
|
-
poll: `/
|
|
212
|
+
poll: `/v1/tournaments/${overview.tournamentId}/agents/${entry.chipAddress}`,
|
|
213
213
|
});
|
|
214
214
|
const booked = entry?.balance ?? 0;
|
|
215
215
|
const wallet = entry?.walletBalance ?? booked;
|
package/dist/room.js
CHANGED
|
@@ -57,7 +57,7 @@ export async function openRoom(client, seatCount) {
|
|
|
57
57
|
seatCount < MIN_ROOM_SEATS ||
|
|
58
58
|
seatCount > MAX_ROOM_SEATS)
|
|
59
59
|
throw new Error(`a private room seats ${MIN_ROOM_SEATS} to ${MAX_ROOM_SEATS}, not ${seatCount}`);
|
|
60
|
-
const { status, json } = await signedFetch(client, "POST", "/
|
|
60
|
+
const { status, json } = await signedFetch(client, "POST", "/v1/tables/private-rooms", { seatCount });
|
|
61
61
|
if (status !== 201) {
|
|
62
62
|
if (json?.code === "agent_not_claimed")
|
|
63
63
|
throw notClaimed();
|
|
@@ -95,7 +95,7 @@ export class RoomJoinRefusal extends Error {
|
|
|
95
95
|
}
|
|
96
96
|
/** Join a room by the id its opener handed out. */
|
|
97
97
|
export async function joinRoom(client, tableId) {
|
|
98
|
-
const target = `/
|
|
98
|
+
const target = `/v1/tables/private-rooms/${tableId}/join`;
|
|
99
99
|
const { status, json } = await signedFetch(client, "POST", target);
|
|
100
100
|
if (status !== 201) {
|
|
101
101
|
if (json?.code === "agent_not_claimed")
|
package/dist/seatTurn.js
CHANGED
|
@@ -478,7 +478,7 @@ async function sayAtTable(fetchImpl, session, agent, agentId, say) {
|
|
|
478
478
|
if (!trimmed)
|
|
479
479
|
return false;
|
|
480
480
|
const { mintAgentHttpCapability, AGENT_HTTP_CAPABILITY_HEADER } = await import("./agentHttp.js");
|
|
481
|
-
const target = `/
|
|
481
|
+
const target = `/v1/executions/${session.executionHex}/talk`;
|
|
482
482
|
const body = new TextEncoder().encode(JSON.stringify({ say: trimmed }));
|
|
483
483
|
try {
|
|
484
484
|
const { header } = await mintAgentHttpCapability(agent, agentId, {
|
package/dist/session.js
CHANGED
|
@@ -534,7 +534,7 @@ export async function openSeatSession(args) {
|
|
|
534
534
|
/* Read again while the product is coming back: a seat reopens its session
|
|
535
535
|
after every dropped stream, and the product answering 502 for the seconds
|
|
536
536
|
it restarts ended the seat as surely as a refusal would have. */
|
|
537
|
-
const offer = await fetchWhileRestarting(fetchImpl, `${product}/
|
|
537
|
+
const offer = await fetchWhileRestarting(fetchImpl, `${product}/v1/playground/matches/${args.offerId}`, args.restartRetry);
|
|
538
538
|
if (!offer.ok)
|
|
539
539
|
throw new Error(`offer read failed (${offer.status}): ${await offer.text()}`);
|
|
540
540
|
const record = (await offer.json());
|
|
@@ -1004,7 +1004,7 @@ export function normaliseCardCode(code) {
|
|
|
1004
1004
|
export async function readDisclosedEntitlement(fetchImpl, product, executionId, seat, capability, options = {}) {
|
|
1005
1005
|
const attempts = options.attempts ?? 15;
|
|
1006
1006
|
const pauseMs = options.pauseMs ?? 1_000;
|
|
1007
|
-
const target = `/
|
|
1007
|
+
const target = `/v1/spectator/executions/${executionId}`;
|
|
1008
1008
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
1009
1009
|
try {
|
|
1010
1010
|
const header = capability ? await capability("GET", target) : null;
|
|
@@ -1032,7 +1032,7 @@ export async function readDisclosedEntitlement(fetchImpl, product, executionId,
|
|
|
1032
1032
|
* gone -- which is precisely the moment this question is being asked. */
|
|
1033
1033
|
export async function readSittingStatus(fetchImpl, product, executionHex) {
|
|
1034
1034
|
try {
|
|
1035
|
-
const response = await fetchImpl(`${product}/
|
|
1035
|
+
const response = await fetchImpl(`${product}/v1/history/matches/${executionHex}`);
|
|
1036
1036
|
if (!response.ok)
|
|
1037
1037
|
return { state: "unknown" };
|
|
1038
1038
|
const wire = (await response.json());
|
|
@@ -1073,7 +1073,7 @@ export function agentReadCapability(agent, agentId) {
|
|
|
1073
1073
|
export async function readPublicTable(fetchImpl, product, executionHex, names = new Map(), capability) {
|
|
1074
1074
|
/* Bound to the target the server reconstructs from the request, which is the
|
|
1075
1075
|
origin-form path and not the absolute URL the fetch is given. */
|
|
1076
|
-
const target = `/
|
|
1076
|
+
const target = `/v1/spectator/executions/${executionHex}`;
|
|
1077
1077
|
try {
|
|
1078
1078
|
const header = capability ? await capability("GET", target) : null;
|
|
1079
1079
|
const response = await fetchImpl(`${product}${target}`, header === null
|
|
@@ -1134,14 +1134,14 @@ export async function readPublicTable(fetchImpl, product, executionHex, names =
|
|
|
1134
1134
|
return null;
|
|
1135
1135
|
}
|
|
1136
1136
|
}
|
|
1137
|
-
/** What an agent is called, from `GET /
|
|
1137
|
+
/** What an agent is called, from `GET /v1/agents/{id}/custody`, read
|
|
1138
1138
|
* once and remembered in `names`. Null where the read did not answer. */
|
|
1139
1139
|
async function readAgentNaming(fetchImpl, product, agentId, names) {
|
|
1140
1140
|
const known = names.get(agentId);
|
|
1141
1141
|
if (known !== undefined)
|
|
1142
1142
|
return known;
|
|
1143
1143
|
try {
|
|
1144
|
-
const response = await fetchImpl(`${product}/
|
|
1144
|
+
const response = await fetchImpl(`${product}/v1/agents/${agentId}/custody`);
|
|
1145
1145
|
if (!response.ok) {
|
|
1146
1146
|
names.set(agentId, null);
|
|
1147
1147
|
return null;
|
|
@@ -1164,7 +1164,7 @@ async function readAgentNaming(fetchImpl, product, agentId, names) {
|
|
|
1164
1164
|
* table: an empty list where the read did not answer. */
|
|
1165
1165
|
export async function readTableTalk(fetchImpl, product, executionHex, handIndex) {
|
|
1166
1166
|
try {
|
|
1167
|
-
const response = await fetchImpl(`${product}/
|
|
1167
|
+
const response = await fetchImpl(`${product}/v1/executions/${executionHex}/talk`);
|
|
1168
1168
|
if (!response.ok)
|
|
1169
1169
|
return [];
|
|
1170
1170
|
const wire = (await response.json());
|
|
@@ -1200,7 +1200,7 @@ function potChips(pot) {
|
|
|
1200
1200
|
/** File one line at the table, signed as this seat's agent. True when the
|
|
1201
1201
|
* product accepted it. */
|
|
1202
1202
|
async function sayAtTable(fetchImpl, product, executionHex, args, say) {
|
|
1203
|
-
const target = `/
|
|
1203
|
+
const target = `/v1/executions/${executionHex}/talk`;
|
|
1204
1204
|
const body = textBytes(JSON.stringify({ say }));
|
|
1205
1205
|
try {
|
|
1206
1206
|
const { header } = await mintAgentHttpCapability(args.agent, args.agentId, {
|
package/dist/sessionWire.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Join, action, and resume signing bodies live here; acknowledgements are
|
|
5
5
|
* unsigned and encoded next to the authority decoder. The layouts are pinned
|
|
6
|
-
* by `libs/dopa-open
|
|
6
|
+
* by `libs/dopa-open/client-rs/vectors/ts-signer-parity.json`.
|
|
7
7
|
*/
|
|
8
8
|
import { ByteWriter, frameSigningBytes, textBytes } from "./bytes.js";
|
|
9
9
|
const JOIN_DOMAIN = textBytes("arena_session::join");
|
package/dist/settlement.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** The Product API's deployed default origin. `dopa-open`'s `--product-url`
|
|
2
|
+
* (and `DOPA_OPEN_PRODUCT_URL`) default here, matching the Rust client and
|
|
3
|
+
* CLI's `DEFAULT_OPEN_API_BASE_URL`, so a caller only has to override it for
|
|
4
|
+
* a local stack or a non-default plane. */
|
|
5
|
+
export declare const DEFAULT_OPEN_API_BASE_URL = "https://open.dopamint.fun";
|
|
1
6
|
export interface SeatEntitlement {
|
|
2
7
|
seat: number;
|
|
3
8
|
amount: bigint;
|
package/dist/settlement.js
CHANGED
|
@@ -2,6 +2,11 @@ import { ByteReader, ByteWriter, equalBytes, fromHex, textBytes, toHex0x, } from
|
|
|
2
2
|
import { digestFramed } from "./crypto.js";
|
|
3
3
|
const ANCHOR_DOMAIN_ID_V1 = textBytes("arena_tunnel::anchor_domain_id_v1");
|
|
4
4
|
const SETTLEMENT_PURPOSE = textBytes("arena_tunnel::settlement");
|
|
5
|
+
/** The Product API's deployed default origin. `dopa-open`'s `--product-url`
|
|
6
|
+
* (and `DOPA_OPEN_PRODUCT_URL`) default here, matching the Rust client and
|
|
7
|
+
* CLI's `DEFAULT_OPEN_API_BASE_URL`, so a caller only has to override it for
|
|
8
|
+
* a local stack or a non-default plane. */
|
|
9
|
+
export const DEFAULT_OPEN_API_BASE_URL = "https://open.dopamint.fun";
|
|
5
10
|
export function encodeTerminalState(state) {
|
|
6
11
|
const writer = new ByteWriter()
|
|
7
12
|
.pushU16(1)
|
|
@@ -103,7 +108,7 @@ export function buildConsentRequest(args) {
|
|
|
103
108
|
}
|
|
104
109
|
export function settlementConsentPath(executionIdHex) {
|
|
105
110
|
const hex = executionIdHex.replace(/^0x/, "");
|
|
106
|
-
return `/
|
|
111
|
+
return `/v1/authority/executions/${hex}/settlements`;
|
|
107
112
|
}
|
|
108
113
|
export function authorityOriginFromSessionBase(sessionBaseUrl) {
|
|
109
114
|
const idx = sessionBaseUrl.indexOf("/v1/exec/");
|
package/dist/tour.js
CHANGED
|
@@ -63,8 +63,8 @@ function unavailable(error) {
|
|
|
63
63
|
}
|
|
64
64
|
export async function enterTour(client, tour, floor = {}) {
|
|
65
65
|
const target = tour === "playground"
|
|
66
|
-
? "/
|
|
67
|
-
: "/
|
|
66
|
+
? "/v1/tours/playground/entries"
|
|
67
|
+
: "/v1/tours/tournament/entries";
|
|
68
68
|
if (floor.minAgents !== undefined && tour !== "playground")
|
|
69
69
|
throw new Error("a floor of real agents only applies to the playground");
|
|
70
70
|
/* No body at all when nothing is stated: the join every client sent before
|
|
@@ -124,13 +124,13 @@ export async function queueUntilSeated(client, tour, options = {}) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
export async function readPosition(client, tableId) {
|
|
127
|
-
const { status, json } = await signedFetch(client, "GET", `/
|
|
127
|
+
const { status, json } = await signedFetch(client, "GET", `/v1/tables/${tableId}/decision`);
|
|
128
128
|
if (status !== 200)
|
|
129
129
|
throw new Error(refusalMessage("decision read", status, json));
|
|
130
130
|
return json;
|
|
131
131
|
}
|
|
132
132
|
export async function act(client, tableId, move) {
|
|
133
|
-
const { status, json } = await signedFetch(client, "POST", `/
|
|
133
|
+
const { status, json } = await signedFetch(client, "POST", `/v1/tables/${tableId}/actions`, move);
|
|
134
134
|
if (status !== 200)
|
|
135
135
|
throw new Error(refusalMessage("action", status, json));
|
|
136
136
|
return json;
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dopamint-fun/open-sdk",
|
|
3
|
-
"version": "0.2.0-dev.
|
|
3
|
+
"version": "0.2.0-dev.6",
|
|
4
4
|
"description": "DOPA-OPEN client SDK: generate and hold your own agent key, sign registrations, and play a Participant Session",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/CommandOSSLabs/dopamint-arena.git",
|
|
9
|
-
"directory": "libs/dopa-open
|
|
9
|
+
"directory": "libs/dopa-open/client-ts"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
12
|
"main": "dist/index.js",
|