@weaveprotocol/cli 0.1.0 → 0.1.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/dist/weave.mjs +160 -19
- package/package.json +1 -1
package/dist/weave.mjs
CHANGED
|
@@ -6583,6 +6583,19 @@ function createEmitter() {
|
|
|
6583
6583
|
|
|
6584
6584
|
// ../src/network/signaling.ts
|
|
6585
6585
|
var SIGNAL_KINDS = /* @__PURE__ */ new Set(["offer", "answer", "candidate"]);
|
|
6586
|
+
function iceFrom(payload) {
|
|
6587
|
+
const { servers, expiresAt } = payload ?? {};
|
|
6588
|
+
if (!Array.isArray(servers) || typeof expiresAt !== "number" || !Number.isFinite(expiresAt)) return null;
|
|
6589
|
+
const kept = [];
|
|
6590
|
+
for (const server of servers.slice(0, 4)) {
|
|
6591
|
+
const urls = (Array.isArray(server?.urls) ? server.urls : [server?.urls]).filter(
|
|
6592
|
+
(url) => typeof url === "string" && url.length < 256 && /^turns?:/.test(url)
|
|
6593
|
+
);
|
|
6594
|
+
if (urls.length === 0 || typeof server.username !== "string" || typeof server.credential !== "string") continue;
|
|
6595
|
+
kept.push({ urls, username: server.username, credential: server.credential });
|
|
6596
|
+
}
|
|
6597
|
+
return kept.length > 0 ? { servers: kept, expiresAt } : null;
|
|
6598
|
+
}
|
|
6586
6599
|
function createSignalingClient(url, did) {
|
|
6587
6600
|
let ws = null;
|
|
6588
6601
|
let connected = false;
|
|
@@ -6619,7 +6632,10 @@ function createSignalingClient(url, did) {
|
|
|
6619
6632
|
try {
|
|
6620
6633
|
const msg = JSON.parse(event.data);
|
|
6621
6634
|
if (SIGNAL_KINDS.has(msg.type)) emit("signal", msg);
|
|
6622
|
-
else if (
|
|
6635
|
+
else if (msg.type === "ice") {
|
|
6636
|
+
const offered2 = iceFrom(msg.payload);
|
|
6637
|
+
if (offered2) emit("ice", offered2.servers, offered2.expiresAt);
|
|
6638
|
+
} else if (typeof msg.room !== "string") return;
|
|
6623
6639
|
else if (msg.type === "join") emit("peer-joined", msg.from, msg.room);
|
|
6624
6640
|
else if (msg.type === "leave") emit("peer-left", msg.from, msg.room);
|
|
6625
6641
|
} catch (err2) {
|
|
@@ -6675,6 +6691,7 @@ function createSignalingClient(url, did) {
|
|
|
6675
6691
|
if (rooms.delete(room)) sendMessage({ type: "leave", room });
|
|
6676
6692
|
},
|
|
6677
6693
|
signal: (kind, targetDid, payload) => sendMessage({ type: kind, to: targetDid, payload }),
|
|
6694
|
+
requestIce: () => sendMessage({ type: "ice" }),
|
|
6678
6695
|
on,
|
|
6679
6696
|
off,
|
|
6680
6697
|
isConnected: () => connected
|
|
@@ -6690,6 +6707,7 @@ function createMultiSignalingClient(urls, did) {
|
|
|
6690
6707
|
const { on, off, emit } = createEmitter();
|
|
6691
6708
|
const routes = /* @__PURE__ */ new Map();
|
|
6692
6709
|
const presence = /* @__PURE__ */ new Map();
|
|
6710
|
+
const turn = /* @__PURE__ */ new Map();
|
|
6693
6711
|
const connectedCount = () => clients.filter((client) => client.isConnected()).length;
|
|
6694
6712
|
const remember = (peer, client) => {
|
|
6695
6713
|
(routes.get(peer) ?? routes.set(peer, /* @__PURE__ */ new Set()).get(peer)).add(client);
|
|
@@ -6718,6 +6736,11 @@ function createMultiSignalingClient(urls, did) {
|
|
|
6718
6736
|
remember(message.from, client);
|
|
6719
6737
|
emit("signal", message);
|
|
6720
6738
|
});
|
|
6739
|
+
client.on("ice", (servers, expiresAt) => {
|
|
6740
|
+
turn.set(client, { servers, expiresAt });
|
|
6741
|
+
const offers = [...turn.values()];
|
|
6742
|
+
emit("ice", offers.flatMap((offer) => offer.servers), Math.min(...offers.map((offer) => offer.expiresAt)));
|
|
6743
|
+
});
|
|
6721
6744
|
client.on("connected", () => {
|
|
6722
6745
|
if (connectedCount() === 1) emit("connected");
|
|
6723
6746
|
});
|
|
@@ -6740,6 +6763,7 @@ function createMultiSignalingClient(urls, did) {
|
|
|
6740
6763
|
for (const client of clients) client.disconnect();
|
|
6741
6764
|
routes.clear();
|
|
6742
6765
|
presence.clear();
|
|
6766
|
+
turn.clear();
|
|
6743
6767
|
},
|
|
6744
6768
|
join: (room) => {
|
|
6745
6769
|
for (const client of clients) client.join(room);
|
|
@@ -6751,6 +6775,9 @@ function createMultiSignalingClient(urls, did) {
|
|
|
6751
6775
|
signal: (kind, target, payload) => {
|
|
6752
6776
|
for (const client of routesFor(target)) client.signal(kind, target, payload);
|
|
6753
6777
|
},
|
|
6778
|
+
requestIce: () => {
|
|
6779
|
+
for (const client of clients) if (client.isConnected()) client.requestIce();
|
|
6780
|
+
},
|
|
6754
6781
|
on,
|
|
6755
6782
|
off,
|
|
6756
6783
|
isConnected: () => connectedCount() > 0
|
|
@@ -6806,7 +6833,8 @@ var DEFAULT_ICE_SERVERS = [
|
|
|
6806
6833
|
{ urls: "stun:stun1.l.google.com:19302" }
|
|
6807
6834
|
];
|
|
6808
6835
|
function createRTCTransport(config) {
|
|
6809
|
-
const
|
|
6836
|
+
const configured = config?.iceServers ?? DEFAULT_ICE_SERVERS;
|
|
6837
|
+
const iceServers = () => typeof configured === "function" ? configured() : configured;
|
|
6810
6838
|
const connections = /* @__PURE__ */ new Map();
|
|
6811
6839
|
const { on, off, emit } = createEmitter();
|
|
6812
6840
|
const setupDataChannel = (peerId, channel) => {
|
|
@@ -6834,7 +6862,7 @@ function createRTCTransport(config) {
|
|
|
6834
6862
|
if (connections.has(peerId)) {
|
|
6835
6863
|
close(peerId);
|
|
6836
6864
|
}
|
|
6837
|
-
const connection = new RTCPeerConnection({ iceServers: [...iceServers] });
|
|
6865
|
+
const connection = new RTCPeerConnection({ iceServers: [...iceServers()] });
|
|
6838
6866
|
connections.set(peerId, { connection, channel: null });
|
|
6839
6867
|
connection.onicecandidate = (event) => {
|
|
6840
6868
|
if (event.candidate) onCandidate(event.candidate.toJSON());
|
|
@@ -7137,10 +7165,20 @@ function createWebSocketTransport(config) {
|
|
|
7137
7165
|
}
|
|
7138
7166
|
|
|
7139
7167
|
// ../src/network/mesh.ts
|
|
7168
|
+
var ICE_REFRESH_MS = 10 * 6e4;
|
|
7169
|
+
var ICE_WAIT_MS = 1500;
|
|
7140
7170
|
function createMesh(config) {
|
|
7141
7171
|
const { did } = config;
|
|
7142
|
-
const transport = config.createTransport?.() ?? createRTCTransport({ iceServers: config.iceServers });
|
|
7143
7172
|
const signaling = createMultiSignalingClient(config.relays, did);
|
|
7173
|
+
const configuredIce = config.iceServers ?? DEFAULT_ICE_SERVERS;
|
|
7174
|
+
let relayIce = null;
|
|
7175
|
+
let iceAskedAt = 0;
|
|
7176
|
+
signaling.on("ice", (servers, expiresAt) => {
|
|
7177
|
+
relayIce = { servers, expiresAt };
|
|
7178
|
+
iceAskedAt = 0;
|
|
7179
|
+
});
|
|
7180
|
+
const currentIce = () => relayIce && relayIce.expiresAt > Date.now() ? [...configuredIce, ...relayIce.servers] : configuredIce;
|
|
7181
|
+
const transport = config.createTransport?.() ?? createRTCTransport({ iceServers: currentIce });
|
|
7144
7182
|
const introduce = config.introductions !== false;
|
|
7145
7183
|
const authTimeoutMs = config.authTimeoutMs ?? 1e4;
|
|
7146
7184
|
const rooms = /* @__PURE__ */ new Map();
|
|
@@ -7341,6 +7379,23 @@ function createMesh(config) {
|
|
|
7341
7379
|
return started;
|
|
7342
7380
|
};
|
|
7343
7381
|
return {
|
|
7382
|
+
async iceServers() {
|
|
7383
|
+
const stale = !relayIce || relayIce.expiresAt - Date.now() < ICE_REFRESH_MS;
|
|
7384
|
+
if (signaling.isConnected() && stale && Date.now() - iceAskedAt > ICE_REFRESH_MS) {
|
|
7385
|
+
iceAskedAt = Date.now();
|
|
7386
|
+
await new Promise((resolve) => {
|
|
7387
|
+
const done = () => {
|
|
7388
|
+
clearTimeout(timer);
|
|
7389
|
+
signaling.off("ice", done);
|
|
7390
|
+
resolve();
|
|
7391
|
+
};
|
|
7392
|
+
const timer = setTimeout(done, ICE_WAIT_MS);
|
|
7393
|
+
signaling.on("ice", done);
|
|
7394
|
+
signaling.requestIce();
|
|
7395
|
+
});
|
|
7396
|
+
}
|
|
7397
|
+
return currentIce();
|
|
7398
|
+
},
|
|
7344
7399
|
join(name, auth = null) {
|
|
7345
7400
|
const room = { auth, peers: /* @__PURE__ */ new Map(), handshakes: /* @__PURE__ */ new Map(), events: createEmitter() };
|
|
7346
7401
|
const send = (peer, message) => {
|
|
@@ -8156,6 +8211,13 @@ var writeCapability = (spaceId) => ({
|
|
|
8156
8211
|
can: "expression/write"
|
|
8157
8212
|
});
|
|
8158
8213
|
var noteCid = (encoded) => cidFromBytes(utf8Encode(encoded));
|
|
8214
|
+
var NO_ACCOUNT = Object.freeze({ account: null, agent: false });
|
|
8215
|
+
var WHO_MESSAGE = "who";
|
|
8216
|
+
var LIVE_MESSAGE = "live";
|
|
8217
|
+
var MAX_LIVE_BYTES = 64 * 1024;
|
|
8218
|
+
var MAX_NOTE_LENGTH = 16 * 1024;
|
|
8219
|
+
var LIVE_BURST = 60;
|
|
8220
|
+
var LIVE_PER_SECOND = 20;
|
|
8159
8221
|
var STANDS = { ok: true };
|
|
8160
8222
|
function looksEncrypted(body) {
|
|
8161
8223
|
const envelope = body;
|
|
@@ -8657,11 +8719,45 @@ async function openSpaceRuntime(deps) {
|
|
|
8657
8719
|
}
|
|
8658
8720
|
}
|
|
8659
8721
|
const connectedPeers = () => [...new Set(networks.flatMap((network) => network.getPeers().map((peer) => peer.did)))];
|
|
8722
|
+
const peerAccounts = /* @__PURE__ */ new Map();
|
|
8723
|
+
const knownAccounts = /* @__PURE__ */ new Map();
|
|
8724
|
+
const liveAllowance = /* @__PURE__ */ new Map();
|
|
8725
|
+
async function accountOf(peer, note) {
|
|
8726
|
+
if (typeof note !== "string" || note.length > MAX_NOTE_LENGTH) return NO_ACCOUNT;
|
|
8727
|
+
const chain = await resolveDelegationRoot(note, () => null, provider).catch(() => null);
|
|
8728
|
+
if (!chain?.valid || chain.audience !== peer || !chain.rootDid) return NO_ACCOUNT;
|
|
8729
|
+
return { account: chain.rootDid, agent: isAgentNote(note) };
|
|
8730
|
+
}
|
|
8731
|
+
function withinAllowance(peer) {
|
|
8732
|
+
const now = Date.now();
|
|
8733
|
+
const held = liveAllowance.get(peer) ?? { tokens: LIVE_BURST, at: now };
|
|
8734
|
+
const tokens = Math.min(LIVE_BURST, held.tokens + (now - held.at) / 1e3 * LIVE_PER_SECOND);
|
|
8735
|
+
liveAllowance.set(peer, { tokens: Math.max(0, tokens - 1), at: now });
|
|
8736
|
+
return tokens >= 1;
|
|
8737
|
+
}
|
|
8738
|
+
async function onLive(peer, payload) {
|
|
8739
|
+
if (!withinAllowance(peer)) return;
|
|
8740
|
+
if ((JSON.stringify(payload ?? null)?.length ?? 0) > MAX_LIVE_BYTES) return;
|
|
8741
|
+
const { account, agent } = await peerAccounts.get(peer) ?? NO_ACCOUNT;
|
|
8742
|
+
if (!routes.has(peer)) return;
|
|
8743
|
+
emit({ type: "message", space: space2.id, from: account, peer, agent, message: payload });
|
|
8744
|
+
}
|
|
8660
8745
|
for (const network of networks) {
|
|
8661
8746
|
network.on("message", (message) => {
|
|
8662
8747
|
if (message.type === "sync") void sync.handleMessage(message.from, message.payload);
|
|
8748
|
+
else if (message.type === WHO_MESSAGE) {
|
|
8749
|
+
const note = message.payload?.note;
|
|
8750
|
+
const checked = accountOf(message.from, note);
|
|
8751
|
+
peerAccounts.set(message.from, checked);
|
|
8752
|
+
void checked.then((known) => {
|
|
8753
|
+
if (peerAccounts.get(message.from) !== checked) return;
|
|
8754
|
+
knownAccounts.set(message.from, known);
|
|
8755
|
+
emit({ type: "status", space: space2.id });
|
|
8756
|
+
});
|
|
8757
|
+
} else if (message.type === LIVE_MESSAGE) void onLive(message.from, message.payload);
|
|
8663
8758
|
});
|
|
8664
8759
|
network.on("peer-connected", (info) => {
|
|
8760
|
+
network.send(info.did, { type: WHO_MESSAGE, from: session.did, payload: { note: session.proof() } });
|
|
8665
8761
|
routes.set(info.did, network);
|
|
8666
8762
|
sync.addPeer(info.did);
|
|
8667
8763
|
sync.notifyPeers([info.did]);
|
|
@@ -8671,6 +8767,9 @@ async function openSpaceRuntime(deps) {
|
|
|
8671
8767
|
if (routes.get(info.did) === network) {
|
|
8672
8768
|
routes.delete(info.did);
|
|
8673
8769
|
sync.removePeer(info.did);
|
|
8770
|
+
peerAccounts.delete(info.did);
|
|
8771
|
+
knownAccounts.delete(info.did);
|
|
8772
|
+
liveAllowance.delete(info.did);
|
|
8674
8773
|
}
|
|
8675
8774
|
emit({ type: "status", space: space2.id });
|
|
8676
8775
|
});
|
|
@@ -8681,10 +8780,16 @@ async function openSpaceRuntime(deps) {
|
|
|
8681
8780
|
}
|
|
8682
8781
|
if (networks.length > 0) {
|
|
8683
8782
|
connection = "connecting";
|
|
8684
|
-
void Promise.
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8783
|
+
void Promise.any(networks.map((network) => network.connect())).then(
|
|
8784
|
+
() => {
|
|
8785
|
+
connection = "connected";
|
|
8786
|
+
emit({ type: "status", space: space2.id });
|
|
8787
|
+
},
|
|
8788
|
+
() => {
|
|
8789
|
+
connection = "error";
|
|
8790
|
+
emit({ type: "status", space: space2.id });
|
|
8791
|
+
}
|
|
8792
|
+
);
|
|
8688
8793
|
sync.start();
|
|
8689
8794
|
}
|
|
8690
8795
|
let watchTimer = null;
|
|
@@ -9031,10 +9136,19 @@ async function openSpaceRuntime(deps) {
|
|
|
9031
9136
|
space: space2.id,
|
|
9032
9137
|
connection,
|
|
9033
9138
|
peers: connectedPeers(),
|
|
9139
|
+
accounts: Object.fromEntries([...knownAccounts].flatMap(([peer, known]) => known.account && routes.has(peer) ? [[peer, known.account]] : [])),
|
|
9034
9140
|
root: await storage.getRootCid(),
|
|
9035
9141
|
rejected
|
|
9036
9142
|
};
|
|
9037
9143
|
},
|
|
9144
|
+
async send(message, to) {
|
|
9145
|
+
const encoded = JSON.stringify(message ?? null);
|
|
9146
|
+
if (encoded.length > MAX_LIVE_BYTES) throw new Error("A live message can be at most 64 KB");
|
|
9147
|
+
for (const peer of connectedPeers()) {
|
|
9148
|
+
if (to && to !== peer && (await peerAccounts.get(peer))?.account !== to) continue;
|
|
9149
|
+
routes.get(peer)?.send(peer, { type: LIVE_MESSAGE, from: session.did, payload: message ?? null });
|
|
9150
|
+
}
|
|
9151
|
+
},
|
|
9038
9152
|
async close() {
|
|
9039
9153
|
if (watchTimer) clearInterval(watchTimer);
|
|
9040
9154
|
if (announceTimer) clearTimeout(announceTimer);
|
|
@@ -9114,6 +9228,7 @@ async function createNode(config) {
|
|
|
9114
9228
|
const registryStore = await config.stores("registry", { seal: true });
|
|
9115
9229
|
const registry = createSpaceManager(registryStore, provider);
|
|
9116
9230
|
const runtimes = /* @__PURE__ */ new Map();
|
|
9231
|
+
const holds2 = /* @__PURE__ */ new Map();
|
|
9117
9232
|
const account = config.accountKey ? await deriveAccountRegistry(config.accountKey, config.signer.did, provider) : null;
|
|
9118
9233
|
const accountSpaceId = account?.space.id ?? null;
|
|
9119
9234
|
async function findRecord(spaceId) {
|
|
@@ -9224,6 +9339,7 @@ async function createNode(config) {
|
|
|
9224
9339
|
}
|
|
9225
9340
|
}
|
|
9226
9341
|
async function closeRuntime(spaceId) {
|
|
9342
|
+
holds2.delete(spaceId);
|
|
9227
9343
|
const open2 = runtimes.get(spaceId);
|
|
9228
9344
|
if (!open2) return;
|
|
9229
9345
|
runtimes.delete(spaceId);
|
|
@@ -9406,10 +9522,28 @@ async function createNode(config) {
|
|
|
9406
9522
|
await registry.remove(spaceId);
|
|
9407
9523
|
emit({ type: "spaces" });
|
|
9408
9524
|
},
|
|
9409
|
-
async
|
|
9410
|
-
|
|
9525
|
+
async hold(spaceId) {
|
|
9526
|
+
const held = holds2.get(spaceId) ?? holds2.set(spaceId, { count: 0 }).get(spaceId);
|
|
9527
|
+
held.count += 1;
|
|
9528
|
+
let released = false;
|
|
9529
|
+
const release = async () => {
|
|
9530
|
+
if (released) return;
|
|
9531
|
+
released = true;
|
|
9532
|
+
if (holds2.get(spaceId) !== held) return;
|
|
9533
|
+
held.count -= 1;
|
|
9534
|
+
if (held.count === 0) await closeRuntime(spaceId);
|
|
9535
|
+
};
|
|
9536
|
+
try {
|
|
9537
|
+
await runtime(spaceId);
|
|
9538
|
+
} catch (error) {
|
|
9539
|
+
await release();
|
|
9540
|
+
throw error;
|
|
9541
|
+
}
|
|
9542
|
+
return release;
|
|
9543
|
+
},
|
|
9544
|
+
async send(spaceId, message, to) {
|
|
9545
|
+
await (await runtime(spaceId)).send(message, to);
|
|
9411
9546
|
},
|
|
9412
|
-
close: closeRuntime,
|
|
9413
9547
|
async status(spaceId) {
|
|
9414
9548
|
const status = await (await runtime(spaceId)).status();
|
|
9415
9549
|
if (!accountSpaceId) return { ...status, own: [], carriers: [] };
|
|
@@ -9618,9 +9752,11 @@ async function createNode(config) {
|
|
|
9618
9752
|
closeInvite: person("close invites"),
|
|
9619
9753
|
revoke: person("revoke notes"),
|
|
9620
9754
|
access: async (spaceId) => (inside(spaceId), spaces.access(spaceId)),
|
|
9621
|
-
|
|
9755
|
+
hold: async (spaceId) => (inside(spaceId), spaces.hold(spaceId)),
|
|
9622
9756
|
status: async (spaceId) => (inside(spaceId), spaces.status(spaceId)),
|
|
9623
9757
|
profiles: async (spaceId) => (inside(spaceId), spaces.profiles(spaceId)),
|
|
9758
|
+
// It would arrive as the person: a live message carries no note of its own to say "via agent".
|
|
9759
|
+
send: person("send live messages"),
|
|
9624
9760
|
authenticator: async () => null
|
|
9625
9761
|
});
|
|
9626
9762
|
const agentRecords = Object.freeze({
|
|
@@ -9670,6 +9806,7 @@ async function createNode(config) {
|
|
|
9670
9806
|
account: Object.freeze({ profile: accountApi.profile, setName: person("rename the account"), revoke: person("revoke notes") }),
|
|
9671
9807
|
carriers: Object.freeze({ list: carriers.list, add: person("add a carrier"), remove: person("remove a carrier") }),
|
|
9672
9808
|
delegation: () => note,
|
|
9809
|
+
iceServers: node.iceServers,
|
|
9673
9810
|
delegate: person("pass its access on"),
|
|
9674
9811
|
asAgent: person("start another agent"),
|
|
9675
9812
|
subscribe: (listener) => node.subscribe((event) => {
|
|
@@ -9690,6 +9827,7 @@ async function createNode(config) {
|
|
|
9690
9827
|
carriers,
|
|
9691
9828
|
asAgent,
|
|
9692
9829
|
delegation: () => current,
|
|
9830
|
+
iceServers: async () => mesh ? mesh.iceServers() : config.network?.iceServers ?? DEFAULT_ICE_SERVERS,
|
|
9693
9831
|
async delegate(params) {
|
|
9694
9832
|
const token = await delegateCapabilities(
|
|
9695
9833
|
{
|
|
@@ -10700,6 +10838,7 @@ async function serve(options) {
|
|
|
10700
10838
|
res.writeHead(426, { "content-type": "text/plain" });
|
|
10701
10839
|
res.end("This endpoint speaks WebSocket only.\n");
|
|
10702
10840
|
});
|
|
10841
|
+
const served = /* @__PURE__ */ new Set();
|
|
10703
10842
|
const onPeer = async (socket, url) => {
|
|
10704
10843
|
const spaceId = url.searchParams.get("space");
|
|
10705
10844
|
if (!spaceId) {
|
|
@@ -10721,7 +10860,10 @@ async function serve(options) {
|
|
|
10721
10860
|
socket.close(4003, "not a reader of this space");
|
|
10722
10861
|
return;
|
|
10723
10862
|
}
|
|
10724
|
-
|
|
10863
|
+
if (!served.has(spaceId)) {
|
|
10864
|
+
await node.spaces.hold(spaceId);
|
|
10865
|
+
served.add(spaceId);
|
|
10866
|
+
}
|
|
10725
10867
|
const sig = await authenticator.welcome(node.sessionDid, hello.nonce);
|
|
10726
10868
|
socket.send(JSON.stringify({ type: "welcome", did: node.sessionDid, sig }));
|
|
10727
10869
|
inbound.accept(spaceId, socket, hello.did);
|
|
@@ -10771,18 +10913,17 @@ async function startDaemon(options) {
|
|
|
10771
10913
|
...options.nodes?.length ? { nodes: options.nodes } : {}
|
|
10772
10914
|
}
|
|
10773
10915
|
});
|
|
10774
|
-
const open2 = /* @__PURE__ */ new
|
|
10916
|
+
const open2 = /* @__PURE__ */ new Map();
|
|
10775
10917
|
const rescan = async () => {
|
|
10776
10918
|
const held = new Set((await node.spaces.list()).map((space2) => space2.id));
|
|
10777
10919
|
for (const id of held) {
|
|
10778
10920
|
if (open2.has(id)) continue;
|
|
10779
|
-
await node.spaces.
|
|
10780
|
-
open2.add(id);
|
|
10921
|
+
open2.set(id, await node.spaces.hold(id));
|
|
10781
10922
|
log(`serving space ${id}`);
|
|
10782
10923
|
}
|
|
10783
|
-
for (const id of [...open2]) {
|
|
10924
|
+
for (const [id, release] of [...open2]) {
|
|
10784
10925
|
if (held.has(id)) continue;
|
|
10785
|
-
await
|
|
10926
|
+
await release();
|
|
10786
10927
|
open2.delete(id);
|
|
10787
10928
|
log(`stopped serving space ${id}`);
|
|
10788
10929
|
}
|
|
@@ -11145,7 +11286,7 @@ async function startAgentNode(home, options = {}) {
|
|
|
11145
11286
|
for (const space2 of grant.spaces) if (!held.has(space2.id)) await base.spaces.join(space2.invite).catch(() => {
|
|
11146
11287
|
});
|
|
11147
11288
|
const node = await base.asAgent({ keys: key2.keys, note: grant.token });
|
|
11148
|
-
for (const space2 of await node.spaces.list()) void node.spaces.
|
|
11289
|
+
for (const space2 of await node.spaces.list()) void node.spaces.hold(space2.id).catch(() => {
|
|
11149
11290
|
});
|
|
11150
11291
|
return {
|
|
11151
11292
|
node,
|