tokmon 0.23.5 → 0.25.0
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 +25 -3
- package/dist/{bootstrap-ink-WA3ENW2M.js → bootstrap-ink-O55MPGXJ.js} +52 -314
- package/dist/chunk-45YYP2FJ.js +299 -0
- package/dist/{daemon-handle-Y2N2NA6D.js → chunk-AMD4PXDG.js} +2 -2
- package/dist/{chunk-5CWOJMAH.js → chunk-E2YXYU73.js} +2 -0
- package/dist/{chunk-IELQ5EY3.js → chunk-INBZLHJQ.js} +306 -549
- package/dist/{chunk-MJWFCDBB.js → chunk-IOIAXKW2.js} +38 -16
- package/dist/{chunk-3TJVFKXV.js → chunk-QM5E5RJZ.js} +1 -1
- package/dist/chunk-QUGH2XA6.js +275 -0
- package/dist/cli-command-YQ5T4R73.js +526 -0
- package/dist/cli.js +30 -5
- package/dist/{config-QC5QSP3G.js → config-2PXUENQL.js} +1 -1
- package/dist/{daemon-PVYTXJMS.js → daemon-QBOXJFRA.js} +7 -6
- package/dist/daemon-handle-2GVZT55B.js +10 -0
- package/dist/server-23ERUOJ7.js +11 -0
- package/dist/web/assets/{Area-DCES17aZ.js → Area-DbVPPS7M.js} +1 -1
- package/dist/web/assets/{analytics-DDW6zcnj.js → analytics-DS5p9Duq.js} +2 -2
- package/dist/web/assets/{breakdown-D5LPPtqh.js → breakdown-zYF0TXN5.js} +1 -1
- package/dist/web/assets/{chart-bHY7MQV0.js → chart-BZHliIXk.js} +1 -1
- package/dist/web/assets/{explore-DdWB-Pu7.js → explore-DYv8bb2r.js} +1 -1
- package/dist/web/assets/{index-C9jxb2o3.js → index-BO8m4dq1.js} +24 -24
- package/dist/web/assets/index-NnKaHxPO.css +1 -0
- package/dist/web/assets/{models-o1wJnG6w.js → models-P3OwakgO.js} +2 -2
- package/dist/web/assets/overview-5WswYjWK.js +2 -0
- package/dist/web/assets/{panel-DBnK1oy0.js → panel-CpwRWsNg.js} +1 -1
- package/dist/web/assets/{primitives-CBmteetD.js → primitives-Do0zWLZ4.js} +1 -1
- package/dist/web/assets/settings-sheet-uD6-A3tV.js +1 -0
- package/dist/web/assets/{share-sheet-DUee3FlK.js → share-sheet-CimegH2K.js} +2 -2
- package/dist/web/assets/{timeline-BxHwOJA_.js → timeline-Ch94Yv4z.js} +1 -1
- package/dist/web/assets/{use-dialog-trap-30EqxYF-.js → use-dialog-trap-LxEFLutK.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/server-XUCQWX6N.js +0 -10
- package/dist/web/assets/index-m0KfWnN2.css +0 -1
- package/dist/web/assets/overview-wq2uAEk4.js +0 -2
- package/dist/web/assets/settings-sheet-CZ6GEPl3.js +0 -1
|
@@ -6,6 +6,12 @@ import {
|
|
|
6
6
|
sendJson,
|
|
7
7
|
serveStatic
|
|
8
8
|
} from "./chunk-SMPY52EV.js";
|
|
9
|
+
import {
|
|
10
|
+
buildAccounts,
|
|
11
|
+
colorHex,
|
|
12
|
+
fetchPeak,
|
|
13
|
+
namedHex
|
|
14
|
+
} from "./chunk-45YYP2FJ.js";
|
|
9
15
|
import {
|
|
10
16
|
PROVIDERS,
|
|
11
17
|
TOKMON_CAPABILITIES,
|
|
@@ -13,14 +19,10 @@ import {
|
|
|
13
19
|
TOKMON_WS_METHODS,
|
|
14
20
|
TOKMON_WS_PATH,
|
|
15
21
|
TokmonRpcGroup,
|
|
16
|
-
buildAccounts,
|
|
17
|
-
colorHex,
|
|
18
22
|
detectProviders,
|
|
19
|
-
fetchPeak,
|
|
20
|
-
namedHex,
|
|
21
23
|
resolveTimezone,
|
|
22
24
|
withTimeout
|
|
23
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-INBZLHJQ.js";
|
|
24
26
|
import {
|
|
25
27
|
cacheDir,
|
|
26
28
|
expandHome,
|
|
@@ -28,7 +30,7 @@ import {
|
|
|
28
30
|
normalizeConfig,
|
|
29
31
|
saveConfig,
|
|
30
32
|
snapshotCacheFile
|
|
31
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-E2YXYU73.js";
|
|
32
34
|
|
|
33
35
|
// src/web/server.ts
|
|
34
36
|
import { createServer } from "http";
|
|
@@ -187,7 +189,7 @@ function deferred() {
|
|
|
187
189
|
});
|
|
188
190
|
return { promise, resolve, reject };
|
|
189
191
|
}
|
|
190
|
-
function createRefreshQueue(perform, skipAutomatic = () => false) {
|
|
192
|
+
function createRefreshQueue(perform, skipAutomatic = () => false, options = {}) {
|
|
191
193
|
let activePass = null;
|
|
192
194
|
let activeResult = null;
|
|
193
195
|
let queued = null;
|
|
@@ -224,6 +226,7 @@ function createRefreshQueue(perform, skipAutomatic = () => false) {
|
|
|
224
226
|
if (stopped || !force && skipAutomatic()) return Promise.resolve();
|
|
225
227
|
if (!activePass) return launch();
|
|
226
228
|
if (!force) return activeResult.promise;
|
|
229
|
+
if (options.forceWhileActive === "join") return activeResult.promise;
|
|
227
230
|
if (!queued) queued = deferred();
|
|
228
231
|
return queued.promise;
|
|
229
232
|
};
|
|
@@ -250,10 +253,21 @@ var IDLE_PAUSE_MS = 6e4;
|
|
|
250
253
|
var SNAPSHOT_CACHE_THROTTLE_MS = 2e4;
|
|
251
254
|
var REVEAL_THROTTLE_MS = 500;
|
|
252
255
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
253
|
-
var
|
|
254
|
-
function billingNeedsCatchUp(accounts, updatedAt, now = Date.now(), maxAgeMs =
|
|
256
|
+
var DEFAULT_BILLING_STALE_MS = 5 * 6e4;
|
|
257
|
+
function billingNeedsCatchUp(accounts, updatedAt, now = Date.now(), maxAgeMs = DEFAULT_BILLING_STALE_MS) {
|
|
255
258
|
return accounts.some(({ account }) => now - (updatedAt.get(account.id) ?? 0) >= maxAgeMs);
|
|
256
259
|
}
|
|
260
|
+
async function forEachProviderSequentially(values, task) {
|
|
261
|
+
const groups = /* @__PURE__ */ new Map();
|
|
262
|
+
for (const value of values) {
|
|
263
|
+
const group = groups.get(value.account.providerId) ?? [];
|
|
264
|
+
group.push(value);
|
|
265
|
+
groups.set(value.account.providerId, group);
|
|
266
|
+
}
|
|
267
|
+
await Promise.all([...groups.values()].map(async (group) => {
|
|
268
|
+
for (const value of group) await task(value);
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
257
271
|
function runInBackground(task) {
|
|
258
272
|
void task.catch(() => {
|
|
259
273
|
});
|
|
@@ -403,7 +417,14 @@ function createDataEngine(opts) {
|
|
|
403
417
|
}
|
|
404
418
|
reveal();
|
|
405
419
|
};
|
|
406
|
-
if (opts2.concurrent) {
|
|
420
|
+
if (opts2.concurrent === "provider") {
|
|
421
|
+
await forEachProviderSequentially(opts2.accounts(), async (r) => {
|
|
422
|
+
const result = await fetchOne(r);
|
|
423
|
+
if (stopped || epoch !== configEpoch) return;
|
|
424
|
+
applyResult(result);
|
|
425
|
+
});
|
|
426
|
+
if (stopped || epoch !== configEpoch) return;
|
|
427
|
+
} else if (opts2.concurrent) {
|
|
407
428
|
await Promise.all(opts2.accounts().map(async (r) => {
|
|
408
429
|
const result = await fetchOne(r);
|
|
409
430
|
if (stopped || epoch !== configEpoch) return;
|
|
@@ -421,7 +442,8 @@ function createDataEngine(opts) {
|
|
|
421
442
|
rebuild();
|
|
422
443
|
throwIfRefreshFailures(opts2.scope, failures);
|
|
423
444
|
},
|
|
424
|
-
idle
|
|
445
|
+
idle,
|
|
446
|
+
{ forceWhileActive: opts2.forceWhileActive }
|
|
425
447
|
);
|
|
426
448
|
const refreshSummary = makeRefreshLoop({
|
|
427
449
|
scope: "summary",
|
|
@@ -452,8 +474,10 @@ function createDataEngine(opts) {
|
|
|
452
474
|
},
|
|
453
475
|
state: billingState,
|
|
454
476
|
updatedAt: billingUpdatedAt,
|
|
455
|
-
concurrent:
|
|
477
|
+
concurrent: "provider",
|
|
478
|
+
forceWhileActive: "join"
|
|
456
479
|
});
|
|
480
|
+
const refreshBillingIfStale = () => billingNeedsCatchUp(billingAccounts, billingUpdatedAt, Date.now(), billingIntervalMs) ? refreshBilling.run(true) : Promise.resolve();
|
|
457
481
|
const refreshPeak = createRefreshQueue(
|
|
458
482
|
async () => {
|
|
459
483
|
if (stopped || !hasClaude) return;
|
|
@@ -547,7 +571,7 @@ function createDataEngine(opts) {
|
|
|
547
571
|
rebuild();
|
|
548
572
|
runInBackground(refreshSummary.run(true));
|
|
549
573
|
runInBackground(refreshTable.run(true));
|
|
550
|
-
runInBackground(
|
|
574
|
+
runInBackground(refreshBillingIfStale());
|
|
551
575
|
if (hasClaude) runInBackground(refreshPeak.run(true));
|
|
552
576
|
startTimers();
|
|
553
577
|
},
|
|
@@ -574,9 +598,7 @@ function createDataEngine(opts) {
|
|
|
574
598
|
runInBackground(refreshSummary.run(true));
|
|
575
599
|
runInBackground(refreshTable.run(true));
|
|
576
600
|
}
|
|
577
|
-
|
|
578
|
-
runInBackground(refreshBilling.run());
|
|
579
|
-
}
|
|
601
|
+
runInBackground(refreshBillingIfStale());
|
|
580
602
|
return () => {
|
|
581
603
|
snapshotSubscribers.delete(onSnapshot);
|
|
582
604
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
cacheDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-E2YXYU73.js";
|
|
5
5
|
|
|
6
6
|
// src/web/lockfile.ts
|
|
7
7
|
import { closeSync, fchmodSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
TOKMON_WS_METHODS,
|
|
4
|
+
TOKMON_WS_PATH,
|
|
5
|
+
TokmonRpcGroup
|
|
6
|
+
} from "./chunk-INBZLHJQ.js";
|
|
7
|
+
|
|
8
|
+
// src/client/daemon-rpc-client.ts
|
|
9
|
+
import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Schedule, Stream } from "effect";
|
|
10
|
+
import { RpcClient, RpcSerialization } from "effect/unstable/rpc";
|
|
11
|
+
import * as Socket from "effect/unstable/socket/Socket";
|
|
12
|
+
var TokmonRpcClient = class extends Context.Service()(
|
|
13
|
+
"tokmon/client/DaemonRpcClient/TokmonRpcClient"
|
|
14
|
+
) {
|
|
15
|
+
};
|
|
16
|
+
var dynamicImport = new Function("specifier", "return import(specifier)");
|
|
17
|
+
function toWsUrl(baseUrl) {
|
|
18
|
+
const base = typeof window === "undefined" ? void 0 : window.location.origin;
|
|
19
|
+
const url = new URL(baseUrl, base);
|
|
20
|
+
if (url.protocol === "http:") url.protocol = "ws:";
|
|
21
|
+
else if (url.protocol === "https:") url.protocol = "wss:";
|
|
22
|
+
if (url.protocol !== "ws:" && url.protocol !== "wss:") {
|
|
23
|
+
throw new Error(`unsupported daemon RPC protocol: ${url.protocol}`);
|
|
24
|
+
}
|
|
25
|
+
url.hash = "";
|
|
26
|
+
url.searchParams.delete("tokmonToken");
|
|
27
|
+
url.searchParams.delete("wsToken");
|
|
28
|
+
url.pathname = TOKMON_WS_PATH;
|
|
29
|
+
return url.toString();
|
|
30
|
+
}
|
|
31
|
+
function shouldUseNodeTransport(transport) {
|
|
32
|
+
if (transport === "node") return true;
|
|
33
|
+
if (transport === "browser") return false;
|
|
34
|
+
return typeof window === "undefined";
|
|
35
|
+
}
|
|
36
|
+
async function socketLayerFor(url, transport) {
|
|
37
|
+
if (shouldUseNodeTransport(transport)) {
|
|
38
|
+
const NodeSocket = await dynamicImport("@effect/platform-node/NodeSocket");
|
|
39
|
+
return NodeSocket.layerWebSocket(url);
|
|
40
|
+
}
|
|
41
|
+
return Socket.layerWebSocket(url).pipe(
|
|
42
|
+
Layer.provide(Socket.layerWebSocketConstructorGlobal)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function retryPolicy(options) {
|
|
46
|
+
const baseDelay = options.reconnectBaseDelayMs ?? 250;
|
|
47
|
+
const policy = Schedule.exponential(Duration.millis(baseDelay), 1.5).pipe(
|
|
48
|
+
Schedule.either(Schedule.spaced(Duration.millis(2500)))
|
|
49
|
+
);
|
|
50
|
+
return typeof options.reconnectAttempts === "number" ? policy.pipe(Schedule.both(Schedule.recurs(options.reconnectAttempts))) : policy;
|
|
51
|
+
}
|
|
52
|
+
function createDaemonRpcClient(baseUrl, options = {}) {
|
|
53
|
+
const url = toWsUrl(baseUrl);
|
|
54
|
+
const fibers = /* @__PURE__ */ new Set();
|
|
55
|
+
let session = null;
|
|
56
|
+
let sessionPromise = null;
|
|
57
|
+
let closed = false;
|
|
58
|
+
const setConn = (state, error) => {
|
|
59
|
+
options.onConn?.(state, error);
|
|
60
|
+
};
|
|
61
|
+
const resetSession = (active) => {
|
|
62
|
+
if (active !== void 0 && active !== null && active !== session) {
|
|
63
|
+
void active.runtime.dispose().catch(() => {
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const dead = active ?? session;
|
|
68
|
+
session = null;
|
|
69
|
+
sessionPromise = null;
|
|
70
|
+
if (dead) void dead.runtime.dispose().catch(() => {
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
const makeProtocolLayer = async () => {
|
|
74
|
+
const socketLayer = await socketLayerFor(url, options.transport);
|
|
75
|
+
const connectionHooksLayer = Layer.succeed(
|
|
76
|
+
RpcClient.ConnectionHooks,
|
|
77
|
+
RpcClient.ConnectionHooks.of({
|
|
78
|
+
onConnect: Effect.sync(() => {
|
|
79
|
+
setConn("live");
|
|
80
|
+
}),
|
|
81
|
+
onDisconnect: Effect.sync(() => {
|
|
82
|
+
if (!closed) {
|
|
83
|
+
setConn("reconnecting");
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
return Layer.effect(
|
|
89
|
+
RpcClient.Protocol,
|
|
90
|
+
RpcClient.makeProtocolSocket({
|
|
91
|
+
retryPolicy: retryPolicy(options),
|
|
92
|
+
retryTransientErrors: true
|
|
93
|
+
})
|
|
94
|
+
).pipe(
|
|
95
|
+
Layer.provide(Layer.mergeAll(socketLayer, RpcSerialization.layerJson, connectionHooksLayer))
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
const ensureSession = async () => {
|
|
99
|
+
if (closed) throw new Error("daemon RPC client is closed");
|
|
100
|
+
if (session) return session;
|
|
101
|
+
if (sessionPromise) return sessionPromise;
|
|
102
|
+
setConn("connecting");
|
|
103
|
+
sessionPromise = (async () => {
|
|
104
|
+
let runtime;
|
|
105
|
+
try {
|
|
106
|
+
const protocolLayer = await makeProtocolLayer();
|
|
107
|
+
const clientLayer = Layer.effect(
|
|
108
|
+
TokmonRpcClient,
|
|
109
|
+
RpcClient.make(TokmonRpcGroup)
|
|
110
|
+
).pipe(
|
|
111
|
+
Layer.provide(protocolLayer)
|
|
112
|
+
);
|
|
113
|
+
runtime = ManagedRuntime.make(clientLayer);
|
|
114
|
+
await runtime.runPromise(TokmonRpcClient.asEffect());
|
|
115
|
+
if (closed) {
|
|
116
|
+
await runtime.dispose();
|
|
117
|
+
throw new Error("daemon RPC client is closed");
|
|
118
|
+
}
|
|
119
|
+
session = { runtime };
|
|
120
|
+
return session;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
sessionPromise = null;
|
|
123
|
+
await runtime?.dispose().catch(() => {
|
|
124
|
+
});
|
|
125
|
+
if (!closed) setConn("error", error);
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
return sessionPromise;
|
|
130
|
+
};
|
|
131
|
+
const run = async (effectFor) => {
|
|
132
|
+
const active = await ensureSession();
|
|
133
|
+
try {
|
|
134
|
+
return await active.runtime.runPromise(
|
|
135
|
+
TokmonRpcClient.use((client) => effectFor(client))
|
|
136
|
+
);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (!closed) {
|
|
139
|
+
resetSession(active);
|
|
140
|
+
setConn("error", error);
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const subscribe = (streamFor, onValue, staleAfterFor) => {
|
|
146
|
+
if (closed) return () => {
|
|
147
|
+
};
|
|
148
|
+
let fiber = null;
|
|
149
|
+
let unsubscribed = false;
|
|
150
|
+
let retryTimer = null;
|
|
151
|
+
let watchdogTimer = null;
|
|
152
|
+
let lastValueAt = 0;
|
|
153
|
+
let staleAfterMs = Number.POSITIVE_INFINITY;
|
|
154
|
+
let watchdogRestart = false;
|
|
155
|
+
const stopFiber = () => {
|
|
156
|
+
if (!fiber) return;
|
|
157
|
+
const current = fiber;
|
|
158
|
+
fiber = null;
|
|
159
|
+
fibers.delete(current);
|
|
160
|
+
void (session?.runtime.runPromise(Fiber.interrupt(current)) ?? Effect.runPromise(Fiber.interrupt(current))).catch(() => {
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
const scheduleRetry = () => {
|
|
164
|
+
if (closed || unsubscribed || retryTimer) return;
|
|
165
|
+
retryTimer = setTimeout(() => {
|
|
166
|
+
retryTimer = null;
|
|
167
|
+
start();
|
|
168
|
+
}, options.reconnectBaseDelayMs ?? 250);
|
|
169
|
+
retryTimer.unref?.();
|
|
170
|
+
};
|
|
171
|
+
if (staleAfterFor) {
|
|
172
|
+
const checkEveryMs = Math.min(5e3, Math.max(10, (options.snapshotStaleFloorMs ?? 9e4) / 2));
|
|
173
|
+
watchdogTimer = setInterval(() => {
|
|
174
|
+
if (!fiber || lastValueAt === 0 || Date.now() - lastValueAt <= staleAfterMs) return;
|
|
175
|
+
watchdogRestart = true;
|
|
176
|
+
lastValueAt = Date.now();
|
|
177
|
+
setConn("reconnecting");
|
|
178
|
+
stopFiber();
|
|
179
|
+
}, checkEveryMs);
|
|
180
|
+
watchdogTimer.unref?.();
|
|
181
|
+
}
|
|
182
|
+
const start = () => {
|
|
183
|
+
void (async () => {
|
|
184
|
+
try {
|
|
185
|
+
const active = await ensureSession();
|
|
186
|
+
if (closed || unsubscribed) return;
|
|
187
|
+
lastValueAt = Date.now();
|
|
188
|
+
staleAfterMs = options.snapshotStaleFloorMs ?? 9e4;
|
|
189
|
+
const currentFiber = active.runtime.runFork(
|
|
190
|
+
TokmonRpcClient.use(
|
|
191
|
+
(client) => streamFor(client).pipe(
|
|
192
|
+
Stream.runForEach(
|
|
193
|
+
(value) => Effect.sync(() => {
|
|
194
|
+
lastValueAt = Date.now();
|
|
195
|
+
if (staleAfterFor) staleAfterMs = staleAfterFor(value);
|
|
196
|
+
try {
|
|
197
|
+
onValue(value);
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
);
|
|
205
|
+
fiber = currentFiber;
|
|
206
|
+
fibers.add(currentFiber);
|
|
207
|
+
currentFiber.addObserver((exit) => {
|
|
208
|
+
fibers.delete(currentFiber);
|
|
209
|
+
if (fiber === currentFiber) fiber = null;
|
|
210
|
+
if (closed || unsubscribed) return;
|
|
211
|
+
const stale = watchdogRestart;
|
|
212
|
+
watchdogRestart = false;
|
|
213
|
+
resetSession(active);
|
|
214
|
+
if (stale || Exit.isSuccess(exit)) setConn("reconnecting");
|
|
215
|
+
else setConn("error", Cause.squash(exit.cause));
|
|
216
|
+
scheduleRetry();
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (!closed && !unsubscribed) {
|
|
220
|
+
resetSession();
|
|
221
|
+
setConn("error", error);
|
|
222
|
+
scheduleRetry();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
226
|
+
};
|
|
227
|
+
start();
|
|
228
|
+
return () => {
|
|
229
|
+
unsubscribed = true;
|
|
230
|
+
if (retryTimer) {
|
|
231
|
+
clearTimeout(retryTimer);
|
|
232
|
+
retryTimer = null;
|
|
233
|
+
}
|
|
234
|
+
if (watchdogTimer) {
|
|
235
|
+
clearInterval(watchdogTimer);
|
|
236
|
+
watchdogTimer = null;
|
|
237
|
+
}
|
|
238
|
+
stopFiber();
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
return {
|
|
242
|
+
getConfig: () => run((client) => client[TOKMON_WS_METHODS.getConfig]({})).then((state) => state),
|
|
243
|
+
setConfig: (config) => run((client) => client[TOKMON_WS_METHODS.setConfig](config)).then((state) => state),
|
|
244
|
+
refresh: (scope = "all") => run((client) => client[TOKMON_WS_METHODS.refresh]({ scope })),
|
|
245
|
+
browseFs: (path) => run((client) => client[TOKMON_WS_METHODS.browseFs]({ path })),
|
|
246
|
+
subscribeSnapshot: (onSnapshot) => subscribe(
|
|
247
|
+
(client) => client[TOKMON_WS_METHODS.snapshot]({}).pipe(Stream.map((value) => value)),
|
|
248
|
+
onSnapshot,
|
|
249
|
+
(snapshot) => Math.max(options.snapshotStaleFloorMs ?? 9e4, snapshot.intervalMs * 3)
|
|
250
|
+
),
|
|
251
|
+
subscribeConfig: (onConfig) => subscribe((client) => client[TOKMON_WS_METHODS.config]({}).pipe(Stream.map((value) => value)), onConfig),
|
|
252
|
+
async close() {
|
|
253
|
+
if (closed) return;
|
|
254
|
+
closed = true;
|
|
255
|
+
setConn("closed");
|
|
256
|
+
const active = [...fibers];
|
|
257
|
+
fibers.clear();
|
|
258
|
+
const activeSession = session ?? await sessionPromise?.catch(() => null) ?? null;
|
|
259
|
+
await Promise.all(active.map(
|
|
260
|
+
(fiber) => (activeSession?.runtime.runPromise(Fiber.interrupt(fiber)) ?? Effect.runPromise(Fiber.interrupt(fiber))).catch(() => {
|
|
261
|
+
})
|
|
262
|
+
));
|
|
263
|
+
session = null;
|
|
264
|
+
sessionPromise = null;
|
|
265
|
+
if (activeSession) {
|
|
266
|
+
await activeSession.runtime.dispose().catch(() => {
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export {
|
|
274
|
+
createDaemonRpcClient
|
|
275
|
+
};
|