modelstat 0.0.20 → 0.0.21
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/cli.mjs +181 -41
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -44503,11 +44503,130 @@ var init_scan = __esm({
|
|
|
44503
44503
|
init_pipeline2();
|
|
44504
44504
|
init_config2();
|
|
44505
44505
|
init_api();
|
|
44506
|
-
AGENT_VERSION = "agent-dev-0.0.
|
|
44506
|
+
AGENT_VERSION = "agent-dev-0.0.21";
|
|
44507
44507
|
BATCH_MAX_EVENTS = 2e3;
|
|
44508
44508
|
}
|
|
44509
44509
|
});
|
|
44510
44510
|
|
|
44511
|
+
// src/lock.ts
|
|
44512
|
+
import {
|
|
44513
|
+
closeSync,
|
|
44514
|
+
existsSync as existsSync5,
|
|
44515
|
+
mkdirSync as mkdirSync2,
|
|
44516
|
+
openSync,
|
|
44517
|
+
readFileSync as readFileSync2,
|
|
44518
|
+
renameSync,
|
|
44519
|
+
unlinkSync as unlinkSync2,
|
|
44520
|
+
writeFileSync as writeFileSync3,
|
|
44521
|
+
writeSync
|
|
44522
|
+
} from "fs";
|
|
44523
|
+
import { homedir as homedir5 } from "os";
|
|
44524
|
+
import { join as join4 } from "path";
|
|
44525
|
+
function isProcessAlive(pid) {
|
|
44526
|
+
if (!pid || pid <= 0) return false;
|
|
44527
|
+
try {
|
|
44528
|
+
process.kill(pid, 0);
|
|
44529
|
+
return true;
|
|
44530
|
+
} catch (e) {
|
|
44531
|
+
const code = e.code;
|
|
44532
|
+
if (code === "EPERM") return true;
|
|
44533
|
+
return false;
|
|
44534
|
+
}
|
|
44535
|
+
}
|
|
44536
|
+
function readLock() {
|
|
44537
|
+
try {
|
|
44538
|
+
const raw = readFileSync2(LOCK_FILE, "utf8");
|
|
44539
|
+
const obj = JSON.parse(raw);
|
|
44540
|
+
if (typeof obj.pid !== "number") return null;
|
|
44541
|
+
return {
|
|
44542
|
+
pid: obj.pid,
|
|
44543
|
+
startedAt: obj.startedAt ?? "unknown",
|
|
44544
|
+
agentVersion: obj.agentVersion ?? "unknown",
|
|
44545
|
+
apiUrl: obj.apiUrl ?? "unknown"
|
|
44546
|
+
};
|
|
44547
|
+
} catch {
|
|
44548
|
+
return null;
|
|
44549
|
+
}
|
|
44550
|
+
}
|
|
44551
|
+
function writeLockAtomic(meta) {
|
|
44552
|
+
mkdirSync2(LOCK_DIR, { recursive: true });
|
|
44553
|
+
const tmp = `${LOCK_FILE}.${meta.pid}.${Date.now()}.tmp`;
|
|
44554
|
+
const fd = openSync(tmp, "wx");
|
|
44555
|
+
try {
|
|
44556
|
+
writeSync(fd, JSON.stringify(meta, null, 2));
|
|
44557
|
+
} finally {
|
|
44558
|
+
closeSync(fd);
|
|
44559
|
+
}
|
|
44560
|
+
renameSync(tmp, LOCK_FILE);
|
|
44561
|
+
}
|
|
44562
|
+
function removeLockIfOwned(ownerPid) {
|
|
44563
|
+
const lock = readLock();
|
|
44564
|
+
if (!lock) return;
|
|
44565
|
+
if (lock.pid !== ownerPid) return;
|
|
44566
|
+
try {
|
|
44567
|
+
unlinkSync2(LOCK_FILE);
|
|
44568
|
+
} catch {
|
|
44569
|
+
}
|
|
44570
|
+
}
|
|
44571
|
+
function acquireDaemonLock(opts) {
|
|
44572
|
+
const existing = readLock();
|
|
44573
|
+
if (existing && isProcessAlive(existing.pid)) {
|
|
44574
|
+
if (!opts.force) {
|
|
44575
|
+
const ageSec = ageInSeconds(existing.startedAt);
|
|
44576
|
+
return { kind: "already_running", owner: existing, ageSec };
|
|
44577
|
+
}
|
|
44578
|
+
try {
|
|
44579
|
+
process.kill(existing.pid, "SIGTERM");
|
|
44580
|
+
} catch {
|
|
44581
|
+
}
|
|
44582
|
+
}
|
|
44583
|
+
const meta = {
|
|
44584
|
+
pid: process.pid,
|
|
44585
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
44586
|
+
agentVersion: opts.agentVersion,
|
|
44587
|
+
apiUrl: opts.apiUrl
|
|
44588
|
+
};
|
|
44589
|
+
writeLockAtomic(meta);
|
|
44590
|
+
const cleanup = () => removeLockIfOwned(process.pid);
|
|
44591
|
+
process.once("beforeExit", cleanup);
|
|
44592
|
+
process.once("SIGINT", () => {
|
|
44593
|
+
cleanup();
|
|
44594
|
+
process.exit(130);
|
|
44595
|
+
});
|
|
44596
|
+
process.once("SIGTERM", () => {
|
|
44597
|
+
cleanup();
|
|
44598
|
+
process.exit(143);
|
|
44599
|
+
});
|
|
44600
|
+
process.once("uncaughtException", (err) => {
|
|
44601
|
+
cleanup();
|
|
44602
|
+
console.error("modelstat daemon crashed:", err);
|
|
44603
|
+
process.exit(1);
|
|
44604
|
+
});
|
|
44605
|
+
return { kind: "acquired" };
|
|
44606
|
+
}
|
|
44607
|
+
function ageInSeconds(iso) {
|
|
44608
|
+
const t = Date.parse(iso);
|
|
44609
|
+
if (Number.isNaN(t)) return -1;
|
|
44610
|
+
return Math.max(0, Math.floor((Date.now() - t) / 1e3));
|
|
44611
|
+
}
|
|
44612
|
+
function formatAge(seconds) {
|
|
44613
|
+
if (seconds < 0) return "?";
|
|
44614
|
+
if (seconds < 60) return `${seconds}s`;
|
|
44615
|
+
const m = Math.floor(seconds / 60);
|
|
44616
|
+
const s = seconds % 60;
|
|
44617
|
+
if (m < 60) return `${m}m ${s}s`;
|
|
44618
|
+
const h = Math.floor(m / 60);
|
|
44619
|
+
return `${h}h ${m % 60}m`;
|
|
44620
|
+
}
|
|
44621
|
+
var LOCK_DIR, LOCK_FILE;
|
|
44622
|
+
var init_lock = __esm({
|
|
44623
|
+
"src/lock.ts"() {
|
|
44624
|
+
"use strict";
|
|
44625
|
+
LOCK_DIR = join4(homedir5(), ".modelstat");
|
|
44626
|
+
LOCK_FILE = join4(LOCK_DIR, "daemon.lock");
|
|
44627
|
+
}
|
|
44628
|
+
});
|
|
44629
|
+
|
|
44511
44630
|
// ../../node_modules/.pnpm/readdirp@4.1.2/node_modules/readdirp/esm/index.js
|
|
44512
44631
|
import { stat as stat3, lstat, readdir as readdir2, realpath } from "fs/promises";
|
|
44513
44632
|
import { Readable as Readable2 } from "stream";
|
|
@@ -46234,7 +46353,7 @@ __export(daemon_exports, {
|
|
|
46234
46353
|
setProgress: () => setProgress,
|
|
46235
46354
|
setQueue: () => setQueue
|
|
46236
46355
|
});
|
|
46237
|
-
import { existsSync as
|
|
46356
|
+
import { existsSync as existsSync6, statSync as statSync2 } from "fs";
|
|
46238
46357
|
function setPhase(phase, message) {
|
|
46239
46358
|
status.phase = phase;
|
|
46240
46359
|
status.message = message ?? null;
|
|
@@ -46326,10 +46445,29 @@ async function runScanCycle(reason) {
|
|
|
46326
46445
|
function basename3(p) {
|
|
46327
46446
|
return p.split("/").pop() ?? p;
|
|
46328
46447
|
}
|
|
46329
|
-
async function runDaemon() {
|
|
46448
|
+
async function runDaemon(opts = {}) {
|
|
46330
46449
|
if (!state.bearer || !state.deviceId) {
|
|
46331
46450
|
throw new Error("not enrolled \u2014 run `modelstat connect` first");
|
|
46332
46451
|
}
|
|
46452
|
+
const lock = acquireDaemonLock({
|
|
46453
|
+
agentVersion: AGENT_VERSION2,
|
|
46454
|
+
apiUrl: state.apiUrl,
|
|
46455
|
+
force: opts.force === true
|
|
46456
|
+
});
|
|
46457
|
+
if (lock.kind === "already_running") {
|
|
46458
|
+
console.log(
|
|
46459
|
+
`modelstat daemon is already running \u2014 PID ${lock.owner.pid}, started ${formatAge(
|
|
46460
|
+
lock.ageSec
|
|
46461
|
+
)} ago, agent ${lock.owner.agentVersion}.`
|
|
46462
|
+
);
|
|
46463
|
+
console.log(
|
|
46464
|
+
" \u2192 to stop it: kill " + lock.owner.pid
|
|
46465
|
+
);
|
|
46466
|
+
console.log(
|
|
46467
|
+
" \u2192 to force-replace it: modelstat start --force"
|
|
46468
|
+
);
|
|
46469
|
+
return;
|
|
46470
|
+
}
|
|
46333
46471
|
setPhase("starting", "Booting");
|
|
46334
46472
|
const hb = setInterval(() => void sendHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
46335
46473
|
hb.unref();
|
|
@@ -46337,21 +46475,21 @@ async function runDaemon() {
|
|
|
46337
46475
|
await runDiscovery();
|
|
46338
46476
|
await runScanCycle("startup");
|
|
46339
46477
|
const chokidar = (await Promise.resolve().then(() => (init_esm2(), esm_exports))).default;
|
|
46340
|
-
const { homedir:
|
|
46341
|
-
const { join:
|
|
46342
|
-
const home2 =
|
|
46478
|
+
const { homedir: homedir7, platform: platform5 } = await import("os");
|
|
46479
|
+
const { join: join8 } = await import("path");
|
|
46480
|
+
const home2 = homedir7();
|
|
46343
46481
|
const dirs = [
|
|
46344
|
-
|
|
46345
|
-
|
|
46346
|
-
|
|
46347
|
-
|
|
46482
|
+
join8(home2, ".claude/projects"),
|
|
46483
|
+
join8(home2, ".codex/sessions"),
|
|
46484
|
+
join8(home2, ".cursor/ai-tracking"),
|
|
46485
|
+
join8(home2, ".gemini"),
|
|
46348
46486
|
...platform5() === "darwin" ? [
|
|
46349
|
-
|
|
46350
|
-
|
|
46487
|
+
join8(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
|
|
46488
|
+
join8(home2, "Library/Application Support/Claude")
|
|
46351
46489
|
] : [
|
|
46352
|
-
|
|
46490
|
+
join8(home2, ".config/Cursor/User/workspaceStorage")
|
|
46353
46491
|
]
|
|
46354
|
-
].filter((p) =>
|
|
46492
|
+
].filter((p) => existsSync6(p) && statSync2(p).isDirectory());
|
|
46355
46493
|
setPhase("watching", `Watching ${dirs.length} directories`);
|
|
46356
46494
|
const watcher = chokidar.watch(dirs, {
|
|
46357
46495
|
persistent: true,
|
|
@@ -46396,8 +46534,9 @@ var init_daemon = __esm({
|
|
|
46396
46534
|
init_src2();
|
|
46397
46535
|
init_api();
|
|
46398
46536
|
init_config2();
|
|
46537
|
+
init_lock();
|
|
46399
46538
|
init_scan();
|
|
46400
|
-
AGENT_VERSION2 = "agent-dev-0.0.
|
|
46539
|
+
AGENT_VERSION2 = "agent-dev-0.0.21";
|
|
46401
46540
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
46402
46541
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
46403
46542
|
status = {
|
|
@@ -46417,37 +46556,37 @@ var watch_exports = {};
|
|
|
46417
46556
|
__export(watch_exports, {
|
|
46418
46557
|
watchForever: () => watchForever
|
|
46419
46558
|
});
|
|
46420
|
-
import { existsSync as
|
|
46421
|
-
import { homedir as
|
|
46422
|
-
import { join as
|
|
46559
|
+
import { existsSync as existsSync7 } from "fs";
|
|
46560
|
+
import { homedir as homedir6, platform as platform3 } from "os";
|
|
46561
|
+
import { join as join7 } from "path";
|
|
46423
46562
|
function resolveWatchDirs() {
|
|
46424
|
-
const home2 =
|
|
46425
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
46426
|
-
const xdgData = process.env.XDG_DATA_HOME ??
|
|
46563
|
+
const home2 = homedir6();
|
|
46564
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join7(home2, ".config");
|
|
46565
|
+
const xdgData = process.env.XDG_DATA_HOME ?? join7(home2, ".local/share");
|
|
46427
46566
|
const candidates = [
|
|
46428
46567
|
// universal (default HOME-rooted CLI data dirs)
|
|
46429
|
-
|
|
46430
|
-
|
|
46431
|
-
|
|
46432
|
-
|
|
46433
|
-
|
|
46568
|
+
join7(home2, ".claude/projects"),
|
|
46569
|
+
join7(home2, ".codex/sessions"),
|
|
46570
|
+
join7(home2, ".cursor/ai-tracking"),
|
|
46571
|
+
join7(home2, ".gemini"),
|
|
46572
|
+
join7(home2, ".aider"),
|
|
46434
46573
|
// XDG / Linux
|
|
46435
|
-
|
|
46436
|
-
|
|
46437
|
-
|
|
46438
|
-
|
|
46439
|
-
|
|
46440
|
-
|
|
46574
|
+
join7(xdgConfig, "claude/projects"),
|
|
46575
|
+
join7(xdgConfig, "codex/sessions"),
|
|
46576
|
+
join7(xdgConfig, "Cursor/User/workspaceStorage"),
|
|
46577
|
+
join7(xdgConfig, "Code/User/workspaceStorage"),
|
|
46578
|
+
join7(xdgConfig, "Code - Insiders/User/workspaceStorage"),
|
|
46579
|
+
join7(xdgData, "claude/projects"),
|
|
46441
46580
|
// macOS
|
|
46442
46581
|
...platform3() === "darwin" ? [
|
|
46443
|
-
|
|
46444
|
-
|
|
46445
|
-
|
|
46446
|
-
|
|
46447
|
-
|
|
46582
|
+
join7(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
|
|
46583
|
+
join7(home2, "Library/Application Support/Claude"),
|
|
46584
|
+
join7(home2, "Library/Application Support/Code/User/workspaceStorage"),
|
|
46585
|
+
join7(home2, "Library/Application Support/Windsurf/User/workspaceStorage"),
|
|
46586
|
+
join7(home2, "Library/Application Support/Zed")
|
|
46448
46587
|
] : []
|
|
46449
46588
|
];
|
|
46450
|
-
return Array.from(new Set(candidates)).filter((p) =>
|
|
46589
|
+
return Array.from(new Set(candidates)).filter((p) => existsSync7(p));
|
|
46451
46590
|
}
|
|
46452
46591
|
async function safeScan(reason) {
|
|
46453
46592
|
if (scanning) {
|
|
@@ -47063,13 +47202,14 @@ async function cmdWatch() {
|
|
|
47063
47202
|
const { watchForever: watchForever2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
|
|
47064
47203
|
await watchForever2();
|
|
47065
47204
|
}
|
|
47066
|
-
async function cmdStart() {
|
|
47205
|
+
async function cmdStart(rest) {
|
|
47067
47206
|
if (!state.bearer || !state.deviceId) {
|
|
47068
47207
|
console.error("not paired yet. Run `modelstat connect` first.");
|
|
47069
47208
|
process.exit(1);
|
|
47070
47209
|
}
|
|
47210
|
+
const force = rest.includes("--force") || rest.includes("-f");
|
|
47071
47211
|
const { runDaemon: runDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
|
|
47072
|
-
await runDaemon2();
|
|
47212
|
+
await runDaemon2({ force });
|
|
47073
47213
|
}
|
|
47074
47214
|
async function cmdStop() {
|
|
47075
47215
|
try {
|
|
@@ -47233,7 +47373,7 @@ async function main() {
|
|
|
47233
47373
|
case "start":
|
|
47234
47374
|
if (!state.bearer || !state.deviceId)
|
|
47235
47375
|
return cmdConnect(parseConnectOpts(rest));
|
|
47236
|
-
return cmdStart();
|
|
47376
|
+
return cmdStart(rest);
|
|
47237
47377
|
case "connect":
|
|
47238
47378
|
return cmdConnect(parseConnectOpts(rest));
|
|
47239
47379
|
case "self-register":
|