@nelsonlongxiang/dsh-open-a2a-net 0.6.28 → 0.6.30

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.
Files changed (69) hide show
  1. package/README.md +87 -14
  2. package/README_zh.md +83 -7
  3. package/assets/nexusDist/assets/index-C_x3iAth.js +9 -0
  4. package/assets/nexusDist/assets/index-Dz8N80tI.css +1 -0
  5. package/assets/nexusDist/index.html +2 -2
  6. package/cli/a2a.mjs +0 -0
  7. package/cordis.patch.yml +21 -21
  8. package/lib/canvas-store.js +21 -1
  9. package/lib/client.js +64 -64
  10. package/lib/client.js.map +1 -1
  11. package/lib/faces/admin.js +755 -0
  12. package/lib/faces/canvas-notify.js +138 -0
  13. package/lib/faces/wire.js +669 -0
  14. package/lib/flood-guard.js +106 -0
  15. package/lib/index.js +200 -1392
  16. package/lib/recursion-guard.js +78 -0
  17. package/lib/runtime.js +1 -0
  18. package/lib/skill/SKILL.md +35 -9
  19. package/lib/skill/references/cli.md +24 -7
  20. package/lib/skill/references/collaboration.md +355 -0
  21. package/lib/skill/references/discovery.md +5 -2
  22. package/lib/skill/references/pitfalls.md +2 -1
  23. package/lib/skill/references/routing.md +18 -8
  24. package/lib/skill/references/setup.md +152 -0
  25. package/lib/task-ledger.js +40 -4
  26. package/lib/types/canvas-store.d.ts +10 -1
  27. package/lib/types/faces/admin.d.ts +133 -0
  28. package/lib/types/faces/canvas-notify.d.ts +74 -0
  29. package/lib/types/faces/wire.d.ts +171 -0
  30. package/lib/types/flood-guard.d.ts +29 -0
  31. package/lib/types/index.d.ts +11 -3
  32. package/lib/types/recursion-guard.d.ts +56 -0
  33. package/lib/types/runtime.d.ts +35 -0
  34. package/lib/types/task-ledger.d.ts +15 -1
  35. package/nexus-stage/package-lock.json +2262 -2262
  36. package/nexus-stage/src/canvas-ops.ts +175 -175
  37. package/nexus-stage/src/canvas-wire.ts +156 -156
  38. package/nexus-stage/src/edges.ts +72 -72
  39. package/nexus-stage/src/federation-overlay.ts +103 -103
  40. package/nexus-stage/src/federation.ts +240 -240
  41. package/nexus-stage/src/layout-doc.ts +143 -143
  42. package/nexus-stage/src/layout-wire.ts +158 -158
  43. package/nexus-stage/src/main.ts +5 -1
  44. package/nexus-stage/src/planning-card-text.ts +73 -0
  45. package/nexus-stage/src/planning-edges.ts +26 -0
  46. package/nexus-stage/src/planning-fit.ts +45 -0
  47. package/nexus-stage/src/planning-legend.ts +38 -0
  48. package/nexus-stage/src/planning-mirror.ts +31 -0
  49. package/nexus-stage/src/planning-overlap.ts +159 -0
  50. package/nexus-stage/src/planning-relayout.ts +121 -0
  51. package/nexus-stage/src/planning-search.ts +168 -0
  52. package/nexus-stage/src/planning-types.ts +1 -0
  53. package/nexus-stage/src/planning-undo.ts +68 -0
  54. package/nexus-stage/src/planning-view.ts +120 -121
  55. package/nexus-stage/src/planning.css +41 -8
  56. package/nexus-stage/src/seat.ts +35 -35
  57. package/nexus-stage/src/stage-name.ts +13 -13
  58. package/nexus-stage/src/tokens.ts +31 -31
  59. package/nexus-stage/src/viewport.ts +24 -1
  60. package/nexus-stage/src/wb-chat.ts +20 -6
  61. package/nexus-stage/src/wb-ledger.ts +20 -7
  62. package/nexus-stage/src/wb-names.ts +43 -0
  63. package/nexus-stage/src/workbench.css +8 -2
  64. package/nexus-stage/src/workbench.ts +55 -17
  65. package/nexus-stage/src/world.ts +13 -21
  66. package/package.json +216 -214
  67. package/assets/nexusDist/assets/index-1niOBYxp.js +0 -5
  68. package/assets/nexusDist/assets/index-uQf7NwjF.css +0 -1
  69. package/nexus-stage/scripts/__pycache__/mockhost.cpython-312.pyc +0 -0
@@ -0,0 +1,138 @@
1
+ /** Coalesce a burst of store writes (a drag that adds three members) into one ping. */
2
+ export const CANVAS_NOTIFY_DEBOUNCE_MS = 400;
3
+ /** A peer's pings refresh its card at most this often. */
4
+ export const CANVAS_REFRESH_MIN_GAP_MS = 1_000;
5
+ const SEND_TIMEOUT_MS = 5_000;
6
+ /**
7
+ * Re-read one peer's card now. Each pass pre-stales the entry (the literal
8
+ * carries no missAt) so it is a GENUINE refetch: pass 1 may join a flight
9
+ * that started before the peer's change; pass 2 always starts after the
10
+ * first settled — that one is the answer. A miss keeps the last good card
11
+ * inside cachedCardFetch itself (the missAt window); there is nothing to
12
+ * restore here.
13
+ * @param d - the card cache and its single-flight fetch.
14
+ * @param url - the tracked peer URL.
15
+ */
16
+ export async function refreshPeerCard(d, url) {
17
+ for (let pass = 0; pass < 2; pass += 1) {
18
+ const hit = d.cardCache.get(url);
19
+ if (hit !== undefined)
20
+ d.cardCache.set(url, { at: 0, card: hit.card });
21
+ await d.cachedCardFetch(url);
22
+ }
23
+ }
24
+ /**
25
+ * Register the receive face and subscribe the send side to the canvas store.
26
+ * @param webServer - the settled host web server.
27
+ * @param d - the captures listed on {@link CanvasNotifyDeps}.
28
+ */
29
+ export function registerCanvasNotify(webServer, d) {
30
+ const { ctx, logger, peerStore, canvasStore, originClaimHeaders, verifyOriginClaim, callerLinkedToThisHost } = d;
31
+ // ── send ──
32
+ let timer;
33
+ const recipients = () => peerStore.list().filter(url => {
34
+ const card = d.cardCache.get(url)?.card;
35
+ // The card face's own predicate (bare-zone caller, as originClaimHeaders
36
+ // signs it): only a peer that is served the full card ever sees canvasOrg.
37
+ return card !== undefined && callerLinkedToThisHost(card.team, url, card);
38
+ });
39
+ const flush = () => {
40
+ timer = undefined;
41
+ for (const url of recipients()) {
42
+ void globalThis.fetch(url.replace(/\/$/, '') + '/a2a/canvas-changed', {
43
+ method: 'POST',
44
+ headers: { 'Content-Type': 'application/json', ...originClaimHeaders() },
45
+ body: '{}',
46
+ signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
47
+ }).then(async (reply) => {
48
+ const out = await reply.json().catch(() => undefined);
49
+ if (out?.ok !== true)
50
+ logger.debug(`a2a: canvas-changed ping to ${url} not taken: ${String(out?.error ?? `HTTP ${String(reply.status)}`)}`);
51
+ }, (error) => {
52
+ // A down peer re-reads the card on its own TTL once it is back.
53
+ logger.debug(`a2a: canvas-changed ping to ${url} failed: ${error instanceof Error ? error.message : String(error)}`);
54
+ });
55
+ }
56
+ };
57
+ ctx.effect(() => canvasStore.onChange(() => {
58
+ if (timer === undefined)
59
+ timer = setTimeout(flush, CANVAS_NOTIFY_DEBOUNCE_MS);
60
+ }), 'a2a: canvas change notifier');
61
+ ctx.effect(() => () => {
62
+ if (timer !== undefined)
63
+ clearTimeout(timer);
64
+ timer = undefined;
65
+ }, 'a2a: canvas change notifier timer');
66
+ // ── receive ──
67
+ const lastRefresh = new Map();
68
+ const trailing = new Map();
69
+ const refresh = (url) => {
70
+ lastRefresh.set(url, Date.now());
71
+ void refreshPeerCard(d, url);
72
+ };
73
+ let disposed = false;
74
+ ctx.effect(() => () => {
75
+ disposed = true;
76
+ for (const handle of trailing.values())
77
+ clearTimeout(handle);
78
+ trailing.clear();
79
+ }, 'a2a: canvas-changed trailing refreshes');
80
+ ctx.effect(() => webServer.register({
81
+ kind: 'exact',
82
+ path: '/a2a/canvas-changed',
83
+ handler: (req, res) => {
84
+ const send = (payload) => {
85
+ const text = JSON.stringify(payload);
86
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(text) });
87
+ res.end(text);
88
+ };
89
+ // The ping has no content: drain a small body without buffering it,
90
+ // and cut off anything larger.
91
+ let size = 0;
92
+ req.on('data', (chunk) => { size += chunk.length; if (size > 4 * 1024)
93
+ req.destroy(); });
94
+ req.on('end', () => {
95
+ void (async () => {
96
+ if (req.method !== 'POST') {
97
+ send({ ok: false, error: 'POST required' });
98
+ return;
99
+ }
100
+ // Tracked first, from the claimed URL: it must BE a tracked peer URL
101
+ // whose card is already cached, so verifying the claim never fetches
102
+ // anything a caller chose (no stranger host, no path on a peer).
103
+ const claimed = String(req.headers['x-a2a-caller-url'] ?? '').replace(/\/$/, '');
104
+ const tracked = claimed === '' ? [] : peerStore.list().filter(url => url.replace(/\/$/, '') === claimed && d.cardCache.get(url)?.card !== undefined);
105
+ if (tracked.length === 0) {
106
+ send({ ok: false, error: 'caller is not a tracked peer of this host' });
107
+ return;
108
+ }
109
+ const claim = await verifyOriginClaim(req);
110
+ if (!claim.ok) {
111
+ send({ ok: false, error: `untrusted-origin: OriginClaim verification failed (${claim.reason})` });
112
+ return;
113
+ }
114
+ if (disposed) {
115
+ send({ ok: false, error: 'host is shutting down' });
116
+ return;
117
+ }
118
+ // Rate limit without losing an update: a ping inside the gap is
119
+ // deferred to one trailing refresh at the gap's end, never dropped.
120
+ let refreshed = 0;
121
+ let deferred = 0;
122
+ for (const url of tracked) {
123
+ const wait = CANVAS_REFRESH_MIN_GAP_MS - (Date.now() - (lastRefresh.get(url) ?? 0));
124
+ if (wait <= 0) {
125
+ refresh(url);
126
+ refreshed += 1;
127
+ continue;
128
+ }
129
+ deferred += 1;
130
+ if (!trailing.has(url))
131
+ trailing.set(url, setTimeout(() => { trailing.delete(url); refresh(url); }, wait));
132
+ }
133
+ send({ ok: true, refreshed, deferred });
134
+ })();
135
+ });
136
+ },
137
+ }), 'a2a: canvas-changed peer route');
138
+ }