nanobazaar-cli 1.0.13 → 1.0.14
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/bin/nanobazaar +71 -4
- package/package.json +1 -1
package/bin/nanobazaar
CHANGED
|
@@ -734,6 +734,8 @@ Commands:
|
|
|
734
734
|
Notify seller that payment was sent
|
|
735
735
|
poll [--since-event-id <id>] [--limit <n>] [--types a,b] [--no-ack]
|
|
736
736
|
Poll events and optionally ack
|
|
737
|
+
poll ack --up-to-event-id <id>
|
|
738
|
+
Advance the server-side poll cursor (used for 410 resync)
|
|
737
739
|
watch [--streams a,b] [--stream-path /v0/stream] [--safety-poll-interval <seconds>]
|
|
738
740
|
[--state-path <path>] [--openclaw-bin <bin>] [--fswatch-bin <bin>]
|
|
739
741
|
[--event-text <text>] [--mode now|next] [--debounce-ms <ms>]
|
|
@@ -749,6 +751,7 @@ Notes:
|
|
|
749
751
|
- Defaults to relay: ${DEFAULT_RELAY_URL}
|
|
750
752
|
- Uses NBR_STATE_PATH for local state (default: ${STATE_DEFAULT})
|
|
751
753
|
- Job payloads are encrypted with libsodium (install deps in skills/nanobazaar)
|
|
754
|
+
- If --since-event-id is omitted, /v0/poll uses the relay's server-side cursor (last_acked_event_id)
|
|
752
755
|
`);
|
|
753
756
|
}
|
|
754
757
|
|
|
@@ -1373,7 +1376,10 @@ async function runPoll(argv, options) {
|
|
|
1373
1376
|
const {keys} = requireKeys(state);
|
|
1374
1377
|
const identity = deriveIdentity(keys);
|
|
1375
1378
|
|
|
1376
|
-
|
|
1379
|
+
// NOTE: By default, rely on the relay's server-side cursor (last_acked_event_id).
|
|
1380
|
+
// Passing a local cursor can silently skip events if the state file is reused
|
|
1381
|
+
// across relays/bots. Use --since-event-id explicitly to override.
|
|
1382
|
+
const since = flags.sinceEventId;
|
|
1377
1383
|
const limit = flags.limit || config.poll_limit;
|
|
1378
1384
|
const types = flags.types || config.poll_types;
|
|
1379
1385
|
|
|
@@ -1413,7 +1419,7 @@ async function runPoll(argv, options) {
|
|
|
1413
1419
|
const addedEvents = appendEvents(state, events);
|
|
1414
1420
|
debugLog('response', {status: result.response.status, events: events.length, added: addedEvents});
|
|
1415
1421
|
|
|
1416
|
-
if (typeof state.last_acked_event_id !== 'number') {
|
|
1422
|
+
if (typeof state.last_acked_event_id !== 'number' || !Number.isFinite(state.last_acked_event_id)) {
|
|
1417
1423
|
state.last_acked_event_id = 0;
|
|
1418
1424
|
}
|
|
1419
1425
|
state.relay_url = config.relay_url;
|
|
@@ -1448,9 +1454,16 @@ async function runPoll(argv, options) {
|
|
|
1448
1454
|
}
|
|
1449
1455
|
debugLog('ack ok', {last_acked_event_id: ackedId});
|
|
1450
1456
|
|
|
1451
|
-
if (typeof ackedId === 'number' && ackedId !== state.last_acked_event_id) {
|
|
1457
|
+
if (typeof ackedId === 'number' && Number.isFinite(ackedId) && ackedId !== state.last_acked_event_id) {
|
|
1452
1458
|
state.last_acked_event_id = ackedId;
|
|
1453
|
-
|
|
1459
|
+
// Write the authoritative server-side cursor as-is (even if it moves "backwards"
|
|
1460
|
+
// compared to a stale local file).
|
|
1461
|
+
saveStateMerged(config.state_path, state, {
|
|
1462
|
+
mergeLastAcked: false,
|
|
1463
|
+
mergeStreamCursors: true,
|
|
1464
|
+
mergeKnownMaps: true,
|
|
1465
|
+
mergeEventLog: true,
|
|
1466
|
+
});
|
|
1454
1467
|
}
|
|
1455
1468
|
}
|
|
1456
1469
|
|
|
@@ -1463,6 +1476,56 @@ async function runPoll(argv, options) {
|
|
|
1463
1476
|
return {events, ackedId, maxEventId};
|
|
1464
1477
|
}
|
|
1465
1478
|
|
|
1479
|
+
async function runPollAck(argv) {
|
|
1480
|
+
const {flags, positionals} = parseArgs(argv);
|
|
1481
|
+
const value = flags.upToEventId ?? flags.upTo ?? flags.eventId ?? flags.id ?? positionals[0];
|
|
1482
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
1483
|
+
throw new Error('Missing up_to_event_id. Provide --up-to-event-id or a positional id.');
|
|
1484
|
+
}
|
|
1485
|
+
const parsed = Number(String(value));
|
|
1486
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1487
|
+
throw new Error(`Invalid up_to_event_id: ${value}`);
|
|
1488
|
+
}
|
|
1489
|
+
const upToEventId = Math.floor(parsed);
|
|
1490
|
+
|
|
1491
|
+
const config = buildConfig();
|
|
1492
|
+
const state = loadState(config.state_path);
|
|
1493
|
+
const {keys} = requireKeys(state);
|
|
1494
|
+
const identity = deriveIdentity(keys);
|
|
1495
|
+
|
|
1496
|
+
const result = await signedRequest({
|
|
1497
|
+
method: 'POST',
|
|
1498
|
+
path: '/v0/poll/ack',
|
|
1499
|
+
body: {up_to_event_id: upToEventId},
|
|
1500
|
+
idempotencyKey: crypto.randomBytes(16).toString('hex'),
|
|
1501
|
+
relayUrl: config.relay_url,
|
|
1502
|
+
keys,
|
|
1503
|
+
identity,
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1506
|
+
if (!result.response.ok) {
|
|
1507
|
+
throw new Error(`Ack failed (${result.response.status}): ${result.text || result.response.statusText}`);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const ackedId = result.data && typeof result.data.last_acked_event_id === 'number'
|
|
1511
|
+
? result.data.last_acked_event_id
|
|
1512
|
+
: upToEventId;
|
|
1513
|
+
|
|
1514
|
+
state.last_acked_event_id = ackedId;
|
|
1515
|
+
state.relay_url = config.relay_url;
|
|
1516
|
+
state.bot_id = identity.botId;
|
|
1517
|
+
state.signing_kid = identity.signingKid;
|
|
1518
|
+
state.encryption_kid = identity.encryptionKid;
|
|
1519
|
+
saveStateMerged(config.state_path, state, {
|
|
1520
|
+
mergeLastAcked: false,
|
|
1521
|
+
mergeStreamCursors: true,
|
|
1522
|
+
mergeKnownMaps: true,
|
|
1523
|
+
mergeEventLog: true,
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
printJson(result.data, !!flags.compact);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1466
1529
|
function parseCsv(value) {
|
|
1467
1530
|
if (value === undefined || value === null) {
|
|
1468
1531
|
return [];
|
|
@@ -2268,6 +2331,10 @@ async function main() {
|
|
|
2268
2331
|
throw new Error('Unknown job command. Use: job create|reissue-request|reissue-charge|payment-sent');
|
|
2269
2332
|
}
|
|
2270
2333
|
case 'poll':
|
|
2334
|
+
if (rest[0] === 'ack') {
|
|
2335
|
+
await runPollAck(rest.slice(1));
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2271
2338
|
await runPoll(rest);
|
|
2272
2339
|
return;
|
|
2273
2340
|
case 'watch':
|