modelstat 0.0.22 → 0.0.23

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 CHANGED
@@ -7487,6 +7487,107 @@ var init_src2 = __esm({
7487
7487
  }
7488
7488
  });
7489
7489
 
7490
+ // src/identity.ts
7491
+ import {
7492
+ chmodSync,
7493
+ mkdirSync,
7494
+ readFileSync as readFileSync2,
7495
+ renameSync,
7496
+ writeFileSync,
7497
+ existsSync as existsSync3
7498
+ } from "fs";
7499
+ import { homedir as homedir2, hostname as osHostname } from "os";
7500
+ import { join as join2 } from "path";
7501
+ function ensureRoot() {
7502
+ mkdirSync(ROOT, { recursive: true, mode: 448 });
7503
+ }
7504
+ function writeAtomic(meta) {
7505
+ ensureRoot();
7506
+ const tmp = `${IDENTITY_FILE}.${process.pid}.tmp`;
7507
+ writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
7508
+ renameSync(tmp, IDENTITY_FILE);
7509
+ try {
7510
+ chmodSync(IDENTITY_FILE, 384);
7511
+ } catch {
7512
+ }
7513
+ }
7514
+ function identityPath() {
7515
+ return IDENTITY_FILE;
7516
+ }
7517
+ function hasIdentityFile() {
7518
+ return existsSync3(IDENTITY_FILE);
7519
+ }
7520
+ function parseFile() {
7521
+ try {
7522
+ const raw = readFileSync2(IDENTITY_FILE, "utf8");
7523
+ const obj = JSON.parse(raw);
7524
+ if (!obj.deviceUuid || !obj.deviceId || !obj.bearerToken) {
7525
+ return null;
7526
+ }
7527
+ return {
7528
+ deviceUuid: obj.deviceUuid,
7529
+ deviceId: obj.deviceId,
7530
+ bearerToken: obj.bearerToken,
7531
+ claimCode: obj.claimCode ?? null,
7532
+ claimUrl: obj.claimUrl ?? null,
7533
+ hostname: obj.hostname ?? osHostname(),
7534
+ createdAt: obj.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
7535
+ userEmail: obj.userEmail ?? null,
7536
+ defaultOrgId: obj.defaultOrgId ?? null
7537
+ };
7538
+ } catch {
7539
+ return null;
7540
+ }
7541
+ }
7542
+ function loadIdentity(migrateFromConf2) {
7543
+ const fromFile = parseFile();
7544
+ if (fromFile) return fromFile;
7545
+ if (!migrateFromConf2) return null;
7546
+ const legacy = migrateFromConf2();
7547
+ if (!legacy) return null;
7548
+ if (!legacy.deviceUuid || !legacy.deviceId || !legacy.bearerToken) {
7549
+ return null;
7550
+ }
7551
+ const migrated = {
7552
+ deviceUuid: legacy.deviceUuid,
7553
+ deviceId: legacy.deviceId,
7554
+ bearerToken: legacy.bearerToken,
7555
+ claimCode: legacy.claimCode ?? null,
7556
+ claimUrl: legacy.claimUrl ?? null,
7557
+ hostname: osHostname(),
7558
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7559
+ userEmail: legacy.userEmail ?? null,
7560
+ defaultOrgId: legacy.defaultOrgId ?? null
7561
+ };
7562
+ writeAtomic(migrated);
7563
+ return migrated;
7564
+ }
7565
+ function saveIdentity(meta) {
7566
+ writeAtomic(meta);
7567
+ }
7568
+ function backupIdentity() {
7569
+ if (!existsSync3(IDENTITY_FILE)) return null;
7570
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7571
+ const dest = `${IDENTITY_FILE}.bak-${stamp}`;
7572
+ renameSync(IDENTITY_FILE, dest);
7573
+ return dest;
7574
+ }
7575
+ function updateIdentity(patch) {
7576
+ const current = parseFile();
7577
+ if (!current) return null;
7578
+ const merged = { ...current, ...patch };
7579
+ writeAtomic(merged);
7580
+ return merged;
7581
+ }
7582
+ var ROOT, IDENTITY_FILE;
7583
+ var init_identity = __esm({
7584
+ "src/identity.ts"() {
7585
+ "use strict";
7586
+ ROOT = join2(homedir2(), ".modelstat");
7587
+ IDENTITY_FILE = join2(ROOT, "identity.json");
7588
+ }
7589
+ });
7590
+
7490
7591
  // ../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js
7491
7592
  var require_symbols = __commonJS({
7492
7593
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/core/symbols.js"(exports, module) {
@@ -33061,15 +33162,15 @@ function envPaths(name, { suffix = "nodejs" } = {}) {
33061
33162
  }
33062
33163
  return linux(name);
33063
33164
  }
33064
- var homedir2, tmpdir, env, macos, windows, linux;
33165
+ var homedir3, tmpdir, env, macos, windows, linux;
33065
33166
  var init_env_paths = __esm({
33066
33167
  "../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js"() {
33067
33168
  "use strict";
33068
- homedir2 = os.homedir();
33169
+ homedir3 = os.homedir();
33069
33170
  tmpdir = os.tmpdir();
33070
33171
  ({ env } = process2);
33071
33172
  macos = (name) => {
33072
- const library = path.join(homedir2, "Library");
33173
+ const library = path.join(homedir3, "Library");
33073
33174
  return {
33074
33175
  data: path.join(library, "Application Support", name),
33075
33176
  config: path.join(library, "Preferences", name),
@@ -33079,8 +33180,8 @@ var init_env_paths = __esm({
33079
33180
  };
33080
33181
  };
33081
33182
  windows = (name) => {
33082
- const appData = env.APPDATA || path.join(homedir2, "AppData", "Roaming");
33083
- const localAppData = env.LOCALAPPDATA || path.join(homedir2, "AppData", "Local");
33183
+ const appData = env.APPDATA || path.join(homedir3, "AppData", "Roaming");
33184
+ const localAppData = env.LOCALAPPDATA || path.join(homedir3, "AppData", "Local");
33084
33185
  return {
33085
33186
  // Data/config/cache/log are invented by me as Windows isn't opinionated about this
33086
33187
  data: path.join(localAppData, name, "Data"),
@@ -33091,13 +33192,13 @@ var init_env_paths = __esm({
33091
33192
  };
33092
33193
  };
33093
33194
  linux = (name) => {
33094
- const username = path.basename(homedir2);
33195
+ const username = path.basename(homedir3);
33095
33196
  return {
33096
- data: path.join(env.XDG_DATA_HOME || path.join(homedir2, ".local", "share"), name),
33097
- config: path.join(env.XDG_CONFIG_HOME || path.join(homedir2, ".config"), name),
33098
- cache: path.join(env.XDG_CACHE_HOME || path.join(homedir2, ".cache"), name),
33197
+ data: path.join(env.XDG_DATA_HOME || path.join(homedir3, ".local", "share"), name),
33198
+ config: path.join(env.XDG_CONFIG_HOME || path.join(homedir3, ".config"), name),
33199
+ cache: path.join(env.XDG_CACHE_HOME || path.join(homedir3, ".cache"), name),
33099
33200
  // https://wiki.debian.org/XDGBaseDirectorySpecification#state
33100
- log: path.join(env.XDG_STATE_HOME || path.join(homedir2, ".local", "state"), name),
33201
+ log: path.join(env.XDG_STATE_HOME || path.join(homedir3, ".local", "state"), name),
33101
33202
  temp: path.join(tmpdir, username, name)
33102
33203
  };
33103
33204
  };
@@ -33538,9 +33639,9 @@ import { once } from "events";
33538
33639
  import { createWriteStream } from "fs";
33539
33640
  import path3 from "path";
33540
33641
  import { Readable } from "stream";
33541
- function writeFileSync(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
33642
+ function writeFileSync2(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
33542
33643
  if (isString(options))
33543
- return writeFileSync(filePath, data, { encoding: options });
33644
+ return writeFileSync2(filePath, data, { encoding: options });
33544
33645
  const timeout = options.timeout ?? DEFAULT_TIMEOUT_SYNC;
33545
33646
  const retryOptions = { timeout };
33546
33647
  let tempDisposer = null;
@@ -43663,7 +43764,7 @@ var init_source = __esm({
43663
43764
  fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
43664
43765
  } else {
43665
43766
  try {
43666
- writeFileSync(this.path, data, { mode: this.#options.configFileMode });
43767
+ writeFileSync2(this.path, data, { mode: this.#options.configFileMode });
43667
43768
  } catch (error) {
43668
43769
  if (error?.code === "EXDEV") {
43669
43770
  fs2.writeFileSync(this.path, data, { mode: this.#options.configFileMode });
@@ -43765,20 +43866,52 @@ var init_source = __esm({
43765
43866
  });
43766
43867
 
43767
43868
  // src/config.ts
43768
- import { existsSync as existsSync3 } from "fs";
43869
+ import { existsSync as existsSync4 } from "fs";
43769
43870
  import { hostname } from "os";
43770
43871
  import { dirname as dirname2, resolve as resolve3 } from "path";
43771
43872
  import { fileURLToPath } from "url";
43772
- var import_dotenv, here, DEFAULT_API_URL, LEGACY_LOCALHOST_API, store, state;
43873
+ function migrateFromConf() {
43874
+ const bearer = store.get("bearerToken");
43875
+ const deviceUuid = store.get("deviceUuid");
43876
+ const deviceId = store.get("deviceId");
43877
+ if (!bearer || !deviceUuid || !deviceId) return null;
43878
+ return {
43879
+ bearerToken: bearer,
43880
+ deviceId,
43881
+ deviceUuid,
43882
+ claimCode: store.get("claimCode"),
43883
+ claimUrl: store.get("claimUrl"),
43884
+ userEmail: store.get("userEmail"),
43885
+ defaultOrgId: store.get("defaultOrgId")
43886
+ };
43887
+ }
43888
+ function clearConfIdentity() {
43889
+ store.set("bearerToken", null);
43890
+ store.set("deviceId", null);
43891
+ store.set("deviceUuid", null);
43892
+ store.set("claimCode", null);
43893
+ store.set("claimUrl", null);
43894
+ }
43895
+ function writeThrough(patch) {
43896
+ if (!cachedIdentity) {
43897
+ throw new Error(
43898
+ "config: no identity yet \u2014 call state.saveFreshIdentity() first"
43899
+ );
43900
+ }
43901
+ cachedIdentity = { ...cachedIdentity, ...patch };
43902
+ updateIdentity(patch);
43903
+ }
43904
+ var import_dotenv, here, DEFAULT_API_URL, LEGACY_LOCALHOST_API, store, cachedIdentity, state;
43773
43905
  var init_config2 = __esm({
43774
43906
  "src/config.ts"() {
43775
43907
  "use strict";
43776
43908
  import_dotenv = __toESM(require_main(), 1);
43777
43909
  init_source();
43910
+ init_identity();
43778
43911
  here = dirname2(fileURLToPath(import.meta.url));
43779
43912
  for (let d = here, i = 0; i < 8; i++, d = resolve3(d, "..")) {
43780
43913
  const candidate = resolve3(d, ".env");
43781
- if (existsSync3(candidate)) {
43914
+ if (existsSync4(candidate)) {
43782
43915
  (0, import_dotenv.config)({ path: candidate });
43783
43916
  break;
43784
43917
  }
@@ -43804,6 +43937,14 @@ var init_config2 = __esm({
43804
43937
  cursor: {}
43805
43938
  }
43806
43939
  });
43940
+ cachedIdentity = (() => {
43941
+ const had = store.get("bearerToken") !== null;
43942
+ const id = loadIdentity(migrateFromConf);
43943
+ if (id && had && !store.path.includes("fallback")) {
43944
+ clearConfIdentity();
43945
+ }
43946
+ return id;
43947
+ })();
43807
43948
  state = {
43808
43949
  /** Resolution order: env var → stored value (if user ran `setApiUrl`
43809
43950
  * or paired pre-0.0.8) → production default. The legacy localhost
@@ -43817,42 +43958,66 @@ var init_config2 = __esm({
43817
43958
  setApiUrl(v) {
43818
43959
  store.set("apiUrl", v);
43819
43960
  },
43961
+ // ── Identity: backed by ~/.modelstat/identity.json ─────────────
43962
+ /** Seed a fresh identity after a successful self-register. Writes
43963
+ * the file atomically; use `state.backupAndReset()` first if
43964
+ * overwriting an existing identity. */
43965
+ saveFreshIdentity(meta) {
43966
+ const id = {
43967
+ deviceUuid: meta.deviceUuid,
43968
+ deviceId: meta.deviceId,
43969
+ bearerToken: meta.bearerToken,
43970
+ claimCode: meta.claimCode,
43971
+ claimUrl: meta.claimUrl,
43972
+ hostname: hostname(),
43973
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
43974
+ userEmail: null,
43975
+ defaultOrgId: null
43976
+ };
43977
+ saveIdentity(id);
43978
+ cachedIdentity = id;
43979
+ },
43980
+ get identity() {
43981
+ return cachedIdentity;
43982
+ },
43820
43983
  get bearer() {
43821
- return store.get("bearerToken");
43984
+ return cachedIdentity?.bearerToken ?? null;
43822
43985
  },
43823
43986
  setBearer(v) {
43824
- store.set("bearerToken", v);
43987
+ if (v === null) {
43988
+ cachedIdentity = null;
43989
+ return;
43990
+ }
43991
+ writeThrough({ bearerToken: v });
43825
43992
  },
43826
43993
  get deviceId() {
43827
- return store.get("deviceId");
43828
- },
43829
- setDeviceId(v) {
43830
- store.set("deviceId", v);
43831
- },
43832
- get userEmail() {
43833
- return store.get("userEmail");
43834
- },
43835
- setUserEmail(v) {
43836
- store.set("userEmail", v);
43994
+ return cachedIdentity?.deviceId ?? null;
43837
43995
  },
43838
43996
  get deviceUuid() {
43839
- return store.get("deviceUuid");
43840
- },
43841
- setDeviceUuid(v) {
43842
- store.set("deviceUuid", v);
43997
+ return cachedIdentity?.deviceUuid ?? null;
43843
43998
  },
43844
43999
  get claimCode() {
43845
- return store.get("claimCode");
44000
+ return cachedIdentity?.claimCode ?? null;
43846
44001
  },
43847
44002
  setClaimCode(v) {
43848
- store.set("claimCode", v);
44003
+ if (!cachedIdentity) return;
44004
+ writeThrough({ claimCode: v });
43849
44005
  },
43850
44006
  get claimUrl() {
43851
- return store.get("claimUrl");
44007
+ return cachedIdentity?.claimUrl ?? null;
43852
44008
  },
43853
44009
  setClaimUrl(v) {
43854
- store.set("claimUrl", v);
44010
+ if (!cachedIdentity) return;
44011
+ writeThrough({ claimUrl: v });
44012
+ },
44013
+ get userEmail() {
44014
+ return cachedIdentity?.userEmail ?? null;
43855
44015
  },
44016
+ setUserEmail(v) {
44017
+ if (!cachedIdentity) return;
44018
+ writeThrough({ userEmail: v });
44019
+ },
44020
+ // ── Runtime state: stays in conf ───────────────────────────────
43856
44021
  getCursor(path5) {
43857
44022
  return store.get("cursor")[path5];
43858
44023
  },
@@ -44402,23 +44567,23 @@ var init_pipeline2 = __esm({
44402
44567
 
44403
44568
  // src/scan.ts
44404
44569
  import { readdir, stat as stat2 } from "fs/promises";
44405
- import { homedir as homedir3 } from "os";
44406
- import { join as join2 } from "path";
44570
+ import { homedir as homedir4 } from "os";
44571
+ import { join as join3 } from "path";
44407
44572
  async function scanAll(cb = {}) {
44408
44573
  const deviceId = state.deviceId;
44409
44574
  if (!deviceId) throw new Error("agent not enrolled \u2014 run `register` first");
44410
44575
  const jobs = [];
44411
44576
  try {
44412
- const base = join2(homedir3(), ".claude/projects");
44577
+ const base = join3(homedir4(), ".claude/projects");
44413
44578
  const projects = await readdir(base).catch(() => []);
44414
44579
  for (const p of projects) {
44415
- const dir = join2(base, p);
44580
+ const dir = join3(base, p);
44416
44581
  const ds = await stat2(dir).catch(() => null);
44417
44582
  if (!ds?.isDirectory()) continue;
44418
44583
  const files = await readdir(dir);
44419
44584
  for (const f of files) {
44420
44585
  if (!f.endsWith(".jsonl")) continue;
44421
- const full = join2(dir, f);
44586
+ const full = join3(dir, f);
44422
44587
  jobs.push({
44423
44588
  path: full,
44424
44589
  parse: async () => {
@@ -44432,17 +44597,17 @@ async function scanAll(cb = {}) {
44432
44597
  console.warn("claude scan skipped:", e.message);
44433
44598
  }
44434
44599
  try {
44435
- const base = join2(homedir3(), ".codex/sessions");
44600
+ const base = join3(homedir4(), ".codex/sessions");
44436
44601
  const years = await readdir(base).catch(() => []);
44437
44602
  for (const y of years) {
44438
- const months = await readdir(join2(base, y)).catch(() => []);
44603
+ const months = await readdir(join3(base, y)).catch(() => []);
44439
44604
  for (const m of months) {
44440
- const days = await readdir(join2(base, y, m)).catch(() => []);
44605
+ const days = await readdir(join3(base, y, m)).catch(() => []);
44441
44606
  for (const d of days) {
44442
- const files = await readdir(join2(base, y, m, d)).catch(() => []);
44607
+ const files = await readdir(join3(base, y, m, d)).catch(() => []);
44443
44608
  for (const f of files) {
44444
44609
  if (!f.startsWith("rollout-") || !f.endsWith(".jsonl")) continue;
44445
- const full = join2(base, y, m, d, f);
44610
+ const full = join3(base, y, m, d, f);
44446
44611
  jobs.push({
44447
44612
  path: full,
44448
44613
  parse: async () => {
@@ -44516,7 +44681,7 @@ var init_scan = __esm({
44516
44681
  init_pipeline2();
44517
44682
  init_config2();
44518
44683
  init_api();
44519
- AGENT_VERSION = "agent-dev-0.0.22";
44684
+ AGENT_VERSION = "agent-dev-0.0.23";
44520
44685
  BATCH_MAX_EVENTS = 2e3;
44521
44686
  }
44522
44687
  });
@@ -44524,17 +44689,17 @@ var init_scan = __esm({
44524
44689
  // src/lock.ts
44525
44690
  import {
44526
44691
  closeSync,
44527
- existsSync as existsSync5,
44528
- mkdirSync as mkdirSync2,
44692
+ existsSync as existsSync6,
44693
+ mkdirSync as mkdirSync3,
44529
44694
  openSync,
44530
- readFileSync as readFileSync2,
44531
- renameSync,
44695
+ readFileSync as readFileSync3,
44696
+ renameSync as renameSync2,
44532
44697
  unlinkSync as unlinkSync2,
44533
- writeFileSync as writeFileSync3,
44698
+ writeFileSync as writeFileSync4,
44534
44699
  writeSync
44535
44700
  } from "fs";
44536
- import { homedir as homedir5 } from "os";
44537
- import { join as join4 } from "path";
44701
+ import { homedir as homedir6 } from "os";
44702
+ import { join as join5 } from "path";
44538
44703
  function isProcessAlive(pid) {
44539
44704
  if (!pid || pid <= 0) return false;
44540
44705
  try {
@@ -44548,7 +44713,7 @@ function isProcessAlive(pid) {
44548
44713
  }
44549
44714
  function readLock() {
44550
44715
  try {
44551
- const raw = readFileSync2(LOCK_FILE, "utf8");
44716
+ const raw = readFileSync3(LOCK_FILE, "utf8");
44552
44717
  const obj = JSON.parse(raw);
44553
44718
  if (typeof obj.pid !== "number") return null;
44554
44719
  return {
@@ -44562,7 +44727,7 @@ function readLock() {
44562
44727
  }
44563
44728
  }
44564
44729
  function writeLockAtomic(meta) {
44565
- mkdirSync2(LOCK_DIR, { recursive: true });
44730
+ mkdirSync3(LOCK_DIR, { recursive: true });
44566
44731
  const tmp = `${LOCK_FILE}.${meta.pid}.${Date.now()}.tmp`;
44567
44732
  const fd = openSync(tmp, "wx");
44568
44733
  try {
@@ -44570,7 +44735,7 @@ function writeLockAtomic(meta) {
44570
44735
  } finally {
44571
44736
  closeSync(fd);
44572
44737
  }
44573
- renameSync(tmp, LOCK_FILE);
44738
+ renameSync2(tmp, LOCK_FILE);
44574
44739
  }
44575
44740
  function removeLockIfOwned(ownerPid) {
44576
44741
  const lock = readLock();
@@ -44635,8 +44800,8 @@ var LOCK_DIR, LOCK_FILE;
44635
44800
  var init_lock = __esm({
44636
44801
  "src/lock.ts"() {
44637
44802
  "use strict";
44638
- LOCK_DIR = join4(homedir5(), ".modelstat");
44639
- LOCK_FILE = join4(LOCK_DIR, "daemon.lock");
44803
+ LOCK_DIR = join5(homedir6(), ".modelstat");
44804
+ LOCK_FILE = join5(LOCK_DIR, "daemon.lock");
44640
44805
  }
44641
44806
  });
44642
44807
 
@@ -46366,7 +46531,7 @@ __export(daemon_exports, {
46366
46531
  setProgress: () => setProgress,
46367
46532
  setQueue: () => setQueue
46368
46533
  });
46369
- import { existsSync as existsSync6, statSync as statSync2 } from "fs";
46534
+ import { existsSync as existsSync7, statSync as statSync2 } from "fs";
46370
46535
  function setPhase(phase, message) {
46371
46536
  status.phase = phase;
46372
46537
  status.message = message ?? null;
@@ -46488,21 +46653,21 @@ async function runDaemon(opts = {}) {
46488
46653
  await runDiscovery();
46489
46654
  await runScanCycle("startup");
46490
46655
  const chokidar = (await Promise.resolve().then(() => (init_esm2(), esm_exports))).default;
46491
- const { homedir: homedir7, platform: platform5 } = await import("os");
46492
- const { join: join8 } = await import("path");
46493
- const home2 = homedir7();
46656
+ const { homedir: homedir8, platform: platform5 } = await import("os");
46657
+ const { join: join9 } = await import("path");
46658
+ const home2 = homedir8();
46494
46659
  const dirs = [
46495
- join8(home2, ".claude/projects"),
46496
- join8(home2, ".codex/sessions"),
46497
- join8(home2, ".cursor/ai-tracking"),
46498
- join8(home2, ".gemini"),
46660
+ join9(home2, ".claude/projects"),
46661
+ join9(home2, ".codex/sessions"),
46662
+ join9(home2, ".cursor/ai-tracking"),
46663
+ join9(home2, ".gemini"),
46499
46664
  ...platform5() === "darwin" ? [
46500
- join8(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
46501
- join8(home2, "Library/Application Support/Claude")
46665
+ join9(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
46666
+ join9(home2, "Library/Application Support/Claude")
46502
46667
  ] : [
46503
- join8(home2, ".config/Cursor/User/workspaceStorage")
46668
+ join9(home2, ".config/Cursor/User/workspaceStorage")
46504
46669
  ]
46505
- ].filter((p) => existsSync6(p) && statSync2(p).isDirectory());
46670
+ ].filter((p) => existsSync7(p) && statSync2(p).isDirectory());
46506
46671
  setPhase("watching", `Watching ${dirs.length} directories`);
46507
46672
  const watcher = chokidar.watch(dirs, {
46508
46673
  persistent: true,
@@ -46549,7 +46714,7 @@ var init_daemon = __esm({
46549
46714
  init_config2();
46550
46715
  init_lock();
46551
46716
  init_scan();
46552
- AGENT_VERSION2 = "agent-dev-0.0.22";
46717
+ AGENT_VERSION2 = "agent-dev-0.0.23";
46553
46718
  HEARTBEAT_INTERVAL_MS = 1e4;
46554
46719
  SCAN_INTERVAL_MS = 5 * 60 * 1e3;
46555
46720
  status = {
@@ -46569,37 +46734,37 @@ var watch_exports = {};
46569
46734
  __export(watch_exports, {
46570
46735
  watchForever: () => watchForever
46571
46736
  });
46572
- import { existsSync as existsSync7 } from "fs";
46573
- import { homedir as homedir6, platform as platform3 } from "os";
46574
- import { join as join7 } from "path";
46737
+ import { existsSync as existsSync8 } from "fs";
46738
+ import { homedir as homedir7, platform as platform3 } from "os";
46739
+ import { join as join8 } from "path";
46575
46740
  function resolveWatchDirs() {
46576
- const home2 = homedir6();
46577
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join7(home2, ".config");
46578
- const xdgData = process.env.XDG_DATA_HOME ?? join7(home2, ".local/share");
46741
+ const home2 = homedir7();
46742
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join8(home2, ".config");
46743
+ const xdgData = process.env.XDG_DATA_HOME ?? join8(home2, ".local/share");
46579
46744
  const candidates = [
46580
46745
  // universal (default HOME-rooted CLI data dirs)
46581
- join7(home2, ".claude/projects"),
46582
- join7(home2, ".codex/sessions"),
46583
- join7(home2, ".cursor/ai-tracking"),
46584
- join7(home2, ".gemini"),
46585
- join7(home2, ".aider"),
46746
+ join8(home2, ".claude/projects"),
46747
+ join8(home2, ".codex/sessions"),
46748
+ join8(home2, ".cursor/ai-tracking"),
46749
+ join8(home2, ".gemini"),
46750
+ join8(home2, ".aider"),
46586
46751
  // XDG / Linux
46587
- join7(xdgConfig, "claude/projects"),
46588
- join7(xdgConfig, "codex/sessions"),
46589
- join7(xdgConfig, "Cursor/User/workspaceStorage"),
46590
- join7(xdgConfig, "Code/User/workspaceStorage"),
46591
- join7(xdgConfig, "Code - Insiders/User/workspaceStorage"),
46592
- join7(xdgData, "claude/projects"),
46752
+ join8(xdgConfig, "claude/projects"),
46753
+ join8(xdgConfig, "codex/sessions"),
46754
+ join8(xdgConfig, "Cursor/User/workspaceStorage"),
46755
+ join8(xdgConfig, "Code/User/workspaceStorage"),
46756
+ join8(xdgConfig, "Code - Insiders/User/workspaceStorage"),
46757
+ join8(xdgData, "claude/projects"),
46593
46758
  // macOS
46594
46759
  ...platform3() === "darwin" ? [
46595
- join7(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
46596
- join7(home2, "Library/Application Support/Claude"),
46597
- join7(home2, "Library/Application Support/Code/User/workspaceStorage"),
46598
- join7(home2, "Library/Application Support/Windsurf/User/workspaceStorage"),
46599
- join7(home2, "Library/Application Support/Zed")
46760
+ join8(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
46761
+ join8(home2, "Library/Application Support/Claude"),
46762
+ join8(home2, "Library/Application Support/Code/User/workspaceStorage"),
46763
+ join8(home2, "Library/Application Support/Windsurf/User/workspaceStorage"),
46764
+ join8(home2, "Library/Application Support/Zed")
46600
46765
  ] : []
46601
46766
  ];
46602
- return Array.from(new Set(candidates)).filter((p) => existsSync7(p));
46767
+ return Array.from(new Set(candidates)).filter((p) => existsSync8(p));
46603
46768
  }
46604
46769
  async function safeScan(reason) {
46605
46770
  if (scanning) {
@@ -46659,51 +46824,53 @@ var init_watch = __esm({
46659
46824
 
46660
46825
  // src/cli.ts
46661
46826
  init_src2();
46827
+ init_identity();
46662
46828
  init_api();
46663
46829
  init_config2();
46664
46830
  init_scan();
46665
46831
  import { spawn } from "child_process";
46666
46832
  import { randomBytes } from "crypto";
46667
46833
  import { platform as platform4, release, arch as cpuArch, hostname as hostname2 } from "os";
46834
+ import { createInterface as createInterface3 } from "readline";
46668
46835
 
46669
46836
  // src/service.ts
46670
46837
  import { spawnSync } from "child_process";
46671
46838
  import {
46672
46839
  copyFileSync,
46673
- existsSync as existsSync4,
46674
- mkdirSync,
46840
+ existsSync as existsSync5,
46841
+ mkdirSync as mkdirSync2,
46675
46842
  unlinkSync,
46676
- writeFileSync as writeFileSync2
46843
+ writeFileSync as writeFileSync3
46677
46844
  } from "fs";
46678
- import { homedir as homedir4, platform as platform2, userInfo } from "os";
46679
- import { dirname as dirname4, join as join3 } from "path";
46845
+ import { homedir as homedir5, platform as platform2, userInfo } from "os";
46846
+ import { dirname as dirname4, join as join4 } from "path";
46680
46847
  import { fileURLToPath as fileURLToPath2 } from "url";
46681
46848
  var SERVICE_LABEL = "ai.modelstat.agent";
46682
46849
  var SYSTEMD_UNIT = "modelstat";
46683
46850
  function home() {
46684
- return homedir4();
46851
+ return homedir5();
46685
46852
  }
46686
46853
  function stateDir() {
46687
- return join3(home(), ".modelstat");
46854
+ return join4(home(), ".modelstat");
46688
46855
  }
46689
46856
  function binDir() {
46690
- return join3(stateDir(), "bin");
46857
+ return join4(stateDir(), "bin");
46691
46858
  }
46692
46859
  function logDir() {
46693
- return join3(stateDir(), "logs");
46860
+ return join4(stateDir(), "logs");
46694
46861
  }
46695
46862
  function installedCliPath() {
46696
- return join3(binDir(), "modelstat.mjs");
46863
+ return join4(binDir(), "modelstat.mjs");
46697
46864
  }
46698
46865
  function runningCliPath() {
46699
46866
  return fileURLToPath2(import.meta.url).replace(/service\.(mjs|js|ts)$/, "cli.mjs");
46700
46867
  }
46701
46868
  function installBundle() {
46702
- mkdirSync(binDir(), { recursive: true });
46703
- mkdirSync(logDir(), { recursive: true });
46869
+ mkdirSync2(binDir(), { recursive: true });
46870
+ mkdirSync2(logDir(), { recursive: true });
46704
46871
  const src = runningCliPath();
46705
46872
  const dest = installedCliPath();
46706
- if (!existsSync4(src)) {
46873
+ if (!existsSync5(src)) {
46707
46874
  throw new Error(
46708
46875
  `Can't find the CLI bundle to install from (${src}). Are you running a local dev build?`
46709
46876
  );
@@ -46715,21 +46882,21 @@ function nodeBinary() {
46715
46882
  return process.execPath;
46716
46883
  }
46717
46884
  function plistPath() {
46718
- return join3(home(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
46885
+ return join4(home(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
46719
46886
  }
46720
46887
  function locateTrayExecutable() {
46721
46888
  const candidates = [
46722
- join3(home(), "Applications", "ModelstatTray.app", "Contents", "MacOS", "modelstat-tray"),
46889
+ join4(home(), "Applications", "ModelstatTray.app", "Contents", "MacOS", "modelstat-tray"),
46723
46890
  "/Applications/ModelstatTray.app/Contents/MacOS/modelstat-tray"
46724
46891
  ];
46725
46892
  for (const p of candidates) {
46726
- if (existsSync4(p)) return p;
46893
+ if (existsSync5(p)) return p;
46727
46894
  }
46728
46895
  return null;
46729
46896
  }
46730
46897
  function writePlist(cliPath) {
46731
46898
  const p = plistPath();
46732
- mkdirSync(dirname4(p), { recursive: true });
46899
+ mkdirSync2(dirname4(p), { recursive: true });
46733
46900
  const tray = locateTrayExecutable();
46734
46901
  const programArgs = tray ? ` <string>${tray}</string>` : [
46735
46902
  ` <string>${nodeBinary()}</string>`,
@@ -46749,8 +46916,8 @@ ${programArgs}
46749
46916
  <key>KeepAlive</key>
46750
46917
  <dict><key>SuccessfulExit</key><false/></dict>
46751
46918
  <key>ThrottleInterval</key><integer>30</integer>
46752
- <key>StandardOutPath</key><string>${join3(logDir(), "out.log")}</string>
46753
- <key>StandardErrorPath</key><string>${join3(logDir(), "err.log")}</string>
46919
+ <key>StandardOutPath</key><string>${join4(logDir(), "out.log")}</string>
46920
+ <key>StandardErrorPath</key><string>${join4(logDir(), "err.log")}</string>
46754
46921
  <key>EnvironmentVariables</key>
46755
46922
  <dict>
46756
46923
  <key>PATH</key><string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
@@ -46759,7 +46926,7 @@ ${programArgs}
46759
46926
  </dict>
46760
46927
  </plist>
46761
46928
  `;
46762
- writeFileSync2(p, plist, { mode: 420 });
46929
+ writeFileSync3(p, plist, { mode: 420 });
46763
46930
  return p;
46764
46931
  }
46765
46932
  function launchctl(args) {
@@ -46790,7 +46957,7 @@ function macUninstall() {
46790
46957
  const target = `gui/${uid}/${SERVICE_LABEL}`;
46791
46958
  launchctl(["bootout", target]);
46792
46959
  const plist = plistPath();
46793
- if (existsSync4(plist)) {
46960
+ if (existsSync5(plist)) {
46794
46961
  try {
46795
46962
  unlinkSync(plist);
46796
46963
  } catch {
@@ -46803,12 +46970,12 @@ function macStatus() {
46803
46970
  return { running: r.ok, hint: r.ok ? "launchd managed" : "not installed" };
46804
46971
  }
46805
46972
  function systemdUnitPath() {
46806
- const xdg = process.env.XDG_CONFIG_HOME ?? join3(home(), ".config");
46807
- return join3(xdg, "systemd", "user", `${SYSTEMD_UNIT}.service`);
46973
+ const xdg = process.env.XDG_CONFIG_HOME ?? join4(home(), ".config");
46974
+ return join4(xdg, "systemd", "user", `${SYSTEMD_UNIT}.service`);
46808
46975
  }
46809
46976
  function writeSystemdUnit(cliPath) {
46810
46977
  const unitPath = systemdUnitPath();
46811
- mkdirSync(dirname4(unitPath), { recursive: true });
46978
+ mkdirSync2(dirname4(unitPath), { recursive: true });
46812
46979
  const unit = `[Unit]
46813
46980
  Description=modelstat agent
46814
46981
  Documentation=https://modelstat.ai
@@ -46823,13 +46990,13 @@ RestartSec=10
46823
46990
  # Don't restart-storm if the service is persistently unreachable.
46824
46991
  StartLimitIntervalSec=300
46825
46992
  StartLimitBurst=10
46826
- StandardOutput=append:${join3(logDir(), "out.log")}
46827
- StandardError=append:${join3(logDir(), "err.log")}
46993
+ StandardOutput=append:${join4(logDir(), "out.log")}
46994
+ StandardError=append:${join4(logDir(), "err.log")}
46828
46995
 
46829
46996
  [Install]
46830
46997
  WantedBy=default.target
46831
46998
  `;
46832
- writeFileSync2(unitPath, unit, { mode: 420 });
46999
+ writeFileSync3(unitPath, unit, { mode: 420 });
46833
47000
  return unitPath;
46834
47001
  }
46835
47002
  function systemctl(args) {
@@ -46849,7 +47016,7 @@ function linuxInstall() {
46849
47016
  function linuxUninstall() {
46850
47017
  systemctl(["disable", "--now", `${SYSTEMD_UNIT}.service`]);
46851
47018
  const unit = systemdUnitPath();
46852
- if (existsSync4(unit)) {
47019
+ if (existsSync5(unit)) {
46853
47020
  try {
46854
47021
  unlinkSync(unit);
46855
47022
  } catch {
@@ -46893,9 +47060,9 @@ function logsDir() {
46893
47060
  }
46894
47061
  function installTrayApp(sourceAppPath) {
46895
47062
  if (platform2() !== "darwin") return null;
46896
- if (!existsSync4(sourceAppPath)) return null;
46897
- const dest = join3(home(), "Applications", "ModelstatTray.app");
46898
- mkdirSync(dirname4(dest), { recursive: true });
47063
+ if (!existsSync5(sourceAppPath)) return null;
47064
+ const dest = join4(home(), "Applications", "ModelstatTray.app");
47065
+ mkdirSync2(dirname4(dest), { recursive: true });
46899
47066
  spawnSync("rm", ["-rf", dest]);
46900
47067
  const r = spawnSync("cp", ["-R", sourceAppPath, dest], { encoding: "utf8" });
46901
47068
  if (r.status !== 0) {
@@ -46908,25 +47075,25 @@ function bundledTrayAppPath() {
46908
47075
  const here2 = dirname4(fileURLToPath2(import.meta.url));
46909
47076
  const candidates = [
46910
47077
  // Pre-built .app — CI with codesigning drops one here.
46911
- join3(here2, "..", "vendor", "ModelstatTray.app"),
47078
+ join4(here2, "..", "vendor", "ModelstatTray.app"),
46912
47079
  // Local dev layout: apps/agent-dev/src/service.ts → ../../tray-mac/build/ModelstatTray.app
46913
- join3(here2, "..", "..", "tray-mac", "build", "ModelstatTray.app")
47080
+ join4(here2, "..", "..", "tray-mac", "build", "ModelstatTray.app")
46914
47081
  ];
46915
47082
  for (const c of candidates) {
46916
- if (existsSync4(c)) return c;
47083
+ if (existsSync5(c)) return c;
46917
47084
  }
46918
47085
  const sourceDirs = [
46919
- join3(here2, "..", "vendor", "tray-mac"),
46920
- join3(here2, "..", "..", "tray-mac")
47086
+ join4(here2, "..", "vendor", "tray-mac"),
47087
+ join4(here2, "..", "..", "tray-mac")
46921
47088
  ];
46922
47089
  for (const src of sourceDirs) {
46923
- const build = join3(src, "build-app.sh");
46924
- if (!existsSync4(build)) continue;
47090
+ const build = join4(src, "build-app.sh");
47091
+ if (!existsSync5(build)) continue;
46925
47092
  if (!hasSwift()) return null;
46926
47093
  const r = spawnSync("bash", [build], { cwd: src, encoding: "utf8" });
46927
47094
  if (r.status === 0) {
46928
- const app = join3(src, "build", "ModelstatTray.app");
46929
- if (existsSync4(app)) return app;
47095
+ const app = join4(src, "build", "ModelstatTray.app");
47096
+ if (existsSync5(app)) return app;
46930
47097
  }
46931
47098
  }
46932
47099
  return null;
@@ -46942,6 +47109,20 @@ function trayStatus() {
46942
47109
  }
46943
47110
 
46944
47111
  // src/cli.ts
47112
+ async function confirmPrompt(question, defaultYes) {
47113
+ if (process.stdin.isTTY !== true) return defaultYes;
47114
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
47115
+ try {
47116
+ const raw = await new Promise((resolve6) => rl.question(question, resolve6));
47117
+ const ans = raw.trim().toLowerCase();
47118
+ if (ans === "") return defaultYes;
47119
+ if (ans === "y" || ans === "yes") return true;
47120
+ if (ans === "n" || ans === "no") return false;
47121
+ return defaultYes;
47122
+ } finally {
47123
+ rl.close();
47124
+ }
47125
+ }
46945
47126
  function tryOpenBrowser(url) {
46946
47127
  const p = platform4();
46947
47128
  const cmd = p === "darwin" ? "open" : p === "win32" ? "cmd" : "xdg-open";
@@ -47008,11 +47189,13 @@ async function cmdSelfRegister() {
47008
47189
  device_uuid: deviceUuid,
47009
47190
  fingerprint
47010
47191
  });
47011
- state.setDeviceUuid(res.device_uuid);
47012
- state.setDeviceId(res.device_id);
47013
- state.setBearer(res.device_secret);
47014
- state.setClaimCode(res.claim_code);
47015
- state.setClaimUrl(res.claim_url);
47192
+ state.saveFreshIdentity({
47193
+ deviceUuid: res.device_uuid,
47194
+ deviceId: res.device_id,
47195
+ bearerToken: res.device_secret,
47196
+ claimCode: res.claim_code,
47197
+ claimUrl: res.claim_url
47198
+ });
47016
47199
  process.stdout.write(` \x1B[32m\u2713\x1B[0m registered device_id=${res.device_id}
47017
47200
  `);
47018
47201
  process.stdout.write(` \x1B[32m\u2713\x1B[0m secret ${res.secret_prefix}\u2026 (hashed on server, never re-sent)
@@ -47072,19 +47255,21 @@ async function cmdConnect(opts) {
47072
47255
  };
47073
47256
  const wipeAndSelfRegister = async (reason) => {
47074
47257
  warn(`${reason} \u2014 re-registering this device`);
47258
+ const bak = backupIdentity();
47259
+ if (bak) warn(`old identity moved to ${bak}`);
47075
47260
  state.setBearer(null);
47076
- state.setDeviceId(null);
47077
- state.setDeviceUuid(null);
47078
- state.setClaimCode(null);
47079
- state.setClaimUrl(null);
47080
47261
  await cmdSelfRegister();
47081
47262
  };
47082
- if (!state.deviceUuid || !state.bearer || !state.deviceId) {
47263
+ if (opts.fresh && hasIdentityFile()) {
47264
+ step("`--fresh` passed \u2014 minting a new device identity");
47265
+ await wipeAndSelfRegister("forced fresh start");
47266
+ } else if (!state.deviceUuid || !state.bearer || !state.deviceId) {
47083
47267
  step("Registering this device with modelstat.ai");
47084
47268
  await cmdSelfRegister();
47085
47269
  } else {
47086
47270
  step("Re-using existing device identity");
47087
47271
  ok(`device ${state.deviceId}`);
47272
+ ok(`identity file ${identityPath()}`);
47088
47273
  try {
47089
47274
  const me = await fetchDeviceMe(state.bearer);
47090
47275
  if (me.claim_code && me.claim_code !== state.claimCode) {
@@ -47096,6 +47281,15 @@ async function cmdConnect(opts) {
47096
47281
  }
47097
47282
  } catch (e) {
47098
47283
  if (e instanceof DeviceMeUnauthorized) {
47284
+ const interactive = !opts.yes && process.stdin.isTTY === true;
47285
+ if (interactive) {
47286
+ const prompt = "cached credentials no longer accepted by the server. Re-register this device? [Y/n] ";
47287
+ const answer = await confirmPrompt(prompt, true);
47288
+ if (!answer) {
47289
+ warn("keeping existing identity; connect aborted");
47290
+ return;
47291
+ }
47292
+ }
47099
47293
  await wipeAndSelfRegister("cached credentials no longer valid");
47100
47294
  } else {
47101
47295
  warn(`couldn't refresh device state: ${e.message}`);
@@ -47375,7 +47569,9 @@ function cmdPaths(args) {
47375
47569
  function parseConnectOpts(argv) {
47376
47570
  return {
47377
47571
  json: argv.includes("--json"),
47378
- noBrowser: argv.includes("--no-browser")
47572
+ noBrowser: argv.includes("--no-browser"),
47573
+ fresh: argv.includes("--fresh"),
47574
+ yes: argv.includes("--yes") || argv.includes("-y")
47379
47575
  };
47380
47576
  }
47381
47577
  async function main() {