@sunasteriskrnd/takumi 1.0.0-dev.42 → 1.0.0-dev.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +149 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19815,7 +19815,7 @@ var package_default;
19815
19815
  var init_package = __esm(() => {
19816
19816
  package_default = {
19817
19817
  name: "@sunasteriskrnd/takumi",
19818
- version: "1.0.0-dev.42",
19818
+ version: "1.0.0-dev.44",
19819
19819
  description: "CLI tool for bootstrapping and managing Takumi projects",
19820
19820
  type: "module",
19821
19821
  repository: {
@@ -37591,6 +37591,13 @@ var init_install_error_handler = __esm(() => {
37591
37591
 
37592
37592
  // src/services/package-installer/skills-installer.ts
37593
37593
  import { join as join38 } from "node:path";
37594
+ function buildSkillsScriptEnv() {
37595
+ return {
37596
+ ...process.env,
37597
+ NON_INTERACTIVE: "1",
37598
+ TAKUMI_CLI_VERSION: getPackageVersion2()
37599
+ };
37600
+ }
37594
37601
  async function installSkillsDependencies(skillsDir, options2 = {}) {
37595
37602
  const { skipConfirm = false, withSudo = false } = options2;
37596
37603
  const displayName = "Skills Dependencies";
@@ -37687,7 +37694,7 @@ async function installSkillsDependencies(skillsDir, options2 = {}) {
37687
37694
  logger.info("Installation cancelled by user");
37688
37695
  logger.info("");
37689
37696
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
37690
- logger.info(` ${platform3 === "win32" ? `powershell -File "${scriptPath}"` : `bash ${scriptPath}`}`);
37697
+ logger.info(` ${platform3 === "win32" ? `$env:TKM_SKIP_CLI_VERSION_CHECK=1; powershell -File "${scriptPath}"` : `TKM_SKIP_CLI_VERSION_CHECK=1 bash ${scriptPath}`}`);
37691
37698
  logger.info("");
37692
37699
  logger.info("Or see complete guide:");
37693
37700
  logger.info(` ${join38(skillsDir, "INSTALLATION.md")}`);
@@ -37760,10 +37767,7 @@ async function installSkillsDependencies(skillsDir, options2 = {}) {
37760
37767
  }
37761
37768
  }
37762
37769
  }
37763
- const scriptEnv = {
37764
- ...process.env,
37765
- NON_INTERACTIVE: "1"
37766
- };
37770
+ const scriptEnv = buildSkillsScriptEnv();
37767
37771
  if (platform3 === "win32") {
37768
37772
  await executeInteractiveScript("powershell.exe", ["-NoLogo", "-ExecutionPolicy", "Bypass", "-File", scriptPath, "-Y"], {
37769
37773
  timeout: 600000,
@@ -37847,6 +37851,7 @@ async function handleSkillsInstallation(skillsDir, options2 = {}) {
37847
37851
  }
37848
37852
  }
37849
37853
  var init_skills_installer = __esm(() => {
37854
+ init_version_display();
37850
37855
  init_environment();
37851
37856
  init_logger();
37852
37857
  init_install_error_handler();
@@ -53480,6 +53485,7 @@ function tryOpenBrowser(target) {
53480
53485
  }
53481
53486
 
53482
53487
  // src/domains/sessions/server.ts
53488
+ init_logger();
53483
53489
  import * as http from "node:http";
53484
53490
 
53485
53491
  // src/domains/sessions/analytics.ts
@@ -53516,16 +53522,26 @@ function accumulateTokens(a3, b3) {
53516
53522
  a3.cacheRead += b3.cacheRead;
53517
53523
  a3.cacheWrite += b3.cacheWrite;
53518
53524
  }
53519
- function buildLast30(byDay, now) {
53525
+ function buildDenseWindow(byDay, startDayMs, endDayMs) {
53520
53526
  const out = [];
53521
- const todayMs = Date.parse(`${dayKey(now)}T00:00:00.000Z`);
53522
- for (let i = LAST_N_DAYS - 1;i >= 0; i -= 1) {
53523
- const date = dayKey(todayMs - i * DAY_MS);
53527
+ for (let ms = startDayMs;ms <= endDayMs; ms += DAY_MS) {
53528
+ const date = dayKey(ms);
53524
53529
  out.push({ date, count: byDay.get(date) ?? 0 });
53525
53530
  }
53526
53531
  return out;
53527
53532
  }
53528
- function foldAnalytics(aggregates, now) {
53533
+ function windowBounds(now, range) {
53534
+ if (!range) {
53535
+ const todayMs = Date.parse(`${dayKey(now)}T00:00:00.000Z`);
53536
+ return { start: todayMs - (LAST_N_DAYS - 1) * DAY_MS, end: todayMs };
53537
+ }
53538
+ const lo = Math.min(range.from, range.to);
53539
+ const hi = Math.max(range.from, range.to);
53540
+ const end = Date.parse(`${dayKey(hi)}T00:00:00.000Z`);
53541
+ const start = Math.max(Date.parse(`${dayKey(lo)}T00:00:00.000Z`), end - (PER_DAY_MAX - 1) * DAY_MS);
53542
+ return { start, end };
53543
+ }
53544
+ function foldAnalytics(aggregates, now, range) {
53529
53545
  const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
53530
53546
  const eventsByDay = new Map;
53531
53547
  const tokensByDow = new Map;
@@ -53580,7 +53596,8 @@ function foldAnalytics(aggregates, now) {
53580
53596
  const perDay = [...eventsByDay.entries()].filter(([date]) => Date.parse(`${date}T00:00:00.000Z`) >= oldestAllowedMs).map(([date, count]) => ({ date, count })).sort((a3, b3) => a3.date.localeCompare(b3.date));
53581
53597
  const byProjectAll = [...tokensByProject.entries()].map(([label, tokens]) => ({ label, tokens })).sort((a3, b3) => b3.tokens - a3.tokens || a3.label.localeCompare(b3.label));
53582
53598
  const byProject = byProjectAll.slice(0, TOP_PROJECTS);
53583
- const daily = buildLast30(tokensByDay, now);
53599
+ const { start: windowStart, end: windowEnd } = windowBounds(now, range);
53600
+ const daily = buildDenseWindow(tokensByDay, windowStart, windowEnd);
53584
53601
  const rankedModels = [...tokensByModel.entries()].map(([label, tokens]) => ({ label, tokens, total: sumTokens(tokens) })).filter((m2) => m2.total > 0).sort((a3, b3) => b3.total - a3.total || a3.label.localeCompare(b3.label)).slice(0, TOP_PROJECTS);
53585
53602
  const byModel = rankedModels.map(({ label, total }) => ({ label, tokens: total }));
53586
53603
  const byModelDetailed = rankedModels.map(({ label, tokens }) => ({ label, tokens }));
@@ -53597,7 +53614,7 @@ function foldAnalytics(aggregates, now) {
53597
53614
  events: {
53598
53615
  total: eventTotal,
53599
53616
  perDay,
53600
- last30: buildLast30(eventsByDay, now)
53617
+ last30: buildDenseWindow(eventsByDay, windowStart, windowEnd)
53601
53618
  },
53602
53619
  tokens: {
53603
53620
  byType: { ...totals },
@@ -54907,6 +54924,24 @@ function invalidate() {
54907
54924
  }
54908
54925
  }
54909
54926
 
54927
+ // src/domains/sessions/session-meta-filter.ts
54928
+ function filterSessionMetas(metas, filter) {
54929
+ return metas.filter((m2) => {
54930
+ if (filter.project !== undefined && m2.project !== filter.project)
54931
+ return false;
54932
+ if (filter.from === undefined && filter.to === undefined)
54933
+ return true;
54934
+ const ms = m2.startedAt === null ? Number.NaN : Date.parse(m2.startedAt);
54935
+ if (Number.isNaN(ms))
54936
+ return false;
54937
+ if (filter.from !== undefined && ms < filter.from)
54938
+ return false;
54939
+ if (filter.to !== undefined && ms > filter.to)
54940
+ return false;
54941
+ return true;
54942
+ });
54943
+ }
54944
+
54910
54945
  // src/domains/sessions/store/ingest.ts
54911
54946
  import { createHash as createHash9 } from "node:crypto";
54912
54947
  import { closeSync as closeSync4, openSync as openSync4, readSync as readSync4, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
@@ -64671,6 +64706,32 @@ async function readStoreAggregates(db) {
64671
64706
  get2(r2.session_id).agentCount += Number(r2.n ?? 0);
64672
64707
  return map;
64673
64708
  }
64709
+ async function attachSessionTokenTotals(db, list2) {
64710
+ const totals = await readSessionTokenTotals(db);
64711
+ return list2.map((s) => {
64712
+ const tokens = totals.get(s.id);
64713
+ return tokens ? { ...s, tokens } : s;
64714
+ });
64715
+ }
64716
+ async function readSessionTokenTotals(db) {
64717
+ const rows = await db.selectFrom("event").where("type", "=", "tokens").select((eb) => [
64718
+ "session_id",
64719
+ eb.fn.sum("input").as("input"),
64720
+ eb.fn.sum("output").as("output"),
64721
+ eb.fn.sum("cache_read").as("cache_read"),
64722
+ eb.fn.sum("cache_write").as("cache_write")
64723
+ ]).groupBy("session_id").execute();
64724
+ const map = new Map;
64725
+ for (const r2 of rows) {
64726
+ map.set(r2.session_id, {
64727
+ input: Number(r2.input ?? 0),
64728
+ output: Number(r2.output ?? 0),
64729
+ cacheCreate: Number(r2.cache_write ?? 0),
64730
+ cacheRead: Number(r2.cache_read ?? 0)
64731
+ });
64732
+ }
64733
+ return map;
64734
+ }
64674
64735
  function buildAggregatesFromStore(sessions, store) {
64675
64736
  return sessions.map((s) => {
64676
64737
  const a3 = store.get(s.id) ?? emptyStoreAggregate();
@@ -64687,30 +64748,52 @@ function buildAggregatesFromStore(sessions, store) {
64687
64748
  }
64688
64749
 
64689
64750
  // src/domains/sessions/analytics-source.ts
64690
- var cache2 = null;
64751
+ var DAY_MS2 = 24 * 60 * 60 * 1000;
64752
+ var DEFAULT_WINDOW_DAYS = 30;
64753
+ var cache2 = new Map;
64691
64754
  var storeHandle = null;
64692
64755
  function setStore(handle) {
64693
64756
  storeHandle = handle;
64694
64757
  }
64695
64758
  function invalidate2() {
64696
- cache2 = null;
64759
+ cache2 = new Map;
64760
+ }
64761
+ var cacheKey = (o2) => `${o2.from ?? ""}|${o2.to ?? ""}|${o2.project === undefined ? "" : `p:${o2.project}`}`;
64762
+ function rangeOf(o2, now) {
64763
+ if (o2.from === undefined && o2.to === undefined)
64764
+ return;
64765
+ const to = o2.to ?? now;
64766
+ const from = o2.from ?? to - (DEFAULT_WINDOW_DAYS - 1) * DAY_MS2;
64767
+ return { from, to };
64697
64768
  }
64698
- async function computeFromStore(handle, refresh2, now) {
64769
+ async function computeFromStore(handle, opts, now) {
64770
+ const refresh2 = opts.refresh ?? false;
64699
64771
  if (refresh2)
64700
64772
  await ingestAll(handle, { refresh: true });
64701
64773
  const sessions = await list({ refresh: refresh2 });
64774
+ const metas = filterSessionMetas(sessions.map((s) => ({ id: s.id, startedAt: s.startedAt, project: s.project })), { from: opts.from, to: opts.to, project: opts.project });
64702
64775
  const storeAggs = await readStoreAggregates(handle.db);
64703
- const metas = sessions.map((s) => ({ id: s.id, startedAt: s.startedAt, project: s.project }));
64704
- return foldAnalytics(buildAggregatesFromStore(metas, storeAggs), now);
64776
+ const payload = foldAnalytics(buildAggregatesFromStore(metas, storeAggs), now, rangeOf(opts, now));
64777
+ return {
64778
+ ...payload,
64779
+ filters: {
64780
+ from: opts.from ?? null,
64781
+ to: opts.to ?? null,
64782
+ project: opts.project ?? null
64783
+ }
64784
+ };
64705
64785
  }
64706
64786
  async function getAnalytics(opts = {}) {
64707
- if (cache2 && !opts.refresh)
64708
- return cache2;
64787
+ const key = cacheKey(opts);
64788
+ const hit = cache2.get(key);
64789
+ if (hit && !opts.refresh)
64790
+ return hit;
64709
64791
  if (!storeHandle)
64710
64792
  throw new StoreUnavailableError;
64711
64793
  const now = Date.now();
64712
- cache2 = await computeFromStore(storeHandle, opts.refresh ?? false, now);
64713
- return cache2;
64794
+ const payload = await computeFromStore(storeHandle, opts, now);
64795
+ cache2.set(key, payload);
64796
+ return payload;
64714
64797
  }
64715
64798
 
64716
64799
  // src/domains/sessions/origin-allowlist.ts
@@ -64738,13 +64821,33 @@ function isAllowedOrigin(origin) {
64738
64821
  return LOOPBACK_HOST_PATTERN.test(origin);
64739
64822
  }
64740
64823
 
64824
+ // src/domains/sessions/range-params.ts
64825
+ function intParam(search, key) {
64826
+ const raw = search.get(key);
64827
+ if (raw === null || raw === "")
64828
+ return;
64829
+ const n = Number(raw);
64830
+ if (!Number.isInteger(n) || n < 0)
64831
+ return { error: `invalid ${key}: ${raw}` };
64832
+ return n;
64833
+ }
64834
+ function parseRangeProjectParams(search) {
64835
+ const from = intParam(search, "from");
64836
+ if (typeof from === "object")
64837
+ return from;
64838
+ const to = intParam(search, "to");
64839
+ if (typeof to === "object")
64840
+ return to;
64841
+ return { from, to, project: search.get("project") || undefined };
64842
+ }
64843
+
64741
64844
  // src/domains/sessions/store/bridge-store.ts
64742
64845
  init_logger();
64743
64846
 
64744
64847
  // src/domains/sessions/store/prune.ts
64745
64848
  import { existsSync as existsSync34, statSync as statSync6 } from "node:fs";
64746
64849
  init_logger();
64747
- var DAY_MS2 = 24 * 60 * 60 * 1000;
64850
+ var DAY_MS3 = 24 * 60 * 60 * 1000;
64748
64851
  var DEFAULT_RETENTION_DAYS = 180;
64749
64852
  var SIZE_DELETE_BATCH = 500;
64750
64853
  var SIZE_PRUNE_MAX_ITERS = 200;
@@ -64770,7 +64873,7 @@ async function runPrune(handle, opts = {}) {
64770
64873
  const now = opts.now ?? Date.now();
64771
64874
  const retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
64772
64875
  const dbPath = opts.dbPath ?? getConsoleStorePath();
64773
- const cutoff = now - retentionDays * DAY_MS2;
64876
+ const cutoff = now - retentionDays * DAY_MS3;
64774
64877
  await db.deleteFrom("event").where("ts", "<", cutoff).execute();
64775
64878
  if (opts.maxSizeMb && opts.maxSizeMb > 0) {
64776
64879
  const cap = opts.maxSizeMb * 1024 * 1024;
@@ -64948,6 +65051,17 @@ async function attachStore(deps = {}) {
64948
65051
  }
64949
65052
 
64950
65053
  // src/domains/sessions/server.ts
65054
+ var tokenStore = null;
65055
+ async function withTokenTotals(list2) {
65056
+ if (!tokenStore)
65057
+ return list2;
65058
+ try {
65059
+ return await attachSessionTokenTotals(tokenStore.db, list2);
65060
+ } catch (err) {
65061
+ logger.verbose(`console bridge: token totals enrichment failed (${String(err)})`);
65062
+ return list2;
65063
+ }
65064
+ }
64951
65065
  var PORT_BUMP_MAX = 9;
64952
65066
  function applyCorsHeaders(req, res) {
64953
65067
  const origin = req.headers.origin;
@@ -65005,15 +65119,20 @@ async function route(req, res, version3) {
65005
65119
  if (refresh2)
65006
65120
  invalidate();
65007
65121
  const list2 = await list({ refresh: refresh2 });
65008
- sendJSON(res, 200, list2);
65122
+ sendJSON(res, 200, await withTokenTotals(list2));
65009
65123
  return;
65010
65124
  }
65011
65125
  if (pathname === "/api/analytics") {
65012
65126
  const refresh2 = url.searchParams.get("refresh") === "1";
65127
+ const rp = parseRangeProjectParams(url.searchParams);
65128
+ if ("error" in rp) {
65129
+ sendJSON(res, 400, { error: rp.error });
65130
+ return;
65131
+ }
65013
65132
  if (refresh2)
65014
65133
  invalidate2();
65015
65134
  try {
65016
- const payload = await getAnalytics({ refresh: refresh2 });
65135
+ const payload = await getAnalytics({ refresh: refresh2, ...rp });
65017
65136
  sendJSON(res, 200, payload);
65018
65137
  } catch (err) {
65019
65138
  if (err instanceof StoreUnavailableError) {
@@ -65086,6 +65205,7 @@ function start(options2 = {}) {
65086
65205
  close: async () => {
65087
65206
  if (attached) {
65088
65207
  setStore(null);
65208
+ tokenStore = null;
65089
65209
  await attached.stop().catch(() => {});
65090
65210
  }
65091
65211
  await closeServer();
@@ -65096,8 +65216,10 @@ function start(options2 = {}) {
65096
65216
  };
65097
65217
  const doAttach = options2.attachStore ?? attachStore;
65098
65218
  doAttach({ onInvalidate: () => invalidate2() }).then((attached) => {
65099
- if (attached)
65219
+ if (attached) {
65100
65220
  setStore(attached.handle);
65221
+ tokenStore = attached.handle;
65222
+ }
65101
65223
  finish(attached);
65102
65224
  }).catch(() => finish(null));
65103
65225
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunasteriskrnd/takumi",
3
- "version": "1.0.0-dev.42",
3
+ "version": "1.0.0-dev.44",
4
4
  "description": "CLI tool for bootstrapping and managing Takumi projects",
5
5
  "type": "module",
6
6
  "repository": {