tokmon 0.28.2 → 0.28.4

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 (56) hide show
  1. package/README.md +156 -234
  2. package/THIRD_PARTY_NOTICES.md +62 -0
  3. package/dist/{bootstrap-ink-IMT3S5HU.js → bootstrap-ink-WG4WG3R2.js} +1579 -1136
  4. package/dist/chunk-5B3XCEMC.js +294 -0
  5. package/dist/chunk-7CH6S7BR.js +447 -0
  6. package/dist/{chunk-LCAHTRGP.js → chunk-CFKUZCMB.js} +56 -29
  7. package/dist/{chunk-LKYOWBD4.js → chunk-FLANGRI6.js} +21 -42
  8. package/dist/chunk-HNWEJ6MS.js +1094 -0
  9. package/dist/{chunk-WUK3MQUB.js → chunk-JZSZEHVD.js} +167 -10
  10. package/dist/{chunk-6ZEW2M6P.js → chunk-P57DVFNH.js} +125 -25
  11. package/dist/chunk-TN6V7XSL.js +512 -0
  12. package/dist/chunk-UYPDMVW5.js +21 -0
  13. package/dist/{cli-command-DCENRCO3.js → cli-command-I4GRR3MF.js} +249 -15
  14. package/dist/cli.js +6 -6
  15. package/dist/{config-HXFJTNLM.js → config-3DGZ47F7.js} +23 -3
  16. package/dist/daemon-ZXV3V5CS.js +384 -0
  17. package/dist/daemon-handle-G4OGPFR3.js +11 -0
  18. package/dist/server-EPSH52DR.js +12 -0
  19. package/dist/web/assets/{Area-CMXvOw33.js → Area-stRnp8Km.js} +1 -1
  20. package/dist/web/assets/analytics-moZZt9L_.js +2 -0
  21. package/dist/web/assets/breakdown-km9w7myU.js +4 -0
  22. package/dist/web/assets/button-DrX04Cc2.js +1 -0
  23. package/dist/web/assets/{chart-cZoLgpmG.js → chart-D0CtuL2k.js} +1 -1
  24. package/dist/web/assets/explore-B11uNJV1.js +22 -0
  25. package/dist/web/assets/index-RDhXsCNG.js +83 -0
  26. package/dist/web/assets/index-ZmOS8p9O.css +1 -0
  27. package/dist/web/assets/models-DYbZnY34.js +2 -0
  28. package/dist/web/assets/overview-CruICbfJ.js +2 -0
  29. package/dist/web/assets/panel-xckM3_GC.js +1 -0
  30. package/dist/web/assets/settings-sheet-G-BTSIF6.js +1 -0
  31. package/dist/web/assets/share-sheet-weIdd4DW.js +2 -0
  32. package/dist/web/assets/timeline-BHgniliZ.js +1 -0
  33. package/dist/web/index.html +30 -4
  34. package/package.json +13 -4
  35. package/site/THIRD_PARTY_NOTICES.md +83 -0
  36. package/site/public/fonts/OFL.txt +93 -0
  37. package/dist/chunk-44ZSQEOS.js +0 -299
  38. package/dist/chunk-HP5UZCXP.js +0 -436
  39. package/dist/chunk-J3JW3RFP.js +0 -196
  40. package/dist/chunk-M7XMV36F.js +0 -275
  41. package/dist/daemon-6Y4O7NXS.js +0 -241
  42. package/dist/daemon-handle-GVCYVDPO.js +0 -10
  43. package/dist/server-GXXOWUVZ.js +0 -11
  44. package/dist/web/assets/analytics-DqdZXOJL.js +0 -2
  45. package/dist/web/assets/breakdown-kOSSJ3mI.js +0 -4
  46. package/dist/web/assets/explore-vcsM8K9I.js +0 -22
  47. package/dist/web/assets/index-BErCXT7f.css +0 -1
  48. package/dist/web/assets/index-DH3dOnbg.js +0 -83
  49. package/dist/web/assets/models-B5ikm83_.js +0 -2
  50. package/dist/web/assets/overview-DD1kP8CP.js +0 -2
  51. package/dist/web/assets/panel-DTQBwExW.js +0 -1
  52. package/dist/web/assets/primitives-C36qJlA2.js +0 -1
  53. package/dist/web/assets/settings-sheet-_i2zTQjf.js +0 -1
  54. package/dist/web/assets/share-sheet-BLHmrFbm.js +0 -2
  55. package/dist/web/assets/timeline-B4uG-Emo.js +0 -1
  56. package/dist/web/assets/use-dialog-trap-bfaXEMz9.js +0 -1
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ daemonChannelFromWire,
4
+ resolveDaemonChannel
5
+ } from "./chunk-UYPDMVW5.js";
6
+ import {
7
+ cacheDir
8
+ } from "./chunk-HNWEJ6MS.js";
9
+
10
+ // src/web/lockfile.ts
11
+ import { closeSync, fchmodSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
12
+ import { isAbsolute, join } from "path";
13
+ function lockDir(opts = {}) {
14
+ const injected = opts.cachePath ?? process.env.TOKMON_DAEMON_CACHE_DIR;
15
+ if (injected && isAbsolute(injected)) return injected;
16
+ const root = cacheDir();
17
+ return resolveDaemonChannel(opts.channel) === "dev" ? join(root, "dev") : root;
18
+ }
19
+ function lockfilePath(opts = {}) {
20
+ return join(lockDir(opts), "daemon.json");
21
+ }
22
+ function isLoopbackUrl(value) {
23
+ try {
24
+ const url = new URL(value);
25
+ return url.protocol === "http:" && (url.hostname === "127.0.0.1" || url.hostname === "localhost" || url.hostname === "::1");
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+ function validLock(value) {
31
+ const lock = value;
32
+ return !!lock && typeof lock.pid === "number" && Number.isInteger(lock.pid) && lock.pid > 0 && typeof lock.port === "number" && Number.isInteger(lock.port) && lock.port >= 0 && lock.port <= 65535 && typeof lock.url === "string" && (lock.state === "starting" || isLoopbackUrl(lock.url)) && typeof lock.wsToken === "string" && lock.wsToken.length >= 32 && typeof lock.version === "string" && typeof lock.protocolVersion === "number" && Number.isSafeInteger(lock.protocolVersion) && lock.protocolVersion >= 1 && Array.isArray(lock.capabilities) && lock.capabilities.every((capability) => typeof capability === "string") && (lock.ownerKind === "cli" || lock.ownerKind === "desktop") && daemonChannelFromWire(lock.channel) !== null && typeof lock.startedAt === "number" && typeof lock.ownerId === "string" && lock.ownerId.length >= 32 && (lock.state === "starting" || lock.state === "ready");
33
+ }
34
+ function readLock(opts = {}) {
35
+ try {
36
+ const path = lockfilePath(opts);
37
+ const stat = statSync(path);
38
+ if (!stat.isFile() || (stat.mode & 63) !== 0) return null;
39
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
40
+ if (!validLock(parsed)) return null;
41
+ const channel = daemonChannelFromWire(parsed.channel);
42
+ return channel === resolveDaemonChannel(opts.channel) ? { ...parsed, channel } : null;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ function readForeignLock(opts = {}) {
48
+ try {
49
+ const path = lockfilePath(opts);
50
+ const stat = statSync(path);
51
+ if (!stat.isFile() || (stat.mode & 63) !== 0) return null;
52
+ const value = JSON.parse(readFileSync(path, "utf-8"));
53
+ const channel = daemonChannelFromWire(value.channel);
54
+ if (!Number.isInteger(value.pid) || value.pid <= 0 || typeof value.url !== "string" || !isLoopbackUrl(value.url) || typeof value.wsToken !== "string" || value.wsToken.length < 32 || value.state !== "ready" || channel !== resolveDaemonChannel(opts.channel)) return null;
55
+ if (value.ownerKind !== void 0 && value.ownerKind !== "cli" && value.ownerKind !== "desktop") return null;
56
+ if (value.protocolVersion !== void 0 && (!Number.isSafeInteger(value.protocolVersion) || value.protocolVersion < 1)) return null;
57
+ return {
58
+ pid: value.pid,
59
+ ...Number.isInteger(value.port) ? { port: value.port } : {},
60
+ url: value.url,
61
+ wsToken: value.wsToken,
62
+ ...typeof value.version === "string" ? { version: value.version } : {},
63
+ ...typeof value.protocolVersion === "number" ? { protocolVersion: value.protocolVersion } : {},
64
+ ...Array.isArray(value.capabilities) && value.capabilities.every((item) => typeof item === "string") ? { capabilities: value.capabilities } : {},
65
+ ...value.ownerKind === "cli" || value.ownerKind === "desktop" ? { ownerKind: value.ownerKind } : {},
66
+ channel,
67
+ ...typeof value.startedAt === "number" ? { startedAt: value.startedAt } : {},
68
+ ...typeof value.ownerId === "string" ? { ownerId: value.ownerId } : {},
69
+ state: "ready"
70
+ };
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+ function ensureLockDir(opts) {
76
+ const dir = lockDir(opts);
77
+ mkdirSync(dir, { recursive: true, mode: 448 });
78
+ let fd;
79
+ try {
80
+ fd = openSync(dir, "r");
81
+ fchmodSync(fd, 448);
82
+ } catch {
83
+ } finally {
84
+ if (fd !== void 0) try {
85
+ closeSync(fd);
86
+ } catch {
87
+ }
88
+ }
89
+ }
90
+ function writeNew(path, lock) {
91
+ let fd;
92
+ let created = false;
93
+ try {
94
+ fd = openSync(path, "wx", 384);
95
+ created = true;
96
+ fchmodSync(fd, 384);
97
+ writeFileSync(fd, JSON.stringify(lock));
98
+ fsyncSync(fd);
99
+ return true;
100
+ } catch {
101
+ if (created) try {
102
+ unlinkSync(path);
103
+ } catch {
104
+ }
105
+ return false;
106
+ } finally {
107
+ if (fd !== void 0) try {
108
+ closeSync(fd);
109
+ } catch {
110
+ }
111
+ }
112
+ }
113
+ function acquireLock(lock, opts = {}) {
114
+ if (lock.channel !== resolveDaemonChannel(opts.channel)) return false;
115
+ try {
116
+ ensureLockDir(opts);
117
+ return writeNew(lockfilePath(opts), lock);
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ function writeLock(lock, opts = {}) {
123
+ const current = readLock(opts);
124
+ if (!current || current.ownerId !== lock.ownerId || current.pid !== process.pid || lock.channel !== current.channel) return false;
125
+ const path = lockfilePath(opts);
126
+ const tmp = join(lockDir(opts), `daemon.json.${process.pid}.${lock.ownerId}.tmp`);
127
+ let fd;
128
+ try {
129
+ writeFileSync(tmp, JSON.stringify(lock), { mode: 384 });
130
+ try {
131
+ fd = openSync(tmp, "r");
132
+ fchmodSync(fd, 384);
133
+ fsyncSync(fd);
134
+ } catch {
135
+ }
136
+ renameSync(tmp, path);
137
+ return true;
138
+ } catch {
139
+ try {
140
+ unlinkSync(tmp);
141
+ } catch {
142
+ }
143
+ return false;
144
+ } finally {
145
+ if (fd !== void 0) try {
146
+ closeSync(fd);
147
+ } catch {
148
+ }
149
+ }
150
+ }
151
+ function unlinkLock(ownerId, opts = {}) {
152
+ const current = readLock(opts);
153
+ if (!current || current.ownerId !== ownerId || current.pid !== process.pid) return false;
154
+ try {
155
+ unlinkSync(lockfilePath(opts));
156
+ return true;
157
+ } catch {
158
+ return false;
159
+ }
160
+ }
161
+ function reclaimDeadLock(opts = {}) {
162
+ const current = readLock(opts);
163
+ if (!current || isAlive(current.pid)) return false;
164
+ try {
165
+ unlinkSync(lockfilePath(opts));
166
+ return true;
167
+ } catch {
168
+ return false;
169
+ }
170
+ }
171
+ function reclaimUnhealthyLock(expected, opts = {}, minimumAgeMs = 0) {
172
+ const path = lockfilePath(opts);
173
+ try {
174
+ const before = lstatSync(path);
175
+ if (!before.isFile()) return false;
176
+ const current = readLock(opts);
177
+ if (!current || current.ownerId !== expected.ownerId || current.pid !== expected.pid || current.startedAt !== expected.startedAt || current.state !== "ready" || Date.now() - current.startedAt < minimumAgeMs) return false;
178
+ const after = lstatSync(path);
179
+ if (after.dev !== before.dev || after.ino !== before.ino) return false;
180
+ unlinkSync(path);
181
+ return true;
182
+ } catch {
183
+ return false;
184
+ }
185
+ }
186
+ function reclaimAbandonedLock(opts = {}, graceMs = 1e4) {
187
+ const path = lockfilePath(opts);
188
+ try {
189
+ const before = lstatSync(path);
190
+ if (!before.isFile()) return false;
191
+ let pid = null;
192
+ try {
193
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
194
+ if (Number.isInteger(parsed.pid) && parsed.pid > 0) pid = parsed.pid;
195
+ } catch {
196
+ }
197
+ if (pid !== null && isAlive(pid)) return false;
198
+ if (pid === null && Date.now() - before.mtimeMs < graceMs) return false;
199
+ const after = lstatSync(path);
200
+ if (after.dev !== before.dev || after.ino !== before.ino) return false;
201
+ unlinkSync(path);
202
+ return true;
203
+ } catch {
204
+ return false;
205
+ }
206
+ }
207
+ function isAlive(pid) {
208
+ if (!Number.isInteger(pid) || pid <= 0) return false;
209
+ try {
210
+ process.kill(pid, 0);
211
+ return true;
212
+ } catch (err) {
213
+ return err.code === "EPERM";
214
+ }
215
+ }
216
+ function sameCapabilities(actual, expected) {
217
+ return Array.isArray(actual) && actual.length === expected.length && actual.every((capability, index) => capability === expected[index]);
218
+ }
219
+ async function ownerHealth(url, token, timeoutMs = 500) {
220
+ if (!isLoopbackUrl(url) || !token) return null;
221
+ try {
222
+ const res = await fetch(`${url.replace(/\/+$/, "")}/healthz`, {
223
+ headers: { "x-tokmon-token": token },
224
+ signal: AbortSignal.timeout(timeoutMs)
225
+ });
226
+ if (!res.ok) return null;
227
+ return await res.json();
228
+ } catch {
229
+ return null;
230
+ }
231
+ }
232
+ async function probeHealth(url, token, expected = {}, timeoutMs = 500) {
233
+ const body = await ownerHealth(url, token, timeoutMs);
234
+ const bodyChannel = daemonChannelFromWire(body?.channel);
235
+ return body !== null && body.ok === true && body.owner === true && (expected.version === void 0 || body.version === expected.version) && (expected.protocolVersion === void 0 || body.protocolVersion === expected.protocolVersion) && (expected.capabilities === void 0 || sameCapabilities(body.capabilities, expected.capabilities)) && (expected.ownerKind === void 0 || body.ownerKind === expected.ownerKind) && (expected.channel === void 0 || bodyChannel === expected.channel);
236
+ }
237
+ async function verifyLock(lock, protocolVersion, timeoutMs) {
238
+ if (!lock || lock.state !== "ready" || lock.protocolVersion !== protocolVersion || !isAlive(lock.pid)) return null;
239
+ return await probeHealth(lock.url, lock.wsToken, lock, timeoutMs) ? lock : null;
240
+ }
241
+ var TAKEOVER_TIMEOUT_MS = 5e3;
242
+ var takeoverDelay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
243
+ function sameForeignOwner(left, right) {
244
+ return left.pid === right.pid && left.wsToken === right.wsToken && left.ownerId === right.ownerId;
245
+ }
246
+ async function retireIncompatibleCliOwner(opts, protocolVersion, timeoutMs = TAKEOVER_TIMEOUT_MS) {
247
+ const compatible = readLock(opts);
248
+ if (compatible?.protocolVersion === protocolVersion) return false;
249
+ const foreign = readForeignLock(opts);
250
+ const legacyCli = foreign?.ownerKind === void 0 && foreign?.protocolVersion === void 0;
251
+ if (!foreign || foreign.state !== "ready" || foreign.ownerKind === "desktop" || foreign.ownerKind !== "cli" && !legacyCli || foreign.pid === process.pid || !isAlive(foreign.pid)) return false;
252
+ const health = await ownerHealth(foreign.url, foreign.wsToken, Math.min(1e3, timeoutMs));
253
+ const healthChannel = daemonChannelFromWire(health?.channel);
254
+ if (health?.ok !== true || health.owner !== true || health.ownerKind !== void 0 && health.ownerKind !== "cli" || healthChannel !== foreign.channel) return false;
255
+ try {
256
+ process.kill(foreign.pid, "SIGTERM");
257
+ } catch {
258
+ if (!isAlive(foreign.pid)) {
259
+ reclaimAbandonedLock(opts, 0);
260
+ return true;
261
+ }
262
+ return false;
263
+ }
264
+ const deadline = Date.now() + Math.max(0, timeoutMs);
265
+ while (Date.now() < deadline) {
266
+ const current = readForeignLock(opts);
267
+ if (!current || !sameForeignOwner(current, foreign)) return true;
268
+ if (!isAlive(foreign.pid)) {
269
+ reclaimAbandonedLock(opts, 0);
270
+ return true;
271
+ }
272
+ await takeoverDelay(50);
273
+ }
274
+ if (!isAlive(foreign.pid)) {
275
+ reclaimAbandonedLock(opts, 0);
276
+ return true;
277
+ }
278
+ return false;
279
+ }
280
+
281
+ export {
282
+ lockfilePath,
283
+ readLock,
284
+ readForeignLock,
285
+ acquireLock,
286
+ writeLock,
287
+ unlinkLock,
288
+ reclaimDeadLock,
289
+ reclaimUnhealthyLock,
290
+ reclaimAbandonedLock,
291
+ isAlive,
292
+ verifyLock,
293
+ retireIncompatibleCliOwner
294
+ };