@genkaku/protocol 0.1.11 → 0.1.13

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/dist/dial.d.ts CHANGED
@@ -5,6 +5,15 @@ import type { OpenEnvelope } from './workerSession.ts';
5
5
  /** Direct dials before a worker's address is considered unreachable and the
6
6
  * seed-VM relay path is preferred for subsequent sessions. */
7
7
  export declare const DIRECT_ATTEMPTS_BEFORE_RELAY = 3;
8
+ /** A circuit-relayed connection carries a relay-imposed byte/duration limit, so
9
+ * libp2p marks it "limited" and refuses new streams unless the caller opts in.
10
+ * Every GenKaKu protocol is application data and must opt in; on a direct
11
+ * connection the flag is ignored.
12
+ *
13
+ * The timeout is per call — sharing one AbortSignal would abort every dial made
14
+ * after the module's first five seconds. An unreachable peer must not stall a
15
+ * marketplace listing behind a TCP timeout. */
16
+ export declare const DIAL_TIMEOUT_MS = 5000;
8
17
  export type DialRoute = 'direct' | 'relay';
9
18
  /** Build `/p2p-circuit` addresses for `peerId` from the relays the seed handed
10
19
  * out. The relay hop keeps its own `/p2p/<id>`; ours is appended last. */
@@ -42,6 +51,10 @@ export declare function dialWorkerAny(node: Libp2p, addr: string, opts?: {
42
51
  route: DialRoute;
43
52
  addr: string;
44
53
  }>;
54
+ /** Open (or reuse) a connection to a relay hop. The circuit transport only
55
+ * reserves a slot while a HOP-capable peer is connected, so a seed restart
56
+ * needs this again with the seed's new peer id. */
57
+ export declare function connectRelay(node: Libp2p, relay: string): void;
45
58
  /** Fetch + deserialize a worker's offer board (one dial, whole-board read). */
46
59
  export declare function fetchOffers(node: Libp2p, addr: string): Promise<Offer[]>;
47
60
  /** P4.5 seed discovery: ask a bootstrap seed which peers it currently sees.
@@ -1 +1 @@
1
- {"version":3,"file":"dial.d.ts","sourceRoot":"","sources":["../src/dial.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dial.d.ts","sourceRoot":"","sources":["../src/dial.ts"],"names":[],"mappings":"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"}
package/dist/dial.js CHANGED
@@ -22,8 +22,15 @@ const directFailures = new Map();
22
22
  /** A circuit-relayed connection carries a relay-imposed byte/duration limit, so
23
23
  * libp2p marks it "limited" and refuses new streams unless the caller opts in.
24
24
  * Every GenKaKu protocol is application data and must opt in; on a direct
25
- * connection the flag is ignored. */
26
- const RELAY_STREAM = { runOnLimitedConnection: true };
25
+ * connection the flag is ignored.
26
+ *
27
+ * The timeout is per call — sharing one AbortSignal would abort every dial made
28
+ * after the module's first five seconds. An unreachable peer must not stall a
29
+ * marketplace listing behind a TCP timeout. */
30
+ export const DIAL_TIMEOUT_MS = 5_000;
31
+ function streamOpts(timeoutMs = DIAL_TIMEOUT_MS) {
32
+ return { signal: AbortSignal.timeout(timeoutMs), runOnLimitedConnection: true };
33
+ }
27
34
  /** Build `/p2p-circuit` addresses for `peerId` from the relays the seed handed
28
35
  * out. The relay hop keeps its own `/p2p/<id>`; ours is appended last. */
29
36
  export function circuitAddrsFor(relays, peerId) {
@@ -121,7 +128,7 @@ export async function dialWorker(node, addr) {
121
128
  }
122
129
  return {
123
130
  async open(env) {
124
- openStream = await node.dialProtocol(ma, '/genkaku/open/0', RELAY_STREAM);
131
+ openStream = await node.dialProtocol(ma, '/genkaku/open/0', streamOpts());
125
132
  // errors surface via the read side
126
133
  openSinkDone = openStream.sink(oq.source()).catch(() => { });
127
134
  oq.push(lpEncode(env));
@@ -155,7 +162,7 @@ export async function dialWorker(node, addr) {
155
162
  onError(cb) { errCbs.add(cb); },
156
163
  async sendReceipt(jobId, seq, amount, sig = '', inputHash = ZERO_HASH, outputHash = ZERO_HASH) {
157
164
  if (!receiptStream) {
158
- receiptStream = await node.dialProtocol(ma, '/genkaku/receipt/0', RELAY_STREAM);
165
+ receiptStream = await node.dialProtocol(ma, '/genkaku/receipt/0', streamOpts());
159
166
  receiptSinkDone = receiptStream.sink(rq.source()).catch(() => { });
160
167
  }
161
168
  rq.push(lpEncode({
@@ -183,7 +190,7 @@ export async function dialWorker(node, addr) {
183
190
  export async function helloWorker(node, addr) {
184
191
  const expectedPeer = peerIdOf(addr);
185
192
  const nonce = newNonce();
186
- const stream = await node.dialProtocol(multiaddr(addr), '/genkaku/hello/0', RELAY_STREAM);
193
+ const stream = await node.dialProtocol(multiaddr(addr), '/genkaku/hello/0', streamOpts());
187
194
  let reply;
188
195
  try {
189
196
  stream.sink([lpEncode({ proto: 'hello/0', nonce })]).catch(() => { });
@@ -278,6 +285,14 @@ export async function dialWorkerAny(node, addr, opts = {}) {
278
285
  }
279
286
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
280
287
  }
288
+ /** Open (or reuse) a connection to a relay hop. The circuit transport only
289
+ * reserves a slot while a HOP-capable peer is connected, so a seed restart
290
+ * needs this again with the seed's new peer id. */
291
+ export function connectRelay(node, relay) {
292
+ if (!relay.startsWith('/'))
293
+ return;
294
+ void node.dial(multiaddr(relay)).catch(() => { });
295
+ }
281
296
  /** Fetch + deserialize a worker's offer board (one dial, whole-board read). */
282
297
  export async function fetchOffers(node, addr) {
283
298
  const offers = await fetchOfferBoard(node, addr);
@@ -300,7 +315,7 @@ export async function fetchOffers(node, addr) {
300
315
  });
301
316
  }
302
317
  async function fetchOfferBoard(node, addr) {
303
- const s = await node.dialProtocol(multiaddr(addr), '/genkaku/offers/0', RELAY_STREAM);
318
+ const s = await node.dialProtocol(multiaddr(addr), '/genkaku/offers/0', streamOpts());
304
319
  const reader = new FrameReader();
305
320
  let board = [];
306
321
  for await (const chunk of s.source) {
@@ -327,7 +342,7 @@ export async function fetchSeedPeers(node, addr) {
327
342
  // takes the native path.
328
343
  if (addr.startsWith('http://') || addr.startsWith('https://')) {
329
344
  try {
330
- const res = await fetch(`${addr.replace(/\/$/, '')}/peers`);
345
+ const res = await fetch(`${addr.replace(/\/$/, '')}/peers`, { signal: AbortSignal.timeout(DIAL_TIMEOUT_MS) });
331
346
  if (!res.ok)
332
347
  return [];
333
348
  const body = (await res.json());
@@ -339,7 +354,7 @@ export async function fetchSeedPeers(node, addr) {
339
354
  return [];
340
355
  }
341
356
  }
342
- const s = await node.dialProtocol(multiaddr(addr), '/genkaku/peers/0', RELAY_STREAM);
357
+ const s = await node.dialProtocol(multiaddr(addr), '/genkaku/peers/0', streamOpts());
343
358
  const reader = new FrameReader();
344
359
  for await (const chunk of s.source) {
345
360
  reader.push(chunk);
@@ -358,7 +373,7 @@ export async function fetchSeedPeers(node, addr) {
358
373
  export async function fetchSeedRelays(node, seed) {
359
374
  if (seed.startsWith('http://') || seed.startsWith('https://')) {
360
375
  try {
361
- const res = await fetch(`${seed.replace(/\/$/, '')}/peers`);
376
+ const res = await fetch(`${seed.replace(/\/$/, '')}/peers`, { signal: AbortSignal.timeout(DIAL_TIMEOUT_MS) });
362
377
  if (!res.ok)
363
378
  return [];
364
379
  const body = (await res.json());
@@ -373,7 +388,7 @@ export async function fetchSeedRelays(node, seed) {
373
388
  if (node == null)
374
389
  return [];
375
390
  try {
376
- const s = await node.dialProtocol(multiaddr(seed), '/genkaku/peers/0', RELAY_STREAM);
391
+ const s = await node.dialProtocol(multiaddr(seed), '/genkaku/peers/0', streamOpts());
377
392
  const reader = new FrameReader();
378
393
  for await (const chunk of s.source) {
379
394
  reader.push(chunk);
@@ -401,7 +416,7 @@ export async function fetchSeedRelays(node, seed) {
401
416
  * digest JobEscrow.settle recovers against the session's worker address.
402
417
  */
403
418
  export async function requestSettleCosign(node, addr, args) {
404
- const s = await node.dialProtocol(multiaddr(addr), '/genkaku/settle/0', RELAY_STREAM);
419
+ const s = await node.dialProtocol(multiaddr(addr), '/genkaku/settle/0', streamOpts());
405
420
  const reader = new FrameReader();
406
421
  let reply = null;
407
422
  const sinkDone = s.sink([lpEncode({
@@ -435,20 +450,34 @@ export async function requestSettleCosign(node, addr, args) {
435
450
  export async function announceToSeed(node, seed, addrs) {
436
451
  if (addrs.length === 0)
437
452
  return false;
438
- const s = await node.dialProtocol(multiaddr(seed), '/genkaku/announce/0', RELAY_STREAM);
439
- try {
440
- s.sink([lpEncode({ proto: 'announce/0', addrs })]);
441
- const reader = new FrameReader();
442
- for await (const chunk of s.source) {
443
- reader.push(chunk);
444
- const f = reader.next();
445
- if (f?.proto === 'announced/0')
446
- return f.ok === true;
453
+ // A seed may be configured as a URL (the shipped default): its peer id
454
+ // changes on restart, so the address to dial is whatever it reports right
455
+ // now. `relay` is the seed's own address; `peers` are workers, never dial
456
+ // those for an announcement.
457
+ const targets = seed.startsWith('http://') || seed.startsWith('https://')
458
+ ? await fetchSeedRelays(node, seed)
459
+ : [seed];
460
+ for (const target of targets) {
461
+ try {
462
+ const s = await node.dialProtocol(multiaddr(target), '/genkaku/announce/0', streamOpts());
463
+ try {
464
+ s.sink([lpEncode({ proto: 'announce/0', addrs })]).catch(() => { });
465
+ const reader = new FrameReader();
466
+ for await (const chunk of s.source) {
467
+ reader.push(chunk);
468
+ const f = reader.next();
469
+ if (f?.proto === 'announced/0' && f.ok === true)
470
+ return true;
471
+ }
472
+ }
473
+ finally {
474
+ await s.close().catch(() => { });
475
+ }
476
+ }
477
+ catch {
478
+ // try the next address the URL reported
447
479
  }
448
- return false;
449
- }
450
- finally {
451
- await s.close().catch(() => { });
452
480
  }
481
+ return false;
453
482
  }
454
483
  //# sourceMappingURL=dial.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genkaku/protocol",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "GenKaKu protocol core: on-chain worker directory, USDC escrow billing, libp2p transport, Ollama lifecycle",
5
5
  "license": "MIT",
6
6
  "type": "module",