modelstat 0.0.45 → 0.0.46
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 +461 -246
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
- package/vendor/tray-mac/Sources/ModelstatTray/main.swift +145 -21
package/dist/cli.mjs
CHANGED
|
@@ -50,9 +50,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
50
50
|
));
|
|
51
51
|
|
|
52
52
|
// ../../packages/parsers/src/types.ts
|
|
53
|
+
var PARSER_EVENT_CHUNK;
|
|
53
54
|
var init_types = __esm({
|
|
54
55
|
"../../packages/parsers/src/types.ts"() {
|
|
55
56
|
"use strict";
|
|
57
|
+
PARSER_EVENT_CHUNK = 256;
|
|
56
58
|
}
|
|
57
59
|
});
|
|
58
60
|
|
|
@@ -4722,6 +4724,21 @@ function extractExcerpt(content) {
|
|
|
4722
4724
|
}
|
|
4723
4725
|
async function parseClaudeCodeJsonl(ctx) {
|
|
4724
4726
|
const events = [];
|
|
4727
|
+
let chunk = [];
|
|
4728
|
+
let emitted = 0;
|
|
4729
|
+
const emit = async (e) => {
|
|
4730
|
+
emitted += 1;
|
|
4731
|
+
if (!ctx.onEvents) {
|
|
4732
|
+
events.push(e);
|
|
4733
|
+
return;
|
|
4734
|
+
}
|
|
4735
|
+
chunk.push(e);
|
|
4736
|
+
if (chunk.length >= PARSER_EVENT_CHUNK) {
|
|
4737
|
+
const full = chunk;
|
|
4738
|
+
chunk = [];
|
|
4739
|
+
await ctx.onEvents(full);
|
|
4740
|
+
}
|
|
4741
|
+
};
|
|
4725
4742
|
let rawLines = 0;
|
|
4726
4743
|
let skipped = 0;
|
|
4727
4744
|
let bytePos = 0;
|
|
@@ -4809,7 +4826,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4809
4826
|
}
|
|
4810
4827
|
const slug = guessRepoSlugFromPath(cwd);
|
|
4811
4828
|
const excerpt = extractExcerpt(a.message?.content);
|
|
4812
|
-
|
|
4829
|
+
await emit({
|
|
4813
4830
|
source_event_id: eventId,
|
|
4814
4831
|
ts: a.timestamp,
|
|
4815
4832
|
kind: "assistant_message",
|
|
@@ -4858,7 +4875,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4858
4875
|
continue;
|
|
4859
4876
|
}
|
|
4860
4877
|
const excerpt = extractExcerpt(u.message?.content);
|
|
4861
|
-
|
|
4878
|
+
await emit({
|
|
4862
4879
|
source_event_id: eventId,
|
|
4863
4880
|
ts: u.timestamp,
|
|
4864
4881
|
kind: "user_message",
|
|
@@ -4883,9 +4900,10 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4883
4900
|
skipped += 1;
|
|
4884
4901
|
}
|
|
4885
4902
|
}
|
|
4903
|
+
if (ctx.onEvents && chunk.length > 0) await ctx.onEvents(chunk);
|
|
4886
4904
|
return {
|
|
4887
4905
|
events,
|
|
4888
|
-
stats: { rawLines, emittedEvents:
|
|
4906
|
+
stats: { rawLines, emittedEvents: emitted, skipped },
|
|
4889
4907
|
sourceFile: ctx.sourceFile
|
|
4890
4908
|
};
|
|
4891
4909
|
}
|
|
@@ -4907,6 +4925,7 @@ var init_claude_code = __esm({
|
|
|
4907
4925
|
"../../packages/parsers/src/claude-code/index.ts"() {
|
|
4908
4926
|
"use strict";
|
|
4909
4927
|
init_src();
|
|
4928
|
+
init_types();
|
|
4910
4929
|
init_git();
|
|
4911
4930
|
}
|
|
4912
4931
|
});
|
|
@@ -4922,6 +4941,21 @@ function deriveSessionIdFromRolloutPath(path5) {
|
|
|
4922
4941
|
}
|
|
4923
4942
|
async function parseCodexRollout(ctx) {
|
|
4924
4943
|
const events = [];
|
|
4944
|
+
let chunk = [];
|
|
4945
|
+
let emitted = 0;
|
|
4946
|
+
const emit = async (e) => {
|
|
4947
|
+
emitted += 1;
|
|
4948
|
+
if (!ctx.onEvents) {
|
|
4949
|
+
events.push(e);
|
|
4950
|
+
return;
|
|
4951
|
+
}
|
|
4952
|
+
chunk.push(e);
|
|
4953
|
+
if (chunk.length >= PARSER_EVENT_CHUNK) {
|
|
4954
|
+
const full = chunk;
|
|
4955
|
+
chunk = [];
|
|
4956
|
+
await ctx.onEvents(full);
|
|
4957
|
+
}
|
|
4958
|
+
};
|
|
4925
4959
|
let rawLines = 0;
|
|
4926
4960
|
let skipped = 0;
|
|
4927
4961
|
let bytePos = 0;
|
|
@@ -4977,7 +5011,7 @@ async function parseCodexRollout(ctx) {
|
|
|
4977
5011
|
continue;
|
|
4978
5012
|
}
|
|
4979
5013
|
const slug = guessRepoSlugFromPath(cwd);
|
|
4980
|
-
|
|
5014
|
+
await emit({
|
|
4981
5015
|
source_event_id: sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart),
|
|
4982
5016
|
ts,
|
|
4983
5017
|
kind: "assistant_message",
|
|
@@ -5016,7 +5050,7 @@ async function parseCodexRollout(ctx) {
|
|
|
5016
5050
|
skipped += 1;
|
|
5017
5051
|
continue;
|
|
5018
5052
|
}
|
|
5019
|
-
|
|
5053
|
+
await emit({
|
|
5020
5054
|
source_event_id: sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart),
|
|
5021
5055
|
ts,
|
|
5022
5056
|
kind: "user_message",
|
|
@@ -5042,9 +5076,10 @@ async function parseCodexRollout(ctx) {
|
|
|
5042
5076
|
}
|
|
5043
5077
|
skipped += 1;
|
|
5044
5078
|
}
|
|
5079
|
+
if (ctx.onEvents && chunk.length > 0) await ctx.onEvents(chunk);
|
|
5045
5080
|
return {
|
|
5046
5081
|
events,
|
|
5047
|
-
stats: { rawLines, emittedEvents:
|
|
5082
|
+
stats: { rawLines, emittedEvents: emitted, skipped },
|
|
5048
5083
|
sourceFile: ctx.sourceFile
|
|
5049
5084
|
};
|
|
5050
5085
|
}
|
|
@@ -5052,6 +5087,7 @@ var init_codex = __esm({
|
|
|
5052
5087
|
"../../packages/parsers/src/codex/index.ts"() {
|
|
5053
5088
|
"use strict";
|
|
5054
5089
|
init_src();
|
|
5090
|
+
init_types();
|
|
5055
5091
|
init_git();
|
|
5056
5092
|
}
|
|
5057
5093
|
});
|
|
@@ -7622,107 +7658,6 @@ var init_src2 = __esm({
|
|
|
7622
7658
|
}
|
|
7623
7659
|
});
|
|
7624
7660
|
|
|
7625
|
-
// src/identity.ts
|
|
7626
|
-
import {
|
|
7627
|
-
chmodSync,
|
|
7628
|
-
mkdirSync,
|
|
7629
|
-
readFileSync as readFileSync2,
|
|
7630
|
-
renameSync,
|
|
7631
|
-
writeFileSync,
|
|
7632
|
-
existsSync as existsSync4
|
|
7633
|
-
} from "fs";
|
|
7634
|
-
import { homedir as homedir2, hostname as osHostname } from "os";
|
|
7635
|
-
import { join as join3 } from "path";
|
|
7636
|
-
function ensureRoot() {
|
|
7637
|
-
mkdirSync(ROOT, { recursive: true, mode: 448 });
|
|
7638
|
-
}
|
|
7639
|
-
function writeAtomic(meta) {
|
|
7640
|
-
ensureRoot();
|
|
7641
|
-
const tmp = `${IDENTITY_FILE}.${process.pid}.tmp`;
|
|
7642
|
-
writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
7643
|
-
renameSync(tmp, IDENTITY_FILE);
|
|
7644
|
-
try {
|
|
7645
|
-
chmodSync(IDENTITY_FILE, 384);
|
|
7646
|
-
} catch {
|
|
7647
|
-
}
|
|
7648
|
-
}
|
|
7649
|
-
function identityPath() {
|
|
7650
|
-
return IDENTITY_FILE;
|
|
7651
|
-
}
|
|
7652
|
-
function hasIdentityFile() {
|
|
7653
|
-
return existsSync4(IDENTITY_FILE);
|
|
7654
|
-
}
|
|
7655
|
-
function parseFile() {
|
|
7656
|
-
try {
|
|
7657
|
-
const raw = readFileSync2(IDENTITY_FILE, "utf8");
|
|
7658
|
-
const obj = JSON.parse(raw);
|
|
7659
|
-
if (!obj.deviceUuid || !obj.deviceId || !obj.bearerToken) {
|
|
7660
|
-
return null;
|
|
7661
|
-
}
|
|
7662
|
-
return {
|
|
7663
|
-
deviceUuid: obj.deviceUuid,
|
|
7664
|
-
deviceId: obj.deviceId,
|
|
7665
|
-
bearerToken: obj.bearerToken,
|
|
7666
|
-
claimCode: obj.claimCode ?? null,
|
|
7667
|
-
claimUrl: obj.claimUrl ?? null,
|
|
7668
|
-
hostname: obj.hostname ?? osHostname(),
|
|
7669
|
-
createdAt: obj.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7670
|
-
userEmail: obj.userEmail ?? null,
|
|
7671
|
-
defaultOrgId: obj.defaultOrgId ?? null
|
|
7672
|
-
};
|
|
7673
|
-
} catch {
|
|
7674
|
-
return null;
|
|
7675
|
-
}
|
|
7676
|
-
}
|
|
7677
|
-
function loadIdentity(migrateFromConf2) {
|
|
7678
|
-
const fromFile = parseFile();
|
|
7679
|
-
if (fromFile) return fromFile;
|
|
7680
|
-
if (!migrateFromConf2) return null;
|
|
7681
|
-
const legacy = migrateFromConf2();
|
|
7682
|
-
if (!legacy) return null;
|
|
7683
|
-
if (!legacy.deviceUuid || !legacy.deviceId || !legacy.bearerToken) {
|
|
7684
|
-
return null;
|
|
7685
|
-
}
|
|
7686
|
-
const migrated = {
|
|
7687
|
-
deviceUuid: legacy.deviceUuid,
|
|
7688
|
-
deviceId: legacy.deviceId,
|
|
7689
|
-
bearerToken: legacy.bearerToken,
|
|
7690
|
-
claimCode: legacy.claimCode ?? null,
|
|
7691
|
-
claimUrl: legacy.claimUrl ?? null,
|
|
7692
|
-
hostname: osHostname(),
|
|
7693
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7694
|
-
userEmail: legacy.userEmail ?? null,
|
|
7695
|
-
defaultOrgId: legacy.defaultOrgId ?? null
|
|
7696
|
-
};
|
|
7697
|
-
writeAtomic(migrated);
|
|
7698
|
-
return migrated;
|
|
7699
|
-
}
|
|
7700
|
-
function saveIdentity(meta) {
|
|
7701
|
-
writeAtomic(meta);
|
|
7702
|
-
}
|
|
7703
|
-
function backupIdentity() {
|
|
7704
|
-
if (!existsSync4(IDENTITY_FILE)) return null;
|
|
7705
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7706
|
-
const dest = `${IDENTITY_FILE}.bak-${stamp}`;
|
|
7707
|
-
renameSync(IDENTITY_FILE, dest);
|
|
7708
|
-
return dest;
|
|
7709
|
-
}
|
|
7710
|
-
function updateIdentity(patch) {
|
|
7711
|
-
const current = parseFile();
|
|
7712
|
-
if (!current) return null;
|
|
7713
|
-
const merged = { ...current, ...patch };
|
|
7714
|
-
writeAtomic(merged);
|
|
7715
|
-
return merged;
|
|
7716
|
-
}
|
|
7717
|
-
var ROOT, IDENTITY_FILE;
|
|
7718
|
-
var init_identity = __esm({
|
|
7719
|
-
"src/identity.ts"() {
|
|
7720
|
-
"use strict";
|
|
7721
|
-
ROOT = join3(homedir2(), ".modelstat");
|
|
7722
|
-
IDENTITY_FILE = join3(ROOT, "identity.json");
|
|
7723
|
-
}
|
|
7724
|
-
});
|
|
7725
|
-
|
|
7726
7661
|
// ../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js
|
|
7727
7662
|
var require_symbols = __commonJS({
|
|
7728
7663
|
"../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js"(exports, module) {
|
|
@@ -32614,6 +32549,9 @@ function classifyStatus(status2, attempt) {
|
|
|
32614
32549
|
}
|
|
32615
32550
|
return { type: "drop", reason: `http_${status2}` };
|
|
32616
32551
|
}
|
|
32552
|
+
function wellFormedStringify(value) {
|
|
32553
|
+
return JSON.stringify(value, (_key, v) => typeof v === "string" ? v.toWellFormed() : v);
|
|
32554
|
+
}
|
|
32617
32555
|
function sleep(ms) {
|
|
32618
32556
|
return new Promise((r) => {
|
|
32619
32557
|
setTimeout(r, ms);
|
|
@@ -32652,7 +32590,10 @@ var init_http = __esm({
|
|
|
32652
32590
|
"content-type": "application/json",
|
|
32653
32591
|
authorization: `Bearer ${token}`
|
|
32654
32592
|
},
|
|
32655
|
-
|
|
32593
|
+
// wellFormedStringify, not JSON.stringify: a truncated-emoji
|
|
32594
|
+
// lone surrogate in any excerpt 400s the whole batch on the
|
|
32595
|
+
// serde_json side. See the helper's doc comment.
|
|
32596
|
+
body: wellFormedStringify(batch)
|
|
32656
32597
|
});
|
|
32657
32598
|
} catch (err) {
|
|
32658
32599
|
const detail = describeErrorWithCause(err);
|
|
@@ -33301,15 +33242,15 @@ function envPaths(name, { suffix = "nodejs" } = {}) {
|
|
|
33301
33242
|
}
|
|
33302
33243
|
return linux(name);
|
|
33303
33244
|
}
|
|
33304
|
-
var
|
|
33245
|
+
var homedir2, tmpdir, env, macos, windows, linux;
|
|
33305
33246
|
var init_env_paths = __esm({
|
|
33306
33247
|
"../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js"() {
|
|
33307
33248
|
"use strict";
|
|
33308
|
-
|
|
33249
|
+
homedir2 = os.homedir();
|
|
33309
33250
|
tmpdir = os.tmpdir();
|
|
33310
33251
|
({ env } = process2);
|
|
33311
33252
|
macos = (name) => {
|
|
33312
|
-
const library = path.join(
|
|
33253
|
+
const library = path.join(homedir2, "Library");
|
|
33313
33254
|
return {
|
|
33314
33255
|
data: path.join(library, "Application Support", name),
|
|
33315
33256
|
config: path.join(library, "Preferences", name),
|
|
@@ -33319,8 +33260,8 @@ var init_env_paths = __esm({
|
|
|
33319
33260
|
};
|
|
33320
33261
|
};
|
|
33321
33262
|
windows = (name) => {
|
|
33322
|
-
const appData = env.APPDATA || path.join(
|
|
33323
|
-
const localAppData = env.LOCALAPPDATA || path.join(
|
|
33263
|
+
const appData = env.APPDATA || path.join(homedir2, "AppData", "Roaming");
|
|
33264
|
+
const localAppData = env.LOCALAPPDATA || path.join(homedir2, "AppData", "Local");
|
|
33324
33265
|
return {
|
|
33325
33266
|
// Data/config/cache/log are invented by me as Windows isn't opinionated about this
|
|
33326
33267
|
data: path.join(localAppData, name, "Data"),
|
|
@@ -33331,13 +33272,13 @@ var init_env_paths = __esm({
|
|
|
33331
33272
|
};
|
|
33332
33273
|
};
|
|
33333
33274
|
linux = (name) => {
|
|
33334
|
-
const username = path.basename(
|
|
33275
|
+
const username = path.basename(homedir2);
|
|
33335
33276
|
return {
|
|
33336
|
-
data: path.join(env.XDG_DATA_HOME || path.join(
|
|
33337
|
-
config: path.join(env.XDG_CONFIG_HOME || path.join(
|
|
33338
|
-
cache: path.join(env.XDG_CACHE_HOME || path.join(
|
|
33277
|
+
data: path.join(env.XDG_DATA_HOME || path.join(homedir2, ".local", "share"), name),
|
|
33278
|
+
config: path.join(env.XDG_CONFIG_HOME || path.join(homedir2, ".config"), name),
|
|
33279
|
+
cache: path.join(env.XDG_CACHE_HOME || path.join(homedir2, ".cache"), name),
|
|
33339
33280
|
// https://wiki.debian.org/XDGBaseDirectorySpecification#state
|
|
33340
|
-
log: path.join(env.XDG_STATE_HOME || path.join(
|
|
33281
|
+
log: path.join(env.XDG_STATE_HOME || path.join(homedir2, ".local", "state"), name),
|
|
33341
33282
|
temp: path.join(tmpdir, username, name)
|
|
33342
33283
|
};
|
|
33343
33284
|
};
|
|
@@ -33778,9 +33719,9 @@ import { once } from "events";
|
|
|
33778
33719
|
import { createWriteStream } from "fs";
|
|
33779
33720
|
import path3 from "path";
|
|
33780
33721
|
import { Readable } from "stream";
|
|
33781
|
-
function
|
|
33722
|
+
function writeFileSync(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
|
|
33782
33723
|
if (isString(options))
|
|
33783
|
-
return
|
|
33724
|
+
return writeFileSync(filePath, data, { encoding: options });
|
|
33784
33725
|
const timeout = options.timeout ?? DEFAULT_TIMEOUT_SYNC;
|
|
33785
33726
|
const retryOptions = { timeout };
|
|
33786
33727
|
let tempDisposer = null;
|
|
@@ -43903,7 +43844,7 @@ var init_source = __esm({
|
|
|
43903
43844
|
fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
43904
43845
|
} else {
|
|
43905
43846
|
try {
|
|
43906
|
-
|
|
43847
|
+
writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
43907
43848
|
} catch (error) {
|
|
43908
43849
|
if (error?.code === "EXDEV") {
|
|
43909
43850
|
fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
@@ -44004,6 +43945,107 @@ var init_source = __esm({
|
|
|
44004
43945
|
}
|
|
44005
43946
|
});
|
|
44006
43947
|
|
|
43948
|
+
// src/identity.ts
|
|
43949
|
+
import {
|
|
43950
|
+
chmodSync,
|
|
43951
|
+
mkdirSync,
|
|
43952
|
+
readFileSync as readFileSync2,
|
|
43953
|
+
renameSync,
|
|
43954
|
+
writeFileSync as writeFileSync2,
|
|
43955
|
+
existsSync as existsSync4
|
|
43956
|
+
} from "fs";
|
|
43957
|
+
import { homedir as homedir3, hostname as osHostname } from "os";
|
|
43958
|
+
import { join as join3 } from "path";
|
|
43959
|
+
function ensureRoot() {
|
|
43960
|
+
mkdirSync(ROOT, { recursive: true, mode: 448 });
|
|
43961
|
+
}
|
|
43962
|
+
function writeAtomic(meta) {
|
|
43963
|
+
ensureRoot();
|
|
43964
|
+
const tmp = `${IDENTITY_FILE}.${process.pid}.tmp`;
|
|
43965
|
+
writeFileSync2(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
43966
|
+
renameSync(tmp, IDENTITY_FILE);
|
|
43967
|
+
try {
|
|
43968
|
+
chmodSync(IDENTITY_FILE, 384);
|
|
43969
|
+
} catch {
|
|
43970
|
+
}
|
|
43971
|
+
}
|
|
43972
|
+
function identityPath() {
|
|
43973
|
+
return IDENTITY_FILE;
|
|
43974
|
+
}
|
|
43975
|
+
function hasIdentityFile() {
|
|
43976
|
+
return existsSync4(IDENTITY_FILE);
|
|
43977
|
+
}
|
|
43978
|
+
function parseFile() {
|
|
43979
|
+
try {
|
|
43980
|
+
const raw = readFileSync2(IDENTITY_FILE, "utf8");
|
|
43981
|
+
const obj = JSON.parse(raw);
|
|
43982
|
+
if (!obj.deviceUuid || !obj.deviceId || !obj.bearerToken) {
|
|
43983
|
+
return null;
|
|
43984
|
+
}
|
|
43985
|
+
return {
|
|
43986
|
+
deviceUuid: obj.deviceUuid,
|
|
43987
|
+
deviceId: obj.deviceId,
|
|
43988
|
+
bearerToken: obj.bearerToken,
|
|
43989
|
+
claimCode: obj.claimCode ?? null,
|
|
43990
|
+
claimUrl: obj.claimUrl ?? null,
|
|
43991
|
+
hostname: obj.hostname ?? osHostname(),
|
|
43992
|
+
createdAt: obj.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
43993
|
+
userEmail: obj.userEmail ?? null,
|
|
43994
|
+
defaultOrgId: obj.defaultOrgId ?? null
|
|
43995
|
+
};
|
|
43996
|
+
} catch {
|
|
43997
|
+
return null;
|
|
43998
|
+
}
|
|
43999
|
+
}
|
|
44000
|
+
function loadIdentity(migrateFromConf2) {
|
|
44001
|
+
const fromFile = parseFile();
|
|
44002
|
+
if (fromFile) return fromFile;
|
|
44003
|
+
if (!migrateFromConf2) return null;
|
|
44004
|
+
const legacy = migrateFromConf2();
|
|
44005
|
+
if (!legacy) return null;
|
|
44006
|
+
if (!legacy.deviceUuid || !legacy.deviceId || !legacy.bearerToken) {
|
|
44007
|
+
return null;
|
|
44008
|
+
}
|
|
44009
|
+
const migrated = {
|
|
44010
|
+
deviceUuid: legacy.deviceUuid,
|
|
44011
|
+
deviceId: legacy.deviceId,
|
|
44012
|
+
bearerToken: legacy.bearerToken,
|
|
44013
|
+
claimCode: legacy.claimCode ?? null,
|
|
44014
|
+
claimUrl: legacy.claimUrl ?? null,
|
|
44015
|
+
hostname: osHostname(),
|
|
44016
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
44017
|
+
userEmail: legacy.userEmail ?? null,
|
|
44018
|
+
defaultOrgId: legacy.defaultOrgId ?? null
|
|
44019
|
+
};
|
|
44020
|
+
writeAtomic(migrated);
|
|
44021
|
+
return migrated;
|
|
44022
|
+
}
|
|
44023
|
+
function saveIdentity(meta) {
|
|
44024
|
+
writeAtomic(meta);
|
|
44025
|
+
}
|
|
44026
|
+
function backupIdentity() {
|
|
44027
|
+
if (!existsSync4(IDENTITY_FILE)) return null;
|
|
44028
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
44029
|
+
const dest = `${IDENTITY_FILE}.bak-${stamp}`;
|
|
44030
|
+
renameSync(IDENTITY_FILE, dest);
|
|
44031
|
+
return dest;
|
|
44032
|
+
}
|
|
44033
|
+
function updateIdentity(patch) {
|
|
44034
|
+
const current = parseFile();
|
|
44035
|
+
if (!current) return null;
|
|
44036
|
+
const merged = { ...current, ...patch };
|
|
44037
|
+
writeAtomic(merged);
|
|
44038
|
+
return merged;
|
|
44039
|
+
}
|
|
44040
|
+
var ROOT, IDENTITY_FILE;
|
|
44041
|
+
var init_identity = __esm({
|
|
44042
|
+
"src/identity.ts"() {
|
|
44043
|
+
"use strict";
|
|
44044
|
+
ROOT = join3(homedir3(), ".modelstat");
|
|
44045
|
+
IDENTITY_FILE = join3(ROOT, "identity.json");
|
|
44046
|
+
}
|
|
44047
|
+
});
|
|
44048
|
+
|
|
44007
44049
|
// src/config.ts
|
|
44008
44050
|
import { existsSync as existsSync5 } from "fs";
|
|
44009
44051
|
import { hostname } from "os";
|
|
@@ -45322,6 +45364,21 @@ var init_llama = __esm({
|
|
|
45322
45364
|
}
|
|
45323
45365
|
});
|
|
45324
45366
|
|
|
45367
|
+
// ../../packages/companion-core/src/optional-module.ts
|
|
45368
|
+
function isMissingOptionalModuleError(err) {
|
|
45369
|
+
const code = err?.code;
|
|
45370
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
|
|
45371
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45372
|
+
return /cannot find (package|module)|cannot resolve|failed to resolve/i.test(msg);
|
|
45373
|
+
}
|
|
45374
|
+
var OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS;
|
|
45375
|
+
var init_optional_module = __esm({
|
|
45376
|
+
"../../packages/companion-core/src/optional-module.ts"() {
|
|
45377
|
+
"use strict";
|
|
45378
|
+
OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS = 3;
|
|
45379
|
+
}
|
|
45380
|
+
});
|
|
45381
|
+
|
|
45325
45382
|
// ../../packages/companion-core/src/node/transformersjs-embed.ts
|
|
45326
45383
|
async function loadPipeline(model) {
|
|
45327
45384
|
if (cached) return cached;
|
|
@@ -45329,11 +45386,7 @@ async function loadPipeline(model) {
|
|
|
45329
45386
|
if (!loadPromise2) {
|
|
45330
45387
|
loadPromise2 = (async () => {
|
|
45331
45388
|
try {
|
|
45332
|
-
const
|
|
45333
|
-
const tjs = await import(
|
|
45334
|
-
/* @vite-ignore */
|
|
45335
|
-
moduleId
|
|
45336
|
-
);
|
|
45389
|
+
const tjs = await importModule("@huggingface/transformers");
|
|
45337
45390
|
const p = await tjs.pipeline("feature-extraction", model, {
|
|
45338
45391
|
device: "cpu",
|
|
45339
45392
|
dtype: "fp32"
|
|
@@ -45342,12 +45395,16 @@ async function loadPipeline(model) {
|
|
|
45342
45395
|
return p;
|
|
45343
45396
|
} catch (err) {
|
|
45344
45397
|
const msg = err.message;
|
|
45345
|
-
|
|
45398
|
+
loadAttempts += 1;
|
|
45399
|
+
if (isMissingOptionalModuleError(err) || /unsupported|architecture|not supported|onnx/i.test(msg) || loadAttempts >= OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS) {
|
|
45346
45400
|
loadFailedPermanently = true;
|
|
45347
45401
|
}
|
|
45348
|
-
|
|
45349
|
-
|
|
45350
|
-
|
|
45402
|
+
if (!warnedUnavailable) {
|
|
45403
|
+
warnedUnavailable = true;
|
|
45404
|
+
console.warn(
|
|
45405
|
+
`[modelstat] transformers.js embedder unavailable (segments will be re-embedded server-side; further attempts ${loadFailedPermanently ? "disabled" : `limited to ${OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS}`}, warning once): ${msg}`
|
|
45406
|
+
);
|
|
45407
|
+
}
|
|
45351
45408
|
loadPromise2 = null;
|
|
45352
45409
|
return null;
|
|
45353
45410
|
}
|
|
@@ -45364,21 +45421,32 @@ function createTransformersJsEmbedder(model = DEFAULT_MODEL) {
|
|
|
45364
45421
|
const out = await pipe(text, { pooling: "mean", normalize: true });
|
|
45365
45422
|
return Array.from(out.data);
|
|
45366
45423
|
} catch (err) {
|
|
45367
|
-
|
|
45368
|
-
|
|
45369
|
-
|
|
45424
|
+
if (!warnedInferenceError) {
|
|
45425
|
+
warnedInferenceError = true;
|
|
45426
|
+
console.warn(
|
|
45427
|
+
`[modelstat] embed error (returning empty vectors, server will re-embed; warning once): ${err.message}`
|
|
45428
|
+
);
|
|
45429
|
+
}
|
|
45370
45430
|
return [];
|
|
45371
45431
|
}
|
|
45372
45432
|
};
|
|
45373
45433
|
}
|
|
45374
|
-
var cached, loadPromise2, loadFailedPermanently, DEFAULT_MODEL;
|
|
45434
|
+
var cached, loadPromise2, loadFailedPermanently, loadAttempts, warnedUnavailable, warnedInferenceError, DEFAULT_MODEL, importModule;
|
|
45375
45435
|
var init_transformersjs_embed = __esm({
|
|
45376
45436
|
"../../packages/companion-core/src/node/transformersjs-embed.ts"() {
|
|
45377
45437
|
"use strict";
|
|
45438
|
+
init_optional_module();
|
|
45378
45439
|
cached = null;
|
|
45379
45440
|
loadPromise2 = null;
|
|
45380
45441
|
loadFailedPermanently = false;
|
|
45442
|
+
loadAttempts = 0;
|
|
45443
|
+
warnedUnavailable = false;
|
|
45444
|
+
warnedInferenceError = false;
|
|
45381
45445
|
DEFAULT_MODEL = "Xenova/bge-small-en-v1.5";
|
|
45446
|
+
importModule = (id) => import(
|
|
45447
|
+
/* @vite-ignore */
|
|
45448
|
+
id
|
|
45449
|
+
);
|
|
45382
45450
|
}
|
|
45383
45451
|
});
|
|
45384
45452
|
|
|
@@ -45417,18 +45485,23 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45417
45485
|
const device = opts.device ?? (isBrowser ? "webgpu" : "cpu");
|
|
45418
45486
|
const dtype = opts.dtype ?? "q4";
|
|
45419
45487
|
const modelId = opts.model ?? "openai/privacy-filter";
|
|
45488
|
+
const importModule2 = opts.importModule ?? ((id) => import(
|
|
45489
|
+
/* @vite-ignore */
|
|
45490
|
+
id
|
|
45491
|
+
));
|
|
45420
45492
|
let cached2 = null;
|
|
45421
45493
|
let loadPromise3 = null;
|
|
45494
|
+
let loadFailedPermanently2 = false;
|
|
45495
|
+
let loadAttempts2 = 0;
|
|
45496
|
+
let warnedUnavailable2 = false;
|
|
45497
|
+
let warnedInferenceError2 = false;
|
|
45422
45498
|
async function loadPipeline2() {
|
|
45423
45499
|
if (cached2) return cached2;
|
|
45500
|
+
if (loadFailedPermanently2) return null;
|
|
45424
45501
|
if (!loadPromise3) {
|
|
45425
45502
|
loadPromise3 = (async () => {
|
|
45426
45503
|
try {
|
|
45427
|
-
const
|
|
45428
|
-
const tjs = await import(
|
|
45429
|
-
/* @vite-ignore */
|
|
45430
|
-
moduleId
|
|
45431
|
-
);
|
|
45504
|
+
const tjs = await importModule2("@huggingface/transformers");
|
|
45432
45505
|
const p = await tjs.pipeline("token-classification", modelId, {
|
|
45433
45506
|
device,
|
|
45434
45507
|
dtype,
|
|
@@ -45438,10 +45511,17 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45438
45511
|
return p;
|
|
45439
45512
|
} catch (err) {
|
|
45440
45513
|
loadPromise3 = null;
|
|
45441
|
-
|
|
45442
|
-
|
|
45443
|
-
|
|
45444
|
-
|
|
45514
|
+
loadAttempts2 += 1;
|
|
45515
|
+
if (isMissingOptionalModuleError(err) || loadAttempts2 >= OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS) {
|
|
45516
|
+
loadFailedPermanently2 = true;
|
|
45517
|
+
}
|
|
45518
|
+
if (!warnedUnavailable2) {
|
|
45519
|
+
warnedUnavailable2 = true;
|
|
45520
|
+
console.warn(
|
|
45521
|
+
"[privacy-filter] adapter unavailable \u2014 install @huggingface/transformers in the consuming package to enable model-based redaction. Falling back to pass-through (warning once).",
|
|
45522
|
+
err.message
|
|
45523
|
+
);
|
|
45524
|
+
}
|
|
45445
45525
|
return null;
|
|
45446
45526
|
}
|
|
45447
45527
|
})();
|
|
@@ -45464,10 +45544,13 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45464
45544
|
try {
|
|
45465
45545
|
tokens = await classify(text);
|
|
45466
45546
|
} catch (err) {
|
|
45467
|
-
|
|
45468
|
-
|
|
45469
|
-
|
|
45470
|
-
|
|
45547
|
+
if (!warnedInferenceError2) {
|
|
45548
|
+
warnedInferenceError2 = true;
|
|
45549
|
+
console.warn(
|
|
45550
|
+
"[privacy-filter] inference failed, returning input unchanged (warning once):",
|
|
45551
|
+
err.message
|
|
45552
|
+
);
|
|
45553
|
+
}
|
|
45471
45554
|
return empty;
|
|
45472
45555
|
}
|
|
45473
45556
|
const spans = [];
|
|
@@ -45504,6 +45587,7 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45504
45587
|
var init_privacy_filter = __esm({
|
|
45505
45588
|
"../../packages/companion-core/src/redact/privacy-filter.ts"() {
|
|
45506
45589
|
"use strict";
|
|
45590
|
+
init_optional_module();
|
|
45507
45591
|
}
|
|
45508
45592
|
});
|
|
45509
45593
|
|
|
@@ -45613,9 +45697,8 @@ async function scanAll(cb = {}) {
|
|
|
45613
45697
|
const full = join5(dir, f);
|
|
45614
45698
|
jobs.push({
|
|
45615
45699
|
path: full,
|
|
45616
|
-
parse: async () => {
|
|
45617
|
-
|
|
45618
|
-
return { events: r.events, path: full };
|
|
45700
|
+
parse: async (sink2) => {
|
|
45701
|
+
await parseClaudeCodeJsonl({ deviceId, sourceFile: full, onEvents: sink2 });
|
|
45619
45702
|
}
|
|
45620
45703
|
});
|
|
45621
45704
|
}
|
|
@@ -45637,9 +45720,8 @@ async function scanAll(cb = {}) {
|
|
|
45637
45720
|
const full = join5(base, y, m, d, f);
|
|
45638
45721
|
jobs.push({
|
|
45639
45722
|
path: full,
|
|
45640
|
-
parse: async () => {
|
|
45641
|
-
|
|
45642
|
-
return { events: r.events, path: full };
|
|
45723
|
+
parse: async (sink2) => {
|
|
45724
|
+
await parseCodexRollout({ deviceId, sourceFile: full, onEvents: sink2 });
|
|
45643
45725
|
}
|
|
45644
45726
|
});
|
|
45645
45727
|
}
|
|
@@ -45694,6 +45776,17 @@ async function scanAll(cb = {}) {
|
|
|
45694
45776
|
buffer = [];
|
|
45695
45777
|
cb.onUploaded?.({ events: res.accepted, segments: segments.length });
|
|
45696
45778
|
}
|
|
45779
|
+
const sink = async (events) => {
|
|
45780
|
+
for (const e of events) {
|
|
45781
|
+
buffer.push(e);
|
|
45782
|
+
if (buffer.length >= BATCH_MAX_EVENTS) await flushBatch();
|
|
45783
|
+
}
|
|
45784
|
+
if (buffer.length > BATCH_BUFFER_HARD_CAP) {
|
|
45785
|
+
throw new Error(
|
|
45786
|
+
`scan event buffer exceeded ${BATCH_BUFFER_HARD_CAP} events \u2014 incremental batch flushing has regressed (see BATCH_BUFFER_HARD_CAP)`
|
|
45787
|
+
);
|
|
45788
|
+
}
|
|
45789
|
+
};
|
|
45697
45790
|
for (let i = 0; i < jobs.length; i++) {
|
|
45698
45791
|
const job = jobs[i];
|
|
45699
45792
|
cb.onFile?.(job.path, i, jobs.length);
|
|
@@ -45705,13 +45798,7 @@ async function scanAll(cb = {}) {
|
|
|
45705
45798
|
}
|
|
45706
45799
|
filesScanned += 1;
|
|
45707
45800
|
try {
|
|
45708
|
-
|
|
45709
|
-
if (r.events.length) {
|
|
45710
|
-
for (const e of r.events) {
|
|
45711
|
-
buffer.push(e);
|
|
45712
|
-
if (buffer.length >= BATCH_MAX_EVENTS) await flushBatch();
|
|
45713
|
-
}
|
|
45714
|
-
}
|
|
45801
|
+
await job.parse(sink);
|
|
45715
45802
|
if (cs) pendingCursors.push({ path: job.path, cs });
|
|
45716
45803
|
} catch (e) {
|
|
45717
45804
|
console.warn(` ! parse failed for ${job.path}:`, e.message);
|
|
@@ -45720,7 +45807,7 @@ async function scanAll(cb = {}) {
|
|
|
45720
45807
|
await flushBatch();
|
|
45721
45808
|
return { filesScanned, filesUnchanged, batchesUploaded, eventsUploaded, segmentsUploaded };
|
|
45722
45809
|
}
|
|
45723
|
-
var AGENT_VERSION, BATCH_MAX_EVENTS, ZERO_TOKENS;
|
|
45810
|
+
var AGENT_VERSION, BATCH_MAX_EVENTS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
|
|
45724
45811
|
var init_scan = __esm({
|
|
45725
45812
|
"src/scan.ts"() {
|
|
45726
45813
|
"use strict";
|
|
@@ -45729,8 +45816,9 @@ var init_scan = __esm({
|
|
|
45729
45816
|
init_pipeline2();
|
|
45730
45817
|
init_config2();
|
|
45731
45818
|
init_api();
|
|
45732
|
-
AGENT_VERSION = true ? "agent-0.0.
|
|
45819
|
+
AGENT_VERSION = true ? "agent-0.0.46" : "agent-dev";
|
|
45733
45820
|
BATCH_MAX_EVENTS = 2e3;
|
|
45821
|
+
BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
|
|
45734
45822
|
ZERO_TOKENS = {
|
|
45735
45823
|
input: 0,
|
|
45736
45824
|
output: 0,
|
|
@@ -45766,9 +45854,9 @@ function isProcessAlive(pid) {
|
|
|
45766
45854
|
return false;
|
|
45767
45855
|
}
|
|
45768
45856
|
}
|
|
45769
|
-
function
|
|
45857
|
+
function readDaemonLock(lockFile = LOCK_FILE) {
|
|
45770
45858
|
try {
|
|
45771
|
-
const raw = readFileSync4(
|
|
45859
|
+
const raw = readFileSync4(lockFile, "utf8");
|
|
45772
45860
|
const obj = JSON.parse(raw);
|
|
45773
45861
|
if (typeof obj.pid !== "number") return null;
|
|
45774
45862
|
return {
|
|
@@ -45793,7 +45881,7 @@ function writeLockAtomic(meta) {
|
|
|
45793
45881
|
renameSync2(tmp, LOCK_FILE);
|
|
45794
45882
|
}
|
|
45795
45883
|
function removeLockIfOwned(ownerPid) {
|
|
45796
|
-
const lock =
|
|
45884
|
+
const lock = readDaemonLock();
|
|
45797
45885
|
if (!lock) return;
|
|
45798
45886
|
if (lock.pid !== ownerPid) return;
|
|
45799
45887
|
try {
|
|
@@ -45802,7 +45890,7 @@ function removeLockIfOwned(ownerPid) {
|
|
|
45802
45890
|
}
|
|
45803
45891
|
}
|
|
45804
45892
|
function acquireDaemonLock(opts) {
|
|
45805
|
-
const existing =
|
|
45893
|
+
const existing = readDaemonLock();
|
|
45806
45894
|
if (existing && isProcessAlive(existing.pid)) {
|
|
45807
45895
|
if (!opts.force) {
|
|
45808
45896
|
const ageSec = ageInSeconds(existing.startedAt);
|
|
@@ -45820,6 +45908,20 @@ function acquireDaemonLock(opts) {
|
|
|
45820
45908
|
apiUrl: opts.apiUrl
|
|
45821
45909
|
};
|
|
45822
45910
|
writeLockAtomic(meta);
|
|
45911
|
+
const recheck = setTimeout(() => {
|
|
45912
|
+
const current = readDaemonLock();
|
|
45913
|
+
if (checkLockOwnership(process.pid, current) !== "lost") return;
|
|
45914
|
+
const winner = current;
|
|
45915
|
+
if (opts.onLockLost) {
|
|
45916
|
+
opts.onLockLost(winner);
|
|
45917
|
+
return;
|
|
45918
|
+
}
|
|
45919
|
+
console.log(
|
|
45920
|
+
`[modelstat] another daemon (pid ${winner.pid}, started ${winner.startedAt}) won the lock race \u2014 this instance is standing down.`
|
|
45921
|
+
);
|
|
45922
|
+
process.exit(0);
|
|
45923
|
+
}, LOCK_RECHECK_MS);
|
|
45924
|
+
recheck.unref();
|
|
45823
45925
|
const cleanup = () => removeLockIfOwned(process.pid);
|
|
45824
45926
|
process.once("beforeExit", cleanup);
|
|
45825
45927
|
process.once("SIGINT", () => {
|
|
@@ -45837,6 +45939,11 @@ function acquireDaemonLock(opts) {
|
|
|
45837
45939
|
});
|
|
45838
45940
|
return { kind: "acquired" };
|
|
45839
45941
|
}
|
|
45942
|
+
function checkLockOwnership(myPid, current, pidAlive = isProcessAlive) {
|
|
45943
|
+
if (!current || current.pid === myPid) return "owned";
|
|
45944
|
+
if (!pidAlive(current.pid)) return "winner_dead";
|
|
45945
|
+
return "lost";
|
|
45946
|
+
}
|
|
45840
45947
|
function ageInSeconds(iso) {
|
|
45841
45948
|
const t = Date.parse(iso);
|
|
45842
45949
|
if (Number.isNaN(t)) return -1;
|
|
@@ -45851,12 +45958,13 @@ function formatAge(seconds) {
|
|
|
45851
45958
|
const h = Math.floor(m / 60);
|
|
45852
45959
|
return `${h}h ${m % 60}m`;
|
|
45853
45960
|
}
|
|
45854
|
-
var LOCK_DIR, LOCK_FILE;
|
|
45961
|
+
var LOCK_DIR, LOCK_FILE, LOCK_RECHECK_MS;
|
|
45855
45962
|
var init_lock = __esm({
|
|
45856
45963
|
"src/lock.ts"() {
|
|
45857
45964
|
"use strict";
|
|
45858
45965
|
LOCK_DIR = join7(homedir7(), ".modelstat");
|
|
45859
45966
|
LOCK_FILE = join7(LOCK_DIR, "daemon.lock");
|
|
45967
|
+
LOCK_RECHECK_MS = 5e3;
|
|
45860
45968
|
}
|
|
45861
45969
|
});
|
|
45862
45970
|
|
|
@@ -47730,17 +47838,44 @@ async function sendHeartbeat() {
|
|
|
47730
47838
|
}
|
|
47731
47839
|
writeLocalStatus(body).catch(() => void 0);
|
|
47732
47840
|
}
|
|
47841
|
+
async function rotateRunawayLogs() {
|
|
47842
|
+
const { homedir: homedir10 } = await import("os");
|
|
47843
|
+
const { join: join12 } = await import("path");
|
|
47844
|
+
const { open: open2, stat: stat6, truncate, writeFile } = await import("fs/promises");
|
|
47845
|
+
const dir = join12(homedir10(), ".modelstat", "logs");
|
|
47846
|
+
for (const name of ["out.log", "err.log"]) {
|
|
47847
|
+
const p = join12(dir, name);
|
|
47848
|
+
try {
|
|
47849
|
+
const st = await stat6(p);
|
|
47850
|
+
if (st.size <= LOG_MAX_BYTES) continue;
|
|
47851
|
+
const keep = Math.min(LOG_TAIL_KEEP_BYTES, st.size);
|
|
47852
|
+
const fh = await open2(p, "r");
|
|
47853
|
+
try {
|
|
47854
|
+
const buf = Buffer.alloc(keep);
|
|
47855
|
+
await fh.read(buf, 0, keep, st.size - keep);
|
|
47856
|
+
await writeFile(p.replace(/\.log$/, ".old.log"), buf);
|
|
47857
|
+
} finally {
|
|
47858
|
+
await fh.close();
|
|
47859
|
+
}
|
|
47860
|
+
await truncate(p, 0);
|
|
47861
|
+
console.log(
|
|
47862
|
+
`[modelstat] rotated ${name}: was ${(st.size / 1024 / 1024).toFixed(0)} MB (> ${LOG_MAX_BYTES / 1024 / 1024} MB cap), tail kept in ${name.replace(/\.log$/, ".old.log")}`
|
|
47863
|
+
);
|
|
47864
|
+
} catch {
|
|
47865
|
+
}
|
|
47866
|
+
}
|
|
47867
|
+
}
|
|
47733
47868
|
async function writeLocalStatus(snapshot) {
|
|
47734
|
-
const { homedir:
|
|
47735
|
-
const { join:
|
|
47869
|
+
const { homedir: homedir10 } = await import("os");
|
|
47870
|
+
const { join: join12 } = await import("path");
|
|
47736
47871
|
const { writeFile, mkdir: mkdir2, rename } = await import("fs/promises");
|
|
47737
47872
|
if (!lastStatusPath) {
|
|
47738
|
-
const dir =
|
|
47873
|
+
const dir = join12(homedir10(), ".modelstat");
|
|
47739
47874
|
try {
|
|
47740
47875
|
await mkdir2(dir, { recursive: true });
|
|
47741
47876
|
} catch {
|
|
47742
47877
|
}
|
|
47743
|
-
lastStatusPath =
|
|
47878
|
+
lastStatusPath = join12(dir, "last-status.json");
|
|
47744
47879
|
}
|
|
47745
47880
|
const tmp = `${lastStatusPath}.tmp`;
|
|
47746
47881
|
try {
|
|
@@ -47822,7 +47957,17 @@ async function runDaemon(opts = {}) {
|
|
|
47822
47957
|
const lock = acquireDaemonLock({
|
|
47823
47958
|
agentVersion: AGENT_VERSION2,
|
|
47824
47959
|
apiUrl: state.apiUrl,
|
|
47825
|
-
force: opts.force === true
|
|
47960
|
+
force: opts.force === true,
|
|
47961
|
+
// If a racing daemon out-renamed us for the lock (see lock.ts
|
|
47962
|
+
// recheck), stand down with exit 0: our supervisor's next health
|
|
47963
|
+
// check sees the winner and adopts it instead of respawning us.
|
|
47964
|
+
onLockLost: (winner) => {
|
|
47965
|
+
setPhase("offline", `another daemon (pid ${winner.pid}) owns the lock \u2014 standing down`);
|
|
47966
|
+
console.log(
|
|
47967
|
+
`[modelstat] another daemon (pid ${winner.pid}, started ${winner.startedAt}) won the lock race \u2014 this instance is standing down.`
|
|
47968
|
+
);
|
|
47969
|
+
process.exit(0);
|
|
47970
|
+
}
|
|
47826
47971
|
});
|
|
47827
47972
|
if (lock.kind === "already_running") {
|
|
47828
47973
|
console.log(
|
|
@@ -47839,6 +47984,7 @@ async function runDaemon(opts = {}) {
|
|
|
47839
47984
|
return;
|
|
47840
47985
|
}
|
|
47841
47986
|
setPhase("starting", "Booting");
|
|
47987
|
+
await rotateRunawayLogs();
|
|
47842
47988
|
setStat("segments_sent", state.segmentsSent);
|
|
47843
47989
|
const hb = setInterval(() => void sendHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
47844
47990
|
hb.unref();
|
|
@@ -47862,19 +48008,19 @@ async function runDaemon(opts = {}) {
|
|
|
47862
48008
|
await runDiscovery();
|
|
47863
48009
|
await requestScan("startup");
|
|
47864
48010
|
const chokidar = (await Promise.resolve().then(() => (init_esm2(), esm_exports))).default;
|
|
47865
|
-
const { homedir:
|
|
47866
|
-
const { join:
|
|
47867
|
-
const home2 =
|
|
48011
|
+
const { homedir: homedir10, platform: platform5 } = await import("os");
|
|
48012
|
+
const { join: join12 } = await import("path");
|
|
48013
|
+
const home2 = homedir10();
|
|
47868
48014
|
const dirs = [
|
|
47869
|
-
|
|
47870
|
-
|
|
47871
|
-
|
|
47872
|
-
|
|
48015
|
+
join12(home2, ".claude/projects"),
|
|
48016
|
+
join12(home2, ".codex/sessions"),
|
|
48017
|
+
join12(home2, ".cursor/ai-tracking"),
|
|
48018
|
+
join12(home2, ".gemini"),
|
|
47873
48019
|
...platform5() === "darwin" ? [
|
|
47874
|
-
|
|
47875
|
-
|
|
48020
|
+
join12(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
|
|
48021
|
+
join12(home2, "Library/Application Support/Claude")
|
|
47876
48022
|
] : [
|
|
47877
|
-
|
|
48023
|
+
join12(home2, ".config/Cursor/User/workspaceStorage")
|
|
47878
48024
|
]
|
|
47879
48025
|
].filter((p) => existsSync9(p) && statSync2(p).isDirectory());
|
|
47880
48026
|
setPhase("watching", `Watching ${dirs.length} directories`);
|
|
@@ -47917,7 +48063,7 @@ async function runDaemon(opts = {}) {
|
|
|
47917
48063
|
await new Promise(() => {
|
|
47918
48064
|
});
|
|
47919
48065
|
}
|
|
47920
|
-
var import_undici2, AGENT_VERSION2, HEARTBEAT_INTERVAL_MS, SCAN_INTERVAL_MS, DISCOVERY_INTERVAL_MS, status, LOCAL_FLUSH_THROTTLE_MS, localFlushTimer, localFlushPending, lastStatusPath, scanRunner;
|
|
48066
|
+
var import_undici2, AGENT_VERSION2, HEARTBEAT_INTERVAL_MS, SCAN_INTERVAL_MS, DISCOVERY_INTERVAL_MS, status, LOCAL_FLUSH_THROTTLE_MS, localFlushTimer, localFlushPending, LOG_MAX_BYTES, LOG_TAIL_KEEP_BYTES, lastStatusPath, scanRunner;
|
|
47921
48067
|
var init_daemon = __esm({
|
|
47922
48068
|
"src/daemon.ts"() {
|
|
47923
48069
|
"use strict";
|
|
@@ -47929,7 +48075,7 @@ var init_daemon = __esm({
|
|
|
47929
48075
|
init_lock();
|
|
47930
48076
|
init_scan();
|
|
47931
48077
|
init_single_flight();
|
|
47932
|
-
AGENT_VERSION2 = true ? "agent-0.0.
|
|
48078
|
+
AGENT_VERSION2 = true ? "agent-0.0.46" : "agent-dev";
|
|
47933
48079
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
47934
48080
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
47935
48081
|
DISCOVERY_INTERVAL_MS = 6e4;
|
|
@@ -47945,6 +48091,8 @@ var init_daemon = __esm({
|
|
|
47945
48091
|
LOCAL_FLUSH_THROTTLE_MS = 400;
|
|
47946
48092
|
localFlushTimer = null;
|
|
47947
48093
|
localFlushPending = false;
|
|
48094
|
+
LOG_MAX_BYTES = 64 * 1024 * 1024;
|
|
48095
|
+
LOG_TAIL_KEEP_BYTES = 4 * 1024 * 1024;
|
|
47948
48096
|
lastStatusPath = null;
|
|
47949
48097
|
scanRunner = createCoalescingRunner(runScanCycle);
|
|
47950
48098
|
}
|
|
@@ -47956,33 +48104,33 @@ __export(watch_exports, {
|
|
|
47956
48104
|
watchForever: () => watchForever
|
|
47957
48105
|
});
|
|
47958
48106
|
import { existsSync as existsSync10 } from "fs";
|
|
47959
|
-
import { homedir as
|
|
47960
|
-
import { join as
|
|
48107
|
+
import { homedir as homedir9, platform as platform3 } from "os";
|
|
48108
|
+
import { join as join11 } from "path";
|
|
47961
48109
|
function resolveWatchDirs() {
|
|
47962
|
-
const home2 =
|
|
47963
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
47964
|
-
const xdgData = process.env.XDG_DATA_HOME ??
|
|
48110
|
+
const home2 = homedir9();
|
|
48111
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join11(home2, ".config");
|
|
48112
|
+
const xdgData = process.env.XDG_DATA_HOME ?? join11(home2, ".local/share");
|
|
47965
48113
|
const candidates = [
|
|
47966
48114
|
// universal (default HOME-rooted CLI data dirs)
|
|
47967
|
-
|
|
47968
|
-
|
|
47969
|
-
|
|
47970
|
-
|
|
47971
|
-
|
|
48115
|
+
join11(home2, ".claude/projects"),
|
|
48116
|
+
join11(home2, ".codex/sessions"),
|
|
48117
|
+
join11(home2, ".cursor/ai-tracking"),
|
|
48118
|
+
join11(home2, ".gemini"),
|
|
48119
|
+
join11(home2, ".aider"),
|
|
47972
48120
|
// XDG / Linux
|
|
47973
|
-
|
|
47974
|
-
|
|
47975
|
-
|
|
47976
|
-
|
|
47977
|
-
|
|
47978
|
-
|
|
48121
|
+
join11(xdgConfig, "claude/projects"),
|
|
48122
|
+
join11(xdgConfig, "codex/sessions"),
|
|
48123
|
+
join11(xdgConfig, "Cursor/User/workspaceStorage"),
|
|
48124
|
+
join11(xdgConfig, "Code/User/workspaceStorage"),
|
|
48125
|
+
join11(xdgConfig, "Code - Insiders/User/workspaceStorage"),
|
|
48126
|
+
join11(xdgData, "claude/projects"),
|
|
47979
48127
|
// macOS
|
|
47980
48128
|
...platform3() === "darwin" ? [
|
|
47981
|
-
|
|
47982
|
-
|
|
47983
|
-
|
|
47984
|
-
|
|
47985
|
-
|
|
48129
|
+
join11(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
|
|
48130
|
+
join11(home2, "Library/Application Support/Claude"),
|
|
48131
|
+
join11(home2, "Library/Application Support/Code/User/workspaceStorage"),
|
|
48132
|
+
join11(home2, "Library/Application Support/Windsurf/User/workspaceStorage"),
|
|
48133
|
+
join11(home2, "Library/Application Support/Zed")
|
|
47986
48134
|
] : []
|
|
47987
48135
|
];
|
|
47988
48136
|
return Array.from(new Set(candidates)).filter((p) => existsSync10(p));
|
|
@@ -48047,13 +48195,13 @@ var init_watch = __esm({
|
|
|
48047
48195
|
|
|
48048
48196
|
// src/cli.ts
|
|
48049
48197
|
init_src2();
|
|
48050
|
-
init_identity();
|
|
48051
48198
|
init_api();
|
|
48052
48199
|
init_config2();
|
|
48200
|
+
init_identity();
|
|
48053
48201
|
init_scan();
|
|
48054
48202
|
import { spawn } from "child_process";
|
|
48055
48203
|
import { randomBytes } from "crypto";
|
|
48056
|
-
import {
|
|
48204
|
+
import { arch as cpuArch, hostname as hostname2, platform as platform4, release } from "os";
|
|
48057
48205
|
import { createInterface as createInterface3 } from "readline";
|
|
48058
48206
|
|
|
48059
48207
|
// src/service.ts
|
|
@@ -48409,6 +48557,59 @@ function trayStatus() {
|
|
|
48409
48557
|
return exe ? { installed: true, path: exe.replace(/\/Contents\/MacOS\/modelstat-tray$/, "") } : { installed: false, path: null };
|
|
48410
48558
|
}
|
|
48411
48559
|
|
|
48560
|
+
// src/supervise.ts
|
|
48561
|
+
init_lock();
|
|
48562
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
48563
|
+
import { homedir as homedir8 } from "os";
|
|
48564
|
+
import { join as join8 } from "path";
|
|
48565
|
+
var STATUS_FRESH_MS = 12e4;
|
|
48566
|
+
var BOOT_GRACE_MS = 9e4;
|
|
48567
|
+
function decideSupervision(input) {
|
|
48568
|
+
const fresh = input.statusFreshMs ?? STATUS_FRESH_MS;
|
|
48569
|
+
const grace = input.bootGraceMs ?? BOOT_GRACE_MS;
|
|
48570
|
+
if (!input.lock || !input.ownerAlive) return "spawn";
|
|
48571
|
+
if (input.myAgentVersion && input.lock.agentVersion !== "unknown" && input.lock.agentVersion !== input.myAgentVersion) {
|
|
48572
|
+
return "replace";
|
|
48573
|
+
}
|
|
48574
|
+
if (input.statusAgeMs !== null && input.statusAgeMs <= fresh) return "adopt";
|
|
48575
|
+
if (input.lockAgeMs !== null && input.lockAgeMs <= grace) return "adopt";
|
|
48576
|
+
return "replace";
|
|
48577
|
+
}
|
|
48578
|
+
function daemonHealth(opts = {}) {
|
|
48579
|
+
const now = opts.now ?? Date.now();
|
|
48580
|
+
const pidAlive = opts.pidAlive ?? isProcessAlive;
|
|
48581
|
+
const statusPath = opts.statusPath ?? join8(homedir8(), ".modelstat", "last-status.json");
|
|
48582
|
+
const lock = opts.lockPath === void 0 ? readDaemonLock() : readDaemonLock(opts.lockPath);
|
|
48583
|
+
const ownerAlive = lock !== null && pidAlive(lock.pid);
|
|
48584
|
+
const lockAgeMs = lock ? ageMs(lock.startedAt, now) : null;
|
|
48585
|
+
let statusAgeMs = null;
|
|
48586
|
+
try {
|
|
48587
|
+
const raw = readFileSync5(statusPath, "utf8");
|
|
48588
|
+
const writtenAt = JSON.parse(raw).written_at;
|
|
48589
|
+
statusAgeMs = writtenAt ? ageMs(writtenAt, now) : null;
|
|
48590
|
+
} catch {
|
|
48591
|
+
statusAgeMs = null;
|
|
48592
|
+
}
|
|
48593
|
+
return {
|
|
48594
|
+
decision: decideSupervision({
|
|
48595
|
+
lock,
|
|
48596
|
+
ownerAlive,
|
|
48597
|
+
lockAgeMs,
|
|
48598
|
+
statusAgeMs,
|
|
48599
|
+
myAgentVersion: opts.myAgentVersion
|
|
48600
|
+
}),
|
|
48601
|
+
lock,
|
|
48602
|
+
ownerAlive,
|
|
48603
|
+
lockAgeMs,
|
|
48604
|
+
statusAgeMs
|
|
48605
|
+
};
|
|
48606
|
+
}
|
|
48607
|
+
function ageMs(iso, now) {
|
|
48608
|
+
const t = Date.parse(iso);
|
|
48609
|
+
if (Number.isNaN(t)) return null;
|
|
48610
|
+
return Math.max(0, now - t);
|
|
48611
|
+
}
|
|
48612
|
+
|
|
48412
48613
|
// src/cli.ts
|
|
48413
48614
|
async function confirmPrompt(question, defaultYes) {
|
|
48414
48615
|
if (process.stdin.isTTY !== true) return defaultYes;
|
|
@@ -48439,7 +48640,7 @@ function tryOpenBrowser(url) {
|
|
|
48439
48640
|
return false;
|
|
48440
48641
|
}
|
|
48441
48642
|
}
|
|
48442
|
-
var AGENT_VERSION3 = true ? "agent-0.0.
|
|
48643
|
+
var AGENT_VERSION3 = true ? "agent-0.0.46" : "agent-dev";
|
|
48443
48644
|
function osFamily() {
|
|
48444
48645
|
const p = platform4();
|
|
48445
48646
|
if (p === "darwin") return "macos";
|
|
@@ -48482,10 +48683,8 @@ async function cmdSelfRegister() {
|
|
|
48482
48683
|
process.stdout.write(` \x1B[2mgenerated UUIDv7 ${deviceUuid.slice(0, 8)}\u2026\x1B[0m
|
|
48483
48684
|
`);
|
|
48484
48685
|
}
|
|
48485
|
-
process.stdout.write(
|
|
48486
|
-
|
|
48487
|
-
`
|
|
48488
|
-
);
|
|
48686
|
+
process.stdout.write(` \x1B[2m\u2192 POST ${state.apiUrl}/v1/devices/self-register\x1B[0m
|
|
48687
|
+
`);
|
|
48489
48688
|
const res = await selfRegister({
|
|
48490
48689
|
device_uuid: deviceUuid,
|
|
48491
48690
|
fingerprint
|
|
@@ -48499,8 +48698,10 @@ async function cmdSelfRegister() {
|
|
|
48499
48698
|
});
|
|
48500
48699
|
process.stdout.write(` \x1B[32m\u2713\x1B[0m registered device_id=${res.device_id}
|
|
48501
48700
|
`);
|
|
48502
|
-
process.stdout.write(
|
|
48503
|
-
`)
|
|
48701
|
+
process.stdout.write(
|
|
48702
|
+
` \x1B[32m\u2713\x1B[0m secret ${res.secret_prefix}\u2026 (hashed on server, never re-sent)
|
|
48703
|
+
`
|
|
48704
|
+
);
|
|
48504
48705
|
process.stdout.write(` \x1B[32m\u2713\x1B[0m claim code ${res.claim_code}
|
|
48505
48706
|
`);
|
|
48506
48707
|
}
|
|
@@ -48533,10 +48734,8 @@ async function cmdAwaitClaim() {
|
|
|
48533
48734
|
}
|
|
48534
48735
|
function emitEvent(opts, event, fields = {}) {
|
|
48535
48736
|
if (!opts.json) return;
|
|
48536
|
-
process.stdout.write(
|
|
48537
|
-
|
|
48538
|
-
`
|
|
48539
|
-
);
|
|
48737
|
+
process.stdout.write(`${JSON.stringify({ v: 1, ts: Date.now(), event, ...fields })}
|
|
48738
|
+
`);
|
|
48540
48739
|
}
|
|
48541
48740
|
async function cmdConnect(opts) {
|
|
48542
48741
|
const step = (msg) => {
|
|
@@ -48639,9 +48838,7 @@ async function cmdConnect(opts) {
|
|
|
48639
48838
|
error: e.message
|
|
48640
48839
|
});
|
|
48641
48840
|
warn(`couldn't prepare summariser model: ${e.message}`);
|
|
48642
|
-
warn(
|
|
48643
|
-
"the background service will retry the download on its first scan"
|
|
48644
|
-
);
|
|
48841
|
+
warn("the background service will retry the download on its first scan");
|
|
48645
48842
|
}
|
|
48646
48843
|
step("Installing/refreshing background service so the agent survives reboots");
|
|
48647
48844
|
let serviceOk = false;
|
|
@@ -48657,9 +48854,7 @@ async function cmdConnect(opts) {
|
|
|
48657
48854
|
} catch (e) {
|
|
48658
48855
|
emitEvent(opts, "service_install_failed", { error: e.message });
|
|
48659
48856
|
warn(`couldn't install service: ${e.message}`);
|
|
48660
|
-
warn(
|
|
48661
|
-
"the agent will not run in the background \u2014 re-run after fixing the issue"
|
|
48662
|
-
);
|
|
48857
|
+
warn("the agent will not run in the background \u2014 re-run after fixing the issue");
|
|
48663
48858
|
}
|
|
48664
48859
|
step("Detecting installed AI tools and signed-in accounts");
|
|
48665
48860
|
let discovered = null;
|
|
@@ -48677,9 +48872,7 @@ async function cmdConnect(opts) {
|
|
|
48677
48872
|
identities: d.identities.length
|
|
48678
48873
|
};
|
|
48679
48874
|
emitEvent(opts, "discovered", discovered);
|
|
48680
|
-
ok(
|
|
48681
|
-
`${discovered.installations} installs \xB7 ${discovered.identities} accounts`
|
|
48682
|
-
);
|
|
48875
|
+
ok(`${discovered.installations} installs \xB7 ${discovered.identities} accounts`);
|
|
48683
48876
|
} catch (e) {
|
|
48684
48877
|
emitEvent(opts, "discovery_failed", { error: e.message });
|
|
48685
48878
|
warn(`couldn't detect accounts: ${e.message}`);
|
|
@@ -48775,9 +48968,7 @@ async function cmdRescan() {
|
|
|
48775
48968
|
console.log(
|
|
48776
48969
|
`[modelstat] cursors wiped \u2014 next \`modelstat scan\` (or daemon scan cycle) will re-read every JSONL from the start and re-summarise every session at processing version v${PROCESSING_VERSION2}.`
|
|
48777
48970
|
);
|
|
48778
|
-
console.log(
|
|
48779
|
-
" If the daemon is running, kick it with: modelstat stop && modelstat start"
|
|
48780
|
-
);
|
|
48971
|
+
console.log(" If the daemon is running, kick it with: modelstat stop && modelstat start");
|
|
48781
48972
|
}
|
|
48782
48973
|
async function cmdWatch() {
|
|
48783
48974
|
const { watchForever: watchForever2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
|
|
@@ -48833,10 +49024,10 @@ function fmtTokens(v) {
|
|
|
48833
49024
|
}
|
|
48834
49025
|
async function readLocalStatus() {
|
|
48835
49026
|
try {
|
|
48836
|
-
const { homedir:
|
|
48837
|
-
const { join:
|
|
49027
|
+
const { homedir: homedir10 } = await import("os");
|
|
49028
|
+
const { join: join12 } = await import("path");
|
|
48838
49029
|
const { readFile } = await import("fs/promises");
|
|
48839
|
-
const p =
|
|
49030
|
+
const p = join12(homedir10(), ".modelstat", "last-status.json");
|
|
48840
49031
|
const txt = await readFile(p, "utf8");
|
|
48841
49032
|
return JSON.parse(txt);
|
|
48842
49033
|
} catch {
|
|
@@ -48879,7 +49070,9 @@ async function cmdStats(args) {
|
|
|
48879
49070
|
console.log("device is claimed \u2014 live stats available at:");
|
|
48880
49071
|
console.log(` ${dashboard}`);
|
|
48881
49072
|
if (local) {
|
|
48882
|
-
console.log(
|
|
49073
|
+
console.log(
|
|
49074
|
+
`local agent: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
|
|
49075
|
+
);
|
|
48883
49076
|
const stats = local.stats ?? {};
|
|
48884
49077
|
for (const [k, v] of Object.entries(stats)) console.log(` ${k}: ${v}`);
|
|
48885
49078
|
}
|
|
@@ -48897,7 +49090,9 @@ async function cmdStats(args) {
|
|
|
48897
49090
|
console.log(
|
|
48898
49091
|
`status: ${view.device.agent_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
|
|
48899
49092
|
);
|
|
48900
|
-
console.log(
|
|
49093
|
+
console.log(
|
|
49094
|
+
`claim: ${view.status}${view.status === "unclaimed" ? ` (at ${view.claim_url})` : ""}`
|
|
49095
|
+
);
|
|
48901
49096
|
console.log(`sessions: ${fmtInt(view.analyzed.count)}`);
|
|
48902
49097
|
console.log(`tokens: ${fmtTokens(view.analyzed.totalTokens)}`);
|
|
48903
49098
|
console.log(`cost: ${fmtCost(view.analyzed.totalCostUsd)}`);
|
|
@@ -48922,10 +49117,8 @@ async function cmdJobs(args) {
|
|
|
48922
49117
|
if (!jobs && !ledger) {
|
|
48923
49118
|
const dashboard = `${state.apiUrl.replace(/\/$/, "")}/dashboard/jobs`;
|
|
48924
49119
|
if (asJson) {
|
|
48925
|
-
process.stdout.write(
|
|
48926
|
-
|
|
48927
|
-
`
|
|
48928
|
-
);
|
|
49120
|
+
process.stdout.write(`${JSON.stringify({ paired: true, claimed: true, dashboard })}
|
|
49121
|
+
`);
|
|
48929
49122
|
} else {
|
|
48930
49123
|
console.log("device is claimed \u2014 job queue at:");
|
|
48931
49124
|
console.log(` ${dashboard}`);
|
|
@@ -49015,6 +49208,14 @@ async function main() {
|
|
|
49015
49208
|
console.log(`\u2713 runtime staged at ${dest}`);
|
|
49016
49209
|
return;
|
|
49017
49210
|
}
|
|
49211
|
+
case "_daemon-health": {
|
|
49212
|
+
try {
|
|
49213
|
+
console.log(JSON.stringify(daemonHealth({ myAgentVersion: AGENT_VERSION3 })));
|
|
49214
|
+
} catch (e) {
|
|
49215
|
+
console.log(JSON.stringify({ decision: "spawn", error: e.message }));
|
|
49216
|
+
}
|
|
49217
|
+
return;
|
|
49218
|
+
}
|
|
49018
49219
|
// ── Diagnostics / dev one-shots ────────────────────────────
|
|
49019
49220
|
case "status":
|
|
49020
49221
|
return cmdStatus();
|
|
@@ -49042,10 +49243,18 @@ async function main() {
|
|
|
49042
49243
|
return cmdAwaitClaim();
|
|
49043
49244
|
default:
|
|
49044
49245
|
console.log("usage:");
|
|
49045
|
-
console.log(
|
|
49046
|
-
|
|
49047
|
-
|
|
49048
|
-
console.log(
|
|
49246
|
+
console.log(
|
|
49247
|
+
" npx modelstat@latest \u2014 install or upgrade. Registers the device, installs the background service, exits."
|
|
49248
|
+
);
|
|
49249
|
+
console.log(
|
|
49250
|
+
" flags: --json (NDJSON events on stdout), --no-browser, --fresh, -y"
|
|
49251
|
+
);
|
|
49252
|
+
console.log(
|
|
49253
|
+
" npx modelstat@latest remove \u2014 stop and uninstall the background service. Keeps your identity."
|
|
49254
|
+
);
|
|
49255
|
+
console.log(
|
|
49256
|
+
" npx modelstat@latest reinstall \u2014 alias for the default. Useful when you want to be explicit."
|
|
49257
|
+
);
|
|
49049
49258
|
console.log();
|
|
49050
49259
|
console.log("Diagnostics:");
|
|
49051
49260
|
console.log(" npx modelstat@latest status \u2014 show pairing + service state");
|
|
@@ -49056,11 +49265,17 @@ async function main() {
|
|
|
49056
49265
|
console.log();
|
|
49057
49266
|
console.log("Dev / one-shots:");
|
|
49058
49267
|
console.log(" npx modelstat@latest scan \u2014 one-shot parse + upload of local JSONL");
|
|
49059
|
-
console.log(
|
|
49060
|
-
|
|
49268
|
+
console.log(
|
|
49269
|
+
" npx modelstat@latest rescan \u2014 wipe file cursors so the next scan re-reads & re-summarises everything"
|
|
49270
|
+
);
|
|
49271
|
+
console.log(
|
|
49272
|
+
" npx modelstat@latest watch \u2014 continuous (chokidar) with periodic backstop"
|
|
49273
|
+
);
|
|
49061
49274
|
console.log(" npx modelstat@latest discover \u2014 one-shot report of installs/identities");
|
|
49062
49275
|
console.log();
|
|
49063
|
-
console.log(
|
|
49276
|
+
console.log(
|
|
49277
|
+
"Internal (called by launchd/systemd, not by humans): _daemon, start, self-register, await-claim"
|
|
49278
|
+
);
|
|
49064
49279
|
process.exit(1);
|
|
49065
49280
|
}
|
|
49066
49281
|
}
|