modelstat 0.0.44 → 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 +492 -247
- 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
|
|
|
@@ -4688,6 +4690,16 @@ import { createReadStream, existsSync as existsSync2, readdirSync } from "fs";
|
|
|
4688
4690
|
import { stat } from "fs/promises";
|
|
4689
4691
|
import { dirname as dirname2, join } from "path";
|
|
4690
4692
|
import { createInterface } from "readline";
|
|
4693
|
+
function countToolCalls(content) {
|
|
4694
|
+
const counts = {};
|
|
4695
|
+
if (!Array.isArray(content)) return counts;
|
|
4696
|
+
for (const block of content) {
|
|
4697
|
+
if (block && block.type === "tool_use" && typeof block.name === "string" && block.name) {
|
|
4698
|
+
counts[block.name] = (counts[block.name] ?? 0) + 1;
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
return counts;
|
|
4702
|
+
}
|
|
4691
4703
|
function extractExcerpt(content) {
|
|
4692
4704
|
if (!content) return void 0;
|
|
4693
4705
|
let text = "";
|
|
@@ -4712,6 +4724,21 @@ function extractExcerpt(content) {
|
|
|
4712
4724
|
}
|
|
4713
4725
|
async function parseClaudeCodeJsonl(ctx) {
|
|
4714
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
|
+
};
|
|
4715
4742
|
let rawLines = 0;
|
|
4716
4743
|
let skipped = 0;
|
|
4717
4744
|
let bytePos = 0;
|
|
@@ -4799,7 +4826,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4799
4826
|
}
|
|
4800
4827
|
const slug = guessRepoSlugFromPath(cwd);
|
|
4801
4828
|
const excerpt = extractExcerpt(a.message?.content);
|
|
4802
|
-
|
|
4829
|
+
await emit({
|
|
4803
4830
|
source_event_id: eventId,
|
|
4804
4831
|
ts: a.timestamp,
|
|
4805
4832
|
kind: "assistant_message",
|
|
@@ -4825,7 +4852,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4825
4852
|
reasoning: 0
|
|
4826
4853
|
},
|
|
4827
4854
|
duration_ms: null,
|
|
4828
|
-
tool_calls:
|
|
4855
|
+
tool_calls: countToolCalls(a.message?.content),
|
|
4829
4856
|
files_touched: [],
|
|
4830
4857
|
...excerpt ? { content_excerpt: excerpt } : {},
|
|
4831
4858
|
source_file: ctx.sourceFile,
|
|
@@ -4848,7 +4875,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4848
4875
|
continue;
|
|
4849
4876
|
}
|
|
4850
4877
|
const excerpt = extractExcerpt(u.message?.content);
|
|
4851
|
-
|
|
4878
|
+
await emit({
|
|
4852
4879
|
source_event_id: eventId,
|
|
4853
4880
|
ts: u.timestamp,
|
|
4854
4881
|
kind: "user_message",
|
|
@@ -4873,9 +4900,10 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
4873
4900
|
skipped += 1;
|
|
4874
4901
|
}
|
|
4875
4902
|
}
|
|
4903
|
+
if (ctx.onEvents && chunk.length > 0) await ctx.onEvents(chunk);
|
|
4876
4904
|
return {
|
|
4877
4905
|
events,
|
|
4878
|
-
stats: { rawLines, emittedEvents:
|
|
4906
|
+
stats: { rawLines, emittedEvents: emitted, skipped },
|
|
4879
4907
|
sourceFile: ctx.sourceFile
|
|
4880
4908
|
};
|
|
4881
4909
|
}
|
|
@@ -4897,6 +4925,7 @@ var init_claude_code = __esm({
|
|
|
4897
4925
|
"../../packages/parsers/src/claude-code/index.ts"() {
|
|
4898
4926
|
"use strict";
|
|
4899
4927
|
init_src();
|
|
4928
|
+
init_types();
|
|
4900
4929
|
init_git();
|
|
4901
4930
|
}
|
|
4902
4931
|
});
|
|
@@ -4912,6 +4941,21 @@ function deriveSessionIdFromRolloutPath(path5) {
|
|
|
4912
4941
|
}
|
|
4913
4942
|
async function parseCodexRollout(ctx) {
|
|
4914
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
|
+
};
|
|
4915
4959
|
let rawLines = 0;
|
|
4916
4960
|
let skipped = 0;
|
|
4917
4961
|
let bytePos = 0;
|
|
@@ -4967,7 +5011,7 @@ async function parseCodexRollout(ctx) {
|
|
|
4967
5011
|
continue;
|
|
4968
5012
|
}
|
|
4969
5013
|
const slug = guessRepoSlugFromPath(cwd);
|
|
4970
|
-
|
|
5014
|
+
await emit({
|
|
4971
5015
|
source_event_id: sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart),
|
|
4972
5016
|
ts,
|
|
4973
5017
|
kind: "assistant_message",
|
|
@@ -5006,7 +5050,7 @@ async function parseCodexRollout(ctx) {
|
|
|
5006
5050
|
skipped += 1;
|
|
5007
5051
|
continue;
|
|
5008
5052
|
}
|
|
5009
|
-
|
|
5053
|
+
await emit({
|
|
5010
5054
|
source_event_id: sourceEventId(ctx.deviceId, ctx.sourceFile, offsetAtLineStart),
|
|
5011
5055
|
ts,
|
|
5012
5056
|
kind: "user_message",
|
|
@@ -5032,9 +5076,10 @@ async function parseCodexRollout(ctx) {
|
|
|
5032
5076
|
}
|
|
5033
5077
|
skipped += 1;
|
|
5034
5078
|
}
|
|
5079
|
+
if (ctx.onEvents && chunk.length > 0) await ctx.onEvents(chunk);
|
|
5035
5080
|
return {
|
|
5036
5081
|
events,
|
|
5037
|
-
stats: { rawLines, emittedEvents:
|
|
5082
|
+
stats: { rawLines, emittedEvents: emitted, skipped },
|
|
5038
5083
|
sourceFile: ctx.sourceFile
|
|
5039
5084
|
};
|
|
5040
5085
|
}
|
|
@@ -5042,6 +5087,7 @@ var init_codex = __esm({
|
|
|
5042
5087
|
"../../packages/parsers/src/codex/index.ts"() {
|
|
5043
5088
|
"use strict";
|
|
5044
5089
|
init_src();
|
|
5090
|
+
init_types();
|
|
5045
5091
|
init_git();
|
|
5046
5092
|
}
|
|
5047
5093
|
});
|
|
@@ -7612,107 +7658,6 @@ var init_src2 = __esm({
|
|
|
7612
7658
|
}
|
|
7613
7659
|
});
|
|
7614
7660
|
|
|
7615
|
-
// src/identity.ts
|
|
7616
|
-
import {
|
|
7617
|
-
chmodSync,
|
|
7618
|
-
mkdirSync,
|
|
7619
|
-
readFileSync as readFileSync2,
|
|
7620
|
-
renameSync,
|
|
7621
|
-
writeFileSync,
|
|
7622
|
-
existsSync as existsSync4
|
|
7623
|
-
} from "fs";
|
|
7624
|
-
import { homedir as homedir2, hostname as osHostname } from "os";
|
|
7625
|
-
import { join as join3 } from "path";
|
|
7626
|
-
function ensureRoot() {
|
|
7627
|
-
mkdirSync(ROOT, { recursive: true, mode: 448 });
|
|
7628
|
-
}
|
|
7629
|
-
function writeAtomic(meta) {
|
|
7630
|
-
ensureRoot();
|
|
7631
|
-
const tmp = `${IDENTITY_FILE}.${process.pid}.tmp`;
|
|
7632
|
-
writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
|
|
7633
|
-
renameSync(tmp, IDENTITY_FILE);
|
|
7634
|
-
try {
|
|
7635
|
-
chmodSync(IDENTITY_FILE, 384);
|
|
7636
|
-
} catch {
|
|
7637
|
-
}
|
|
7638
|
-
}
|
|
7639
|
-
function identityPath() {
|
|
7640
|
-
return IDENTITY_FILE;
|
|
7641
|
-
}
|
|
7642
|
-
function hasIdentityFile() {
|
|
7643
|
-
return existsSync4(IDENTITY_FILE);
|
|
7644
|
-
}
|
|
7645
|
-
function parseFile() {
|
|
7646
|
-
try {
|
|
7647
|
-
const raw = readFileSync2(IDENTITY_FILE, "utf8");
|
|
7648
|
-
const obj = JSON.parse(raw);
|
|
7649
|
-
if (!obj.deviceUuid || !obj.deviceId || !obj.bearerToken) {
|
|
7650
|
-
return null;
|
|
7651
|
-
}
|
|
7652
|
-
return {
|
|
7653
|
-
deviceUuid: obj.deviceUuid,
|
|
7654
|
-
deviceId: obj.deviceId,
|
|
7655
|
-
bearerToken: obj.bearerToken,
|
|
7656
|
-
claimCode: obj.claimCode ?? null,
|
|
7657
|
-
claimUrl: obj.claimUrl ?? null,
|
|
7658
|
-
hostname: obj.hostname ?? osHostname(),
|
|
7659
|
-
createdAt: obj.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7660
|
-
userEmail: obj.userEmail ?? null,
|
|
7661
|
-
defaultOrgId: obj.defaultOrgId ?? null
|
|
7662
|
-
};
|
|
7663
|
-
} catch {
|
|
7664
|
-
return null;
|
|
7665
|
-
}
|
|
7666
|
-
}
|
|
7667
|
-
function loadIdentity(migrateFromConf2) {
|
|
7668
|
-
const fromFile = parseFile();
|
|
7669
|
-
if (fromFile) return fromFile;
|
|
7670
|
-
if (!migrateFromConf2) return null;
|
|
7671
|
-
const legacy = migrateFromConf2();
|
|
7672
|
-
if (!legacy) return null;
|
|
7673
|
-
if (!legacy.deviceUuid || !legacy.deviceId || !legacy.bearerToken) {
|
|
7674
|
-
return null;
|
|
7675
|
-
}
|
|
7676
|
-
const migrated = {
|
|
7677
|
-
deviceUuid: legacy.deviceUuid,
|
|
7678
|
-
deviceId: legacy.deviceId,
|
|
7679
|
-
bearerToken: legacy.bearerToken,
|
|
7680
|
-
claimCode: legacy.claimCode ?? null,
|
|
7681
|
-
claimUrl: legacy.claimUrl ?? null,
|
|
7682
|
-
hostname: osHostname(),
|
|
7683
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7684
|
-
userEmail: legacy.userEmail ?? null,
|
|
7685
|
-
defaultOrgId: legacy.defaultOrgId ?? null
|
|
7686
|
-
};
|
|
7687
|
-
writeAtomic(migrated);
|
|
7688
|
-
return migrated;
|
|
7689
|
-
}
|
|
7690
|
-
function saveIdentity(meta) {
|
|
7691
|
-
writeAtomic(meta);
|
|
7692
|
-
}
|
|
7693
|
-
function backupIdentity() {
|
|
7694
|
-
if (!existsSync4(IDENTITY_FILE)) return null;
|
|
7695
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7696
|
-
const dest = `${IDENTITY_FILE}.bak-${stamp}`;
|
|
7697
|
-
renameSync(IDENTITY_FILE, dest);
|
|
7698
|
-
return dest;
|
|
7699
|
-
}
|
|
7700
|
-
function updateIdentity(patch) {
|
|
7701
|
-
const current = parseFile();
|
|
7702
|
-
if (!current) return null;
|
|
7703
|
-
const merged = { ...current, ...patch };
|
|
7704
|
-
writeAtomic(merged);
|
|
7705
|
-
return merged;
|
|
7706
|
-
}
|
|
7707
|
-
var ROOT, IDENTITY_FILE;
|
|
7708
|
-
var init_identity = __esm({
|
|
7709
|
-
"src/identity.ts"() {
|
|
7710
|
-
"use strict";
|
|
7711
|
-
ROOT = join3(homedir2(), ".modelstat");
|
|
7712
|
-
IDENTITY_FILE = join3(ROOT, "identity.json");
|
|
7713
|
-
}
|
|
7714
|
-
});
|
|
7715
|
-
|
|
7716
7661
|
// ../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js
|
|
7717
7662
|
var require_symbols = __commonJS({
|
|
7718
7663
|
"../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js"(exports, module) {
|
|
@@ -32604,6 +32549,9 @@ function classifyStatus(status2, attempt) {
|
|
|
32604
32549
|
}
|
|
32605
32550
|
return { type: "drop", reason: `http_${status2}` };
|
|
32606
32551
|
}
|
|
32552
|
+
function wellFormedStringify(value) {
|
|
32553
|
+
return JSON.stringify(value, (_key, v) => typeof v === "string" ? v.toWellFormed() : v);
|
|
32554
|
+
}
|
|
32607
32555
|
function sleep(ms) {
|
|
32608
32556
|
return new Promise((r) => {
|
|
32609
32557
|
setTimeout(r, ms);
|
|
@@ -32642,7 +32590,10 @@ var init_http = __esm({
|
|
|
32642
32590
|
"content-type": "application/json",
|
|
32643
32591
|
authorization: `Bearer ${token}`
|
|
32644
32592
|
},
|
|
32645
|
-
|
|
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)
|
|
32646
32597
|
});
|
|
32647
32598
|
} catch (err) {
|
|
32648
32599
|
const detail = describeErrorWithCause(err);
|
|
@@ -33291,15 +33242,15 @@ function envPaths(name, { suffix = "nodejs" } = {}) {
|
|
|
33291
33242
|
}
|
|
33292
33243
|
return linux(name);
|
|
33293
33244
|
}
|
|
33294
|
-
var
|
|
33245
|
+
var homedir2, tmpdir, env, macos, windows, linux;
|
|
33295
33246
|
var init_env_paths = __esm({
|
|
33296
33247
|
"../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js"() {
|
|
33297
33248
|
"use strict";
|
|
33298
|
-
|
|
33249
|
+
homedir2 = os.homedir();
|
|
33299
33250
|
tmpdir = os.tmpdir();
|
|
33300
33251
|
({ env } = process2);
|
|
33301
33252
|
macos = (name) => {
|
|
33302
|
-
const library = path.join(
|
|
33253
|
+
const library = path.join(homedir2, "Library");
|
|
33303
33254
|
return {
|
|
33304
33255
|
data: path.join(library, "Application Support", name),
|
|
33305
33256
|
config: path.join(library, "Preferences", name),
|
|
@@ -33309,8 +33260,8 @@ var init_env_paths = __esm({
|
|
|
33309
33260
|
};
|
|
33310
33261
|
};
|
|
33311
33262
|
windows = (name) => {
|
|
33312
|
-
const appData = env.APPDATA || path.join(
|
|
33313
|
-
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");
|
|
33314
33265
|
return {
|
|
33315
33266
|
// Data/config/cache/log are invented by me as Windows isn't opinionated about this
|
|
33316
33267
|
data: path.join(localAppData, name, "Data"),
|
|
@@ -33321,13 +33272,13 @@ var init_env_paths = __esm({
|
|
|
33321
33272
|
};
|
|
33322
33273
|
};
|
|
33323
33274
|
linux = (name) => {
|
|
33324
|
-
const username = path.basename(
|
|
33275
|
+
const username = path.basename(homedir2);
|
|
33325
33276
|
return {
|
|
33326
|
-
data: path.join(env.XDG_DATA_HOME || path.join(
|
|
33327
|
-
config: path.join(env.XDG_CONFIG_HOME || path.join(
|
|
33328
|
-
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),
|
|
33329
33280
|
// https://wiki.debian.org/XDGBaseDirectorySpecification#state
|
|
33330
|
-
log: path.join(env.XDG_STATE_HOME || path.join(
|
|
33281
|
+
log: path.join(env.XDG_STATE_HOME || path.join(homedir2, ".local", "state"), name),
|
|
33331
33282
|
temp: path.join(tmpdir, username, name)
|
|
33332
33283
|
};
|
|
33333
33284
|
};
|
|
@@ -33768,9 +33719,9 @@ import { once } from "events";
|
|
|
33768
33719
|
import { createWriteStream } from "fs";
|
|
33769
33720
|
import path3 from "path";
|
|
33770
33721
|
import { Readable } from "stream";
|
|
33771
|
-
function
|
|
33722
|
+
function writeFileSync(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
|
|
33772
33723
|
if (isString(options))
|
|
33773
|
-
return
|
|
33724
|
+
return writeFileSync(filePath, data, { encoding: options });
|
|
33774
33725
|
const timeout = options.timeout ?? DEFAULT_TIMEOUT_SYNC;
|
|
33775
33726
|
const retryOptions = { timeout };
|
|
33776
33727
|
let tempDisposer = null;
|
|
@@ -43893,7 +43844,7 @@ var init_source = __esm({
|
|
|
43893
43844
|
fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
43894
43845
|
} else {
|
|
43895
43846
|
try {
|
|
43896
|
-
|
|
43847
|
+
writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
43897
43848
|
} catch (error) {
|
|
43898
43849
|
if (error?.code === "EXDEV") {
|
|
43899
43850
|
fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
|
|
@@ -43994,6 +43945,107 @@ var init_source = __esm({
|
|
|
43994
43945
|
}
|
|
43995
43946
|
});
|
|
43996
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
|
+
|
|
43997
44049
|
// src/config.ts
|
|
43998
44050
|
import { existsSync as existsSync5 } from "fs";
|
|
43999
44051
|
import { hostname } from "os";
|
|
@@ -45312,6 +45364,21 @@ var init_llama = __esm({
|
|
|
45312
45364
|
}
|
|
45313
45365
|
});
|
|
45314
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
|
+
|
|
45315
45382
|
// ../../packages/companion-core/src/node/transformersjs-embed.ts
|
|
45316
45383
|
async function loadPipeline(model) {
|
|
45317
45384
|
if (cached) return cached;
|
|
@@ -45319,11 +45386,7 @@ async function loadPipeline(model) {
|
|
|
45319
45386
|
if (!loadPromise2) {
|
|
45320
45387
|
loadPromise2 = (async () => {
|
|
45321
45388
|
try {
|
|
45322
|
-
const
|
|
45323
|
-
const tjs = await import(
|
|
45324
|
-
/* @vite-ignore */
|
|
45325
|
-
moduleId
|
|
45326
|
-
);
|
|
45389
|
+
const tjs = await importModule("@huggingface/transformers");
|
|
45327
45390
|
const p = await tjs.pipeline("feature-extraction", model, {
|
|
45328
45391
|
device: "cpu",
|
|
45329
45392
|
dtype: "fp32"
|
|
@@ -45332,12 +45395,16 @@ async function loadPipeline(model) {
|
|
|
45332
45395
|
return p;
|
|
45333
45396
|
} catch (err) {
|
|
45334
45397
|
const msg = err.message;
|
|
45335
|
-
|
|
45398
|
+
loadAttempts += 1;
|
|
45399
|
+
if (isMissingOptionalModuleError(err) || /unsupported|architecture|not supported|onnx/i.test(msg) || loadAttempts >= OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS) {
|
|
45336
45400
|
loadFailedPermanently = true;
|
|
45337
45401
|
}
|
|
45338
|
-
|
|
45339
|
-
|
|
45340
|
-
|
|
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
|
+
}
|
|
45341
45408
|
loadPromise2 = null;
|
|
45342
45409
|
return null;
|
|
45343
45410
|
}
|
|
@@ -45354,21 +45421,32 @@ function createTransformersJsEmbedder(model = DEFAULT_MODEL) {
|
|
|
45354
45421
|
const out = await pipe(text, { pooling: "mean", normalize: true });
|
|
45355
45422
|
return Array.from(out.data);
|
|
45356
45423
|
} catch (err) {
|
|
45357
|
-
|
|
45358
|
-
|
|
45359
|
-
|
|
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
|
+
}
|
|
45360
45430
|
return [];
|
|
45361
45431
|
}
|
|
45362
45432
|
};
|
|
45363
45433
|
}
|
|
45364
|
-
var cached, loadPromise2, loadFailedPermanently, DEFAULT_MODEL;
|
|
45434
|
+
var cached, loadPromise2, loadFailedPermanently, loadAttempts, warnedUnavailable, warnedInferenceError, DEFAULT_MODEL, importModule;
|
|
45365
45435
|
var init_transformersjs_embed = __esm({
|
|
45366
45436
|
"../../packages/companion-core/src/node/transformersjs-embed.ts"() {
|
|
45367
45437
|
"use strict";
|
|
45438
|
+
init_optional_module();
|
|
45368
45439
|
cached = null;
|
|
45369
45440
|
loadPromise2 = null;
|
|
45370
45441
|
loadFailedPermanently = false;
|
|
45442
|
+
loadAttempts = 0;
|
|
45443
|
+
warnedUnavailable = false;
|
|
45444
|
+
warnedInferenceError = false;
|
|
45371
45445
|
DEFAULT_MODEL = "Xenova/bge-small-en-v1.5";
|
|
45446
|
+
importModule = (id) => import(
|
|
45447
|
+
/* @vite-ignore */
|
|
45448
|
+
id
|
|
45449
|
+
);
|
|
45372
45450
|
}
|
|
45373
45451
|
});
|
|
45374
45452
|
|
|
@@ -45407,18 +45485,23 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45407
45485
|
const device = opts.device ?? (isBrowser ? "webgpu" : "cpu");
|
|
45408
45486
|
const dtype = opts.dtype ?? "q4";
|
|
45409
45487
|
const modelId = opts.model ?? "openai/privacy-filter";
|
|
45488
|
+
const importModule2 = opts.importModule ?? ((id) => import(
|
|
45489
|
+
/* @vite-ignore */
|
|
45490
|
+
id
|
|
45491
|
+
));
|
|
45410
45492
|
let cached2 = null;
|
|
45411
45493
|
let loadPromise3 = null;
|
|
45494
|
+
let loadFailedPermanently2 = false;
|
|
45495
|
+
let loadAttempts2 = 0;
|
|
45496
|
+
let warnedUnavailable2 = false;
|
|
45497
|
+
let warnedInferenceError2 = false;
|
|
45412
45498
|
async function loadPipeline2() {
|
|
45413
45499
|
if (cached2) return cached2;
|
|
45500
|
+
if (loadFailedPermanently2) return null;
|
|
45414
45501
|
if (!loadPromise3) {
|
|
45415
45502
|
loadPromise3 = (async () => {
|
|
45416
45503
|
try {
|
|
45417
|
-
const
|
|
45418
|
-
const tjs = await import(
|
|
45419
|
-
/* @vite-ignore */
|
|
45420
|
-
moduleId
|
|
45421
|
-
);
|
|
45504
|
+
const tjs = await importModule2("@huggingface/transformers");
|
|
45422
45505
|
const p = await tjs.pipeline("token-classification", modelId, {
|
|
45423
45506
|
device,
|
|
45424
45507
|
dtype,
|
|
@@ -45428,10 +45511,17 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45428
45511
|
return p;
|
|
45429
45512
|
} catch (err) {
|
|
45430
45513
|
loadPromise3 = null;
|
|
45431
|
-
|
|
45432
|
-
|
|
45433
|
-
|
|
45434
|
-
|
|
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
|
+
}
|
|
45435
45525
|
return null;
|
|
45436
45526
|
}
|
|
45437
45527
|
})();
|
|
@@ -45454,10 +45544,13 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45454
45544
|
try {
|
|
45455
45545
|
tokens = await classify(text);
|
|
45456
45546
|
} catch (err) {
|
|
45457
|
-
|
|
45458
|
-
|
|
45459
|
-
|
|
45460
|
-
|
|
45547
|
+
if (!warnedInferenceError2) {
|
|
45548
|
+
warnedInferenceError2 = true;
|
|
45549
|
+
console.warn(
|
|
45550
|
+
"[privacy-filter] inference failed, returning input unchanged (warning once):",
|
|
45551
|
+
err.message
|
|
45552
|
+
);
|
|
45553
|
+
}
|
|
45461
45554
|
return empty;
|
|
45462
45555
|
}
|
|
45463
45556
|
const spans = [];
|
|
@@ -45494,6 +45587,7 @@ async function createPrivacyFilterRedactor(opts = {}) {
|
|
|
45494
45587
|
var init_privacy_filter = __esm({
|
|
45495
45588
|
"../../packages/companion-core/src/redact/privacy-filter.ts"() {
|
|
45496
45589
|
"use strict";
|
|
45590
|
+
init_optional_module();
|
|
45497
45591
|
}
|
|
45498
45592
|
});
|
|
45499
45593
|
|
|
@@ -45603,9 +45697,8 @@ async function scanAll(cb = {}) {
|
|
|
45603
45697
|
const full = join5(dir, f);
|
|
45604
45698
|
jobs.push({
|
|
45605
45699
|
path: full,
|
|
45606
|
-
parse: async () => {
|
|
45607
|
-
|
|
45608
|
-
return { events: r.events, path: full };
|
|
45700
|
+
parse: async (sink2) => {
|
|
45701
|
+
await parseClaudeCodeJsonl({ deviceId, sourceFile: full, onEvents: sink2 });
|
|
45609
45702
|
}
|
|
45610
45703
|
});
|
|
45611
45704
|
}
|
|
@@ -45627,9 +45720,8 @@ async function scanAll(cb = {}) {
|
|
|
45627
45720
|
const full = join5(base, y, m, d, f);
|
|
45628
45721
|
jobs.push({
|
|
45629
45722
|
path: full,
|
|
45630
|
-
parse: async () => {
|
|
45631
|
-
|
|
45632
|
-
return { events: r.events, path: full };
|
|
45723
|
+
parse: async (sink2) => {
|
|
45724
|
+
await parseCodexRollout({ deviceId, sourceFile: full, onEvents: sink2 });
|
|
45633
45725
|
}
|
|
45634
45726
|
});
|
|
45635
45727
|
}
|
|
@@ -45684,6 +45776,17 @@ async function scanAll(cb = {}) {
|
|
|
45684
45776
|
buffer = [];
|
|
45685
45777
|
cb.onUploaded?.({ events: res.accepted, segments: segments.length });
|
|
45686
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
|
+
};
|
|
45687
45790
|
for (let i = 0; i < jobs.length; i++) {
|
|
45688
45791
|
const job = jobs[i];
|
|
45689
45792
|
cb.onFile?.(job.path, i, jobs.length);
|
|
@@ -45695,13 +45798,7 @@ async function scanAll(cb = {}) {
|
|
|
45695
45798
|
}
|
|
45696
45799
|
filesScanned += 1;
|
|
45697
45800
|
try {
|
|
45698
|
-
|
|
45699
|
-
if (r.events.length) {
|
|
45700
|
-
for (const e of r.events) {
|
|
45701
|
-
buffer.push(e);
|
|
45702
|
-
if (buffer.length >= BATCH_MAX_EVENTS) await flushBatch();
|
|
45703
|
-
}
|
|
45704
|
-
}
|
|
45801
|
+
await job.parse(sink);
|
|
45705
45802
|
if (cs) pendingCursors.push({ path: job.path, cs });
|
|
45706
45803
|
} catch (e) {
|
|
45707
45804
|
console.warn(` ! parse failed for ${job.path}:`, e.message);
|
|
@@ -45710,7 +45807,7 @@ async function scanAll(cb = {}) {
|
|
|
45710
45807
|
await flushBatch();
|
|
45711
45808
|
return { filesScanned, filesUnchanged, batchesUploaded, eventsUploaded, segmentsUploaded };
|
|
45712
45809
|
}
|
|
45713
|
-
var AGENT_VERSION, BATCH_MAX_EVENTS, ZERO_TOKENS;
|
|
45810
|
+
var AGENT_VERSION, BATCH_MAX_EVENTS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
|
|
45714
45811
|
var init_scan = __esm({
|
|
45715
45812
|
"src/scan.ts"() {
|
|
45716
45813
|
"use strict";
|
|
@@ -45719,8 +45816,9 @@ var init_scan = __esm({
|
|
|
45719
45816
|
init_pipeline2();
|
|
45720
45817
|
init_config2();
|
|
45721
45818
|
init_api();
|
|
45722
|
-
AGENT_VERSION = true ? "agent-0.0.
|
|
45819
|
+
AGENT_VERSION = true ? "agent-0.0.46" : "agent-dev";
|
|
45723
45820
|
BATCH_MAX_EVENTS = 2e3;
|
|
45821
|
+
BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
|
|
45724
45822
|
ZERO_TOKENS = {
|
|
45725
45823
|
input: 0,
|
|
45726
45824
|
output: 0,
|
|
@@ -45756,9 +45854,9 @@ function isProcessAlive(pid) {
|
|
|
45756
45854
|
return false;
|
|
45757
45855
|
}
|
|
45758
45856
|
}
|
|
45759
|
-
function
|
|
45857
|
+
function readDaemonLock(lockFile = LOCK_FILE) {
|
|
45760
45858
|
try {
|
|
45761
|
-
const raw = readFileSync4(
|
|
45859
|
+
const raw = readFileSync4(lockFile, "utf8");
|
|
45762
45860
|
const obj = JSON.parse(raw);
|
|
45763
45861
|
if (typeof obj.pid !== "number") return null;
|
|
45764
45862
|
return {
|
|
@@ -45783,7 +45881,7 @@ function writeLockAtomic(meta) {
|
|
|
45783
45881
|
renameSync2(tmp, LOCK_FILE);
|
|
45784
45882
|
}
|
|
45785
45883
|
function removeLockIfOwned(ownerPid) {
|
|
45786
|
-
const lock =
|
|
45884
|
+
const lock = readDaemonLock();
|
|
45787
45885
|
if (!lock) return;
|
|
45788
45886
|
if (lock.pid !== ownerPid) return;
|
|
45789
45887
|
try {
|
|
@@ -45792,7 +45890,7 @@ function removeLockIfOwned(ownerPid) {
|
|
|
45792
45890
|
}
|
|
45793
45891
|
}
|
|
45794
45892
|
function acquireDaemonLock(opts) {
|
|
45795
|
-
const existing =
|
|
45893
|
+
const existing = readDaemonLock();
|
|
45796
45894
|
if (existing && isProcessAlive(existing.pid)) {
|
|
45797
45895
|
if (!opts.force) {
|
|
45798
45896
|
const ageSec = ageInSeconds(existing.startedAt);
|
|
@@ -45810,6 +45908,20 @@ function acquireDaemonLock(opts) {
|
|
|
45810
45908
|
apiUrl: opts.apiUrl
|
|
45811
45909
|
};
|
|
45812
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();
|
|
45813
45925
|
const cleanup = () => removeLockIfOwned(process.pid);
|
|
45814
45926
|
process.once("beforeExit", cleanup);
|
|
45815
45927
|
process.once("SIGINT", () => {
|
|
@@ -45827,6 +45939,11 @@ function acquireDaemonLock(opts) {
|
|
|
45827
45939
|
});
|
|
45828
45940
|
return { kind: "acquired" };
|
|
45829
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
|
+
}
|
|
45830
45947
|
function ageInSeconds(iso) {
|
|
45831
45948
|
const t = Date.parse(iso);
|
|
45832
45949
|
if (Number.isNaN(t)) return -1;
|
|
@@ -45841,12 +45958,13 @@ function formatAge(seconds) {
|
|
|
45841
45958
|
const h = Math.floor(m / 60);
|
|
45842
45959
|
return `${h}h ${m % 60}m`;
|
|
45843
45960
|
}
|
|
45844
|
-
var LOCK_DIR, LOCK_FILE;
|
|
45961
|
+
var LOCK_DIR, LOCK_FILE, LOCK_RECHECK_MS;
|
|
45845
45962
|
var init_lock = __esm({
|
|
45846
45963
|
"src/lock.ts"() {
|
|
45847
45964
|
"use strict";
|
|
45848
45965
|
LOCK_DIR = join7(homedir7(), ".modelstat");
|
|
45849
45966
|
LOCK_FILE = join7(LOCK_DIR, "daemon.lock");
|
|
45967
|
+
LOCK_RECHECK_MS = 5e3;
|
|
45850
45968
|
}
|
|
45851
45969
|
});
|
|
45852
45970
|
|
|
@@ -47720,17 +47838,44 @@ async function sendHeartbeat() {
|
|
|
47720
47838
|
}
|
|
47721
47839
|
writeLocalStatus(body).catch(() => void 0);
|
|
47722
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
|
+
}
|
|
47723
47868
|
async function writeLocalStatus(snapshot) {
|
|
47724
|
-
const { homedir:
|
|
47725
|
-
const { join:
|
|
47869
|
+
const { homedir: homedir10 } = await import("os");
|
|
47870
|
+
const { join: join12 } = await import("path");
|
|
47726
47871
|
const { writeFile, mkdir: mkdir2, rename } = await import("fs/promises");
|
|
47727
47872
|
if (!lastStatusPath) {
|
|
47728
|
-
const dir =
|
|
47873
|
+
const dir = join12(homedir10(), ".modelstat");
|
|
47729
47874
|
try {
|
|
47730
47875
|
await mkdir2(dir, { recursive: true });
|
|
47731
47876
|
} catch {
|
|
47732
47877
|
}
|
|
47733
|
-
lastStatusPath =
|
|
47878
|
+
lastStatusPath = join12(dir, "last-status.json");
|
|
47734
47879
|
}
|
|
47735
47880
|
const tmp = `${lastStatusPath}.tmp`;
|
|
47736
47881
|
try {
|
|
@@ -47812,7 +47957,17 @@ async function runDaemon(opts = {}) {
|
|
|
47812
47957
|
const lock = acquireDaemonLock({
|
|
47813
47958
|
agentVersion: AGENT_VERSION2,
|
|
47814
47959
|
apiUrl: state.apiUrl,
|
|
47815
|
-
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
|
+
}
|
|
47816
47971
|
});
|
|
47817
47972
|
if (lock.kind === "already_running") {
|
|
47818
47973
|
console.log(
|
|
@@ -47829,6 +47984,7 @@ async function runDaemon(opts = {}) {
|
|
|
47829
47984
|
return;
|
|
47830
47985
|
}
|
|
47831
47986
|
setPhase("starting", "Booting");
|
|
47987
|
+
await rotateRunawayLogs();
|
|
47832
47988
|
setStat("segments_sent", state.segmentsSent);
|
|
47833
47989
|
const hb = setInterval(() => void sendHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
47834
47990
|
hb.unref();
|
|
@@ -47852,19 +48008,19 @@ async function runDaemon(opts = {}) {
|
|
|
47852
48008
|
await runDiscovery();
|
|
47853
48009
|
await requestScan("startup");
|
|
47854
48010
|
const chokidar = (await Promise.resolve().then(() => (init_esm2(), esm_exports))).default;
|
|
47855
|
-
const { homedir:
|
|
47856
|
-
const { join:
|
|
47857
|
-
const home2 =
|
|
48011
|
+
const { homedir: homedir10, platform: platform5 } = await import("os");
|
|
48012
|
+
const { join: join12 } = await import("path");
|
|
48013
|
+
const home2 = homedir10();
|
|
47858
48014
|
const dirs = [
|
|
47859
|
-
|
|
47860
|
-
|
|
47861
|
-
|
|
47862
|
-
|
|
48015
|
+
join12(home2, ".claude/projects"),
|
|
48016
|
+
join12(home2, ".codex/sessions"),
|
|
48017
|
+
join12(home2, ".cursor/ai-tracking"),
|
|
48018
|
+
join12(home2, ".gemini"),
|
|
47863
48019
|
...platform5() === "darwin" ? [
|
|
47864
|
-
|
|
47865
|
-
|
|
48020
|
+
join12(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
|
|
48021
|
+
join12(home2, "Library/Application Support/Claude")
|
|
47866
48022
|
] : [
|
|
47867
|
-
|
|
48023
|
+
join12(home2, ".config/Cursor/User/workspaceStorage")
|
|
47868
48024
|
]
|
|
47869
48025
|
].filter((p) => existsSync9(p) && statSync2(p).isDirectory());
|
|
47870
48026
|
setPhase("watching", `Watching ${dirs.length} directories`);
|
|
@@ -47907,7 +48063,7 @@ async function runDaemon(opts = {}) {
|
|
|
47907
48063
|
await new Promise(() => {
|
|
47908
48064
|
});
|
|
47909
48065
|
}
|
|
47910
|
-
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;
|
|
47911
48067
|
var init_daemon = __esm({
|
|
47912
48068
|
"src/daemon.ts"() {
|
|
47913
48069
|
"use strict";
|
|
@@ -47919,7 +48075,7 @@ var init_daemon = __esm({
|
|
|
47919
48075
|
init_lock();
|
|
47920
48076
|
init_scan();
|
|
47921
48077
|
init_single_flight();
|
|
47922
|
-
AGENT_VERSION2 = true ? "agent-0.0.
|
|
48078
|
+
AGENT_VERSION2 = true ? "agent-0.0.46" : "agent-dev";
|
|
47923
48079
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
47924
48080
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
47925
48081
|
DISCOVERY_INTERVAL_MS = 6e4;
|
|
@@ -47935,6 +48091,8 @@ var init_daemon = __esm({
|
|
|
47935
48091
|
LOCAL_FLUSH_THROTTLE_MS = 400;
|
|
47936
48092
|
localFlushTimer = null;
|
|
47937
48093
|
localFlushPending = false;
|
|
48094
|
+
LOG_MAX_BYTES = 64 * 1024 * 1024;
|
|
48095
|
+
LOG_TAIL_KEEP_BYTES = 4 * 1024 * 1024;
|
|
47938
48096
|
lastStatusPath = null;
|
|
47939
48097
|
scanRunner = createCoalescingRunner(runScanCycle);
|
|
47940
48098
|
}
|
|
@@ -47946,33 +48104,33 @@ __export(watch_exports, {
|
|
|
47946
48104
|
watchForever: () => watchForever
|
|
47947
48105
|
});
|
|
47948
48106
|
import { existsSync as existsSync10 } from "fs";
|
|
47949
|
-
import { homedir as
|
|
47950
|
-
import { join as
|
|
48107
|
+
import { homedir as homedir9, platform as platform3 } from "os";
|
|
48108
|
+
import { join as join11 } from "path";
|
|
47951
48109
|
function resolveWatchDirs() {
|
|
47952
|
-
const home2 =
|
|
47953
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
47954
|
-
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");
|
|
47955
48113
|
const candidates = [
|
|
47956
48114
|
// universal (default HOME-rooted CLI data dirs)
|
|
47957
|
-
|
|
47958
|
-
|
|
47959
|
-
|
|
47960
|
-
|
|
47961
|
-
|
|
48115
|
+
join11(home2, ".claude/projects"),
|
|
48116
|
+
join11(home2, ".codex/sessions"),
|
|
48117
|
+
join11(home2, ".cursor/ai-tracking"),
|
|
48118
|
+
join11(home2, ".gemini"),
|
|
48119
|
+
join11(home2, ".aider"),
|
|
47962
48120
|
// XDG / Linux
|
|
47963
|
-
|
|
47964
|
-
|
|
47965
|
-
|
|
47966
|
-
|
|
47967
|
-
|
|
47968
|
-
|
|
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"),
|
|
47969
48127
|
// macOS
|
|
47970
48128
|
...platform3() === "darwin" ? [
|
|
47971
|
-
|
|
47972
|
-
|
|
47973
|
-
|
|
47974
|
-
|
|
47975
|
-
|
|
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")
|
|
47976
48134
|
] : []
|
|
47977
48135
|
];
|
|
47978
48136
|
return Array.from(new Set(candidates)).filter((p) => existsSync10(p));
|
|
@@ -48037,13 +48195,13 @@ var init_watch = __esm({
|
|
|
48037
48195
|
|
|
48038
48196
|
// src/cli.ts
|
|
48039
48197
|
init_src2();
|
|
48040
|
-
init_identity();
|
|
48041
48198
|
init_api();
|
|
48042
48199
|
init_config2();
|
|
48200
|
+
init_identity();
|
|
48043
48201
|
init_scan();
|
|
48044
48202
|
import { spawn } from "child_process";
|
|
48045
48203
|
import { randomBytes } from "crypto";
|
|
48046
|
-
import {
|
|
48204
|
+
import { arch as cpuArch, hostname as hostname2, platform as platform4, release } from "os";
|
|
48047
48205
|
import { createInterface as createInterface3 } from "readline";
|
|
48048
48206
|
|
|
48049
48207
|
// src/service.ts
|
|
@@ -48399,6 +48557,59 @@ function trayStatus() {
|
|
|
48399
48557
|
return exe ? { installed: true, path: exe.replace(/\/Contents\/MacOS\/modelstat-tray$/, "") } : { installed: false, path: null };
|
|
48400
48558
|
}
|
|
48401
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
|
+
|
|
48402
48613
|
// src/cli.ts
|
|
48403
48614
|
async function confirmPrompt(question, defaultYes) {
|
|
48404
48615
|
if (process.stdin.isTTY !== true) return defaultYes;
|
|
@@ -48429,7 +48640,7 @@ function tryOpenBrowser(url) {
|
|
|
48429
48640
|
return false;
|
|
48430
48641
|
}
|
|
48431
48642
|
}
|
|
48432
|
-
var AGENT_VERSION3 = true ? "agent-0.0.
|
|
48643
|
+
var AGENT_VERSION3 = true ? "agent-0.0.46" : "agent-dev";
|
|
48433
48644
|
function osFamily() {
|
|
48434
48645
|
const p = platform4();
|
|
48435
48646
|
if (p === "darwin") return "macos";
|
|
@@ -48472,10 +48683,8 @@ async function cmdSelfRegister() {
|
|
|
48472
48683
|
process.stdout.write(` \x1B[2mgenerated UUIDv7 ${deviceUuid.slice(0, 8)}\u2026\x1B[0m
|
|
48473
48684
|
`);
|
|
48474
48685
|
}
|
|
48475
|
-
process.stdout.write(
|
|
48476
|
-
|
|
48477
|
-
`
|
|
48478
|
-
);
|
|
48686
|
+
process.stdout.write(` \x1B[2m\u2192 POST ${state.apiUrl}/v1/devices/self-register\x1B[0m
|
|
48687
|
+
`);
|
|
48479
48688
|
const res = await selfRegister({
|
|
48480
48689
|
device_uuid: deviceUuid,
|
|
48481
48690
|
fingerprint
|
|
@@ -48489,8 +48698,10 @@ async function cmdSelfRegister() {
|
|
|
48489
48698
|
});
|
|
48490
48699
|
process.stdout.write(` \x1B[32m\u2713\x1B[0m registered device_id=${res.device_id}
|
|
48491
48700
|
`);
|
|
48492
|
-
process.stdout.write(
|
|
48493
|
-
`)
|
|
48701
|
+
process.stdout.write(
|
|
48702
|
+
` \x1B[32m\u2713\x1B[0m secret ${res.secret_prefix}\u2026 (hashed on server, never re-sent)
|
|
48703
|
+
`
|
|
48704
|
+
);
|
|
48494
48705
|
process.stdout.write(` \x1B[32m\u2713\x1B[0m claim code ${res.claim_code}
|
|
48495
48706
|
`);
|
|
48496
48707
|
}
|
|
@@ -48523,10 +48734,8 @@ async function cmdAwaitClaim() {
|
|
|
48523
48734
|
}
|
|
48524
48735
|
function emitEvent(opts, event, fields = {}) {
|
|
48525
48736
|
if (!opts.json) return;
|
|
48526
|
-
process.stdout.write(
|
|
48527
|
-
|
|
48528
|
-
`
|
|
48529
|
-
);
|
|
48737
|
+
process.stdout.write(`${JSON.stringify({ v: 1, ts: Date.now(), event, ...fields })}
|
|
48738
|
+
`);
|
|
48530
48739
|
}
|
|
48531
48740
|
async function cmdConnect(opts) {
|
|
48532
48741
|
const step = (msg) => {
|
|
@@ -48629,9 +48838,7 @@ async function cmdConnect(opts) {
|
|
|
48629
48838
|
error: e.message
|
|
48630
48839
|
});
|
|
48631
48840
|
warn(`couldn't prepare summariser model: ${e.message}`);
|
|
48632
|
-
warn(
|
|
48633
|
-
"the background service will retry the download on its first scan"
|
|
48634
|
-
);
|
|
48841
|
+
warn("the background service will retry the download on its first scan");
|
|
48635
48842
|
}
|
|
48636
48843
|
step("Installing/refreshing background service so the agent survives reboots");
|
|
48637
48844
|
let serviceOk = false;
|
|
@@ -48647,9 +48854,7 @@ async function cmdConnect(opts) {
|
|
|
48647
48854
|
} catch (e) {
|
|
48648
48855
|
emitEvent(opts, "service_install_failed", { error: e.message });
|
|
48649
48856
|
warn(`couldn't install service: ${e.message}`);
|
|
48650
|
-
warn(
|
|
48651
|
-
"the agent will not run in the background \u2014 re-run after fixing the issue"
|
|
48652
|
-
);
|
|
48857
|
+
warn("the agent will not run in the background \u2014 re-run after fixing the issue");
|
|
48653
48858
|
}
|
|
48654
48859
|
step("Detecting installed AI tools and signed-in accounts");
|
|
48655
48860
|
let discovered = null;
|
|
@@ -48667,9 +48872,7 @@ async function cmdConnect(opts) {
|
|
|
48667
48872
|
identities: d.identities.length
|
|
48668
48873
|
};
|
|
48669
48874
|
emitEvent(opts, "discovered", discovered);
|
|
48670
|
-
ok(
|
|
48671
|
-
`${discovered.installations} installs \xB7 ${discovered.identities} accounts`
|
|
48672
|
-
);
|
|
48875
|
+
ok(`${discovered.installations} installs \xB7 ${discovered.identities} accounts`);
|
|
48673
48876
|
} catch (e) {
|
|
48674
48877
|
emitEvent(opts, "discovery_failed", { error: e.message });
|
|
48675
48878
|
warn(`couldn't detect accounts: ${e.message}`);
|
|
@@ -48765,9 +48968,7 @@ async function cmdRescan() {
|
|
|
48765
48968
|
console.log(
|
|
48766
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}.`
|
|
48767
48970
|
);
|
|
48768
|
-
console.log(
|
|
48769
|
-
" If the daemon is running, kick it with: modelstat stop && modelstat start"
|
|
48770
|
-
);
|
|
48971
|
+
console.log(" If the daemon is running, kick it with: modelstat stop && modelstat start");
|
|
48771
48972
|
}
|
|
48772
48973
|
async function cmdWatch() {
|
|
48773
48974
|
const { watchForever: watchForever2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
|
|
@@ -48823,10 +49024,10 @@ function fmtTokens(v) {
|
|
|
48823
49024
|
}
|
|
48824
49025
|
async function readLocalStatus() {
|
|
48825
49026
|
try {
|
|
48826
|
-
const { homedir:
|
|
48827
|
-
const { join:
|
|
49027
|
+
const { homedir: homedir10 } = await import("os");
|
|
49028
|
+
const { join: join12 } = await import("path");
|
|
48828
49029
|
const { readFile } = await import("fs/promises");
|
|
48829
|
-
const p =
|
|
49030
|
+
const p = join12(homedir10(), ".modelstat", "last-status.json");
|
|
48830
49031
|
const txt = await readFile(p, "utf8");
|
|
48831
49032
|
return JSON.parse(txt);
|
|
48832
49033
|
} catch {
|
|
@@ -48869,7 +49070,9 @@ async function cmdStats(args) {
|
|
|
48869
49070
|
console.log("device is claimed \u2014 live stats available at:");
|
|
48870
49071
|
console.log(` ${dashboard}`);
|
|
48871
49072
|
if (local) {
|
|
48872
|
-
console.log(
|
|
49073
|
+
console.log(
|
|
49074
|
+
`local agent: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
|
|
49075
|
+
);
|
|
48873
49076
|
const stats = local.stats ?? {};
|
|
48874
49077
|
for (const [k, v] of Object.entries(stats)) console.log(` ${k}: ${v}`);
|
|
48875
49078
|
}
|
|
@@ -48887,7 +49090,9 @@ async function cmdStats(args) {
|
|
|
48887
49090
|
console.log(
|
|
48888
49091
|
`status: ${view.device.agent_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
|
|
48889
49092
|
);
|
|
48890
|
-
console.log(
|
|
49093
|
+
console.log(
|
|
49094
|
+
`claim: ${view.status}${view.status === "unclaimed" ? ` (at ${view.claim_url})` : ""}`
|
|
49095
|
+
);
|
|
48891
49096
|
console.log(`sessions: ${fmtInt(view.analyzed.count)}`);
|
|
48892
49097
|
console.log(`tokens: ${fmtTokens(view.analyzed.totalTokens)}`);
|
|
48893
49098
|
console.log(`cost: ${fmtCost(view.analyzed.totalCostUsd)}`);
|
|
@@ -48912,10 +49117,8 @@ async function cmdJobs(args) {
|
|
|
48912
49117
|
if (!jobs && !ledger) {
|
|
48913
49118
|
const dashboard = `${state.apiUrl.replace(/\/$/, "")}/dashboard/jobs`;
|
|
48914
49119
|
if (asJson) {
|
|
48915
|
-
process.stdout.write(
|
|
48916
|
-
|
|
48917
|
-
`
|
|
48918
|
-
);
|
|
49120
|
+
process.stdout.write(`${JSON.stringify({ paired: true, claimed: true, dashboard })}
|
|
49121
|
+
`);
|
|
48919
49122
|
} else {
|
|
48920
49123
|
console.log("device is claimed \u2014 job queue at:");
|
|
48921
49124
|
console.log(` ${dashboard}`);
|
|
@@ -48959,6 +49162,22 @@ function cmdPaths(args) {
|
|
|
48959
49162
|
console.log(`${k.padEnd(8)} ${String(v)}`);
|
|
48960
49163
|
}
|
|
48961
49164
|
}
|
|
49165
|
+
function cmdToken(args) {
|
|
49166
|
+
if (!state.bearer) {
|
|
49167
|
+
console.error("not paired \u2014 run `npx modelstat@latest` first");
|
|
49168
|
+
process.exit(1);
|
|
49169
|
+
}
|
|
49170
|
+
if (args.includes("--json")) {
|
|
49171
|
+
process.stdout.write(`${JSON.stringify({ token: state.bearer, api: state.apiUrl })}
|
|
49172
|
+
`);
|
|
49173
|
+
return;
|
|
49174
|
+
}
|
|
49175
|
+
process.stdout.write(`${state.bearer}
|
|
49176
|
+
`);
|
|
49177
|
+
if (process.stderr.isTTY) {
|
|
49178
|
+
console.error("(device token \u2014 treat it like a password; rotate via the dashboard if leaked)");
|
|
49179
|
+
}
|
|
49180
|
+
}
|
|
48962
49181
|
function parseConnectOpts(argv) {
|
|
48963
49182
|
return {
|
|
48964
49183
|
json: argv.includes("--json"),
|
|
@@ -48989,6 +49208,14 @@ async function main() {
|
|
|
48989
49208
|
console.log(`\u2713 runtime staged at ${dest}`);
|
|
48990
49209
|
return;
|
|
48991
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
|
+
}
|
|
48992
49219
|
// ── Diagnostics / dev one-shots ────────────────────────────
|
|
48993
49220
|
case "status":
|
|
48994
49221
|
return cmdStatus();
|
|
@@ -48999,6 +49226,9 @@ async function main() {
|
|
|
48999
49226
|
case "paths":
|
|
49000
49227
|
cmdPaths(rest);
|
|
49001
49228
|
return;
|
|
49229
|
+
case "token":
|
|
49230
|
+
cmdToken(rest);
|
|
49231
|
+
return;
|
|
49002
49232
|
case "discover":
|
|
49003
49233
|
return cmdDiscover();
|
|
49004
49234
|
case "scan":
|
|
@@ -49013,24 +49243,39 @@ async function main() {
|
|
|
49013
49243
|
return cmdAwaitClaim();
|
|
49014
49244
|
default:
|
|
49015
49245
|
console.log("usage:");
|
|
49016
|
-
console.log(
|
|
49017
|
-
|
|
49018
|
-
|
|
49019
|
-
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
|
+
);
|
|
49020
49258
|
console.log();
|
|
49021
49259
|
console.log("Diagnostics:");
|
|
49022
49260
|
console.log(" npx modelstat@latest status \u2014 show pairing + service state");
|
|
49023
49261
|
console.log(" npx modelstat@latest stats \u2014 live device summary: sessions \xB7 tokens \xB7 cost (--json)");
|
|
49024
49262
|
console.log(" npx modelstat@latest jobs \u2014 pipeline queue + recent processing ledger (--json)");
|
|
49025
49263
|
console.log(" npx modelstat@latest paths \u2014 print state file + log dir + api URL (--json)");
|
|
49264
|
+
console.log(" npx modelstat@latest token \u2014 print the device token for hosted MCP / API access (--json)");
|
|
49026
49265
|
console.log();
|
|
49027
49266
|
console.log("Dev / one-shots:");
|
|
49028
49267
|
console.log(" npx modelstat@latest scan \u2014 one-shot parse + upload of local JSONL");
|
|
49029
|
-
console.log(
|
|
49030
|
-
|
|
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
|
+
);
|
|
49031
49274
|
console.log(" npx modelstat@latest discover \u2014 one-shot report of installs/identities");
|
|
49032
49275
|
console.log();
|
|
49033
|
-
console.log(
|
|
49276
|
+
console.log(
|
|
49277
|
+
"Internal (called by launchd/systemd, not by humans): _daemon, start, self-register, await-claim"
|
|
49278
|
+
);
|
|
49034
49279
|
process.exit(1);
|
|
49035
49280
|
}
|
|
49036
49281
|
}
|