standout 0.5.28 → 0.5.29

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.
@@ -99,7 +99,7 @@ export type RawStats = {
99
99
  sessions: Map<string, {
100
100
  firstTs: number;
101
101
  lastTs: number;
102
- eventTimestamps: number[];
102
+ eventTimestamps: Set<number>;
103
103
  cwd: string | null;
104
104
  models: Set<string>;
105
105
  modelTurnCounts: Map<string, number>;
package/dist/ai-usage.js CHANGED
@@ -305,13 +305,14 @@ function registerFile(raw, filePath) {
305
305
  raw.filePaths.add(filePath);
306
306
  }
307
307
  function recordSessionMeta(raw, sessionId, ts, cwd, model, version) {
308
+ const minute = Math.floor(ts / 60_000);
308
309
  const existing = raw.sessions.get(sessionId);
309
310
  if (existing) {
310
311
  if (ts < existing.firstTs)
311
312
  existing.firstTs = ts;
312
313
  if (ts > existing.lastTs)
313
314
  existing.lastTs = ts;
314
- existing.eventTimestamps.push(ts);
315
+ existing.eventTimestamps.add(minute);
315
316
  if (!existing.cwd && cwd)
316
317
  existing.cwd = cwd;
317
318
  if (model)
@@ -323,7 +324,7 @@ function recordSessionMeta(raw, sessionId, ts, cwd, model, version) {
323
324
  raw.sessions.set(sessionId, {
324
325
  firstTs: ts,
325
326
  lastTs: ts,
326
- eventTimestamps: [ts],
327
+ eventTimestamps: new Set([minute]),
327
328
  cwd,
328
329
  models: model ? new Set([model]) : new Set(),
329
330
  modelTurnCounts: new Map(),
@@ -804,10 +805,11 @@ export function computeStreaks(activeDays) {
804
805
  current = 0;
805
806
  return { longest, current };
806
807
  }
807
- function estimateActiveDurationMs(timestamps) {
808
- const sorted = [...new Set(timestamps)]
809
- .filter((ts) => Number.isFinite(ts))
810
- .sort((a, b) => a - b);
808
+ function estimateActiveDurationMs(minuteBuckets) {
809
+ const sorted = [...new Set(minuteBuckets)]
810
+ .filter((m) => Number.isFinite(m))
811
+ .sort((a, b) => a - b)
812
+ .map((m) => m * 60_000);
811
813
  if (sorted.length === 0)
812
814
  return 0;
813
815
  if (sorted.length === 1)
@@ -840,10 +842,11 @@ function compactCwd(cwd) {
840
842
  }
841
843
  // Split a session's events into active segments (consecutive events <= 15min
842
844
  // apart), so concurrency reflects real work, not a tab idle for days.
843
- function activeSegments(timestamps) {
844
- const sorted = [...new Set(timestamps)]
845
- .filter((t) => Number.isFinite(t))
846
- .sort((a, b) => a - b);
845
+ function activeSegments(minuteBuckets) {
846
+ const sorted = [...new Set(minuteBuckets)]
847
+ .filter((m) => Number.isFinite(m))
848
+ .sort((a, b) => a - b)
849
+ .map((m) => m * 60_000);
847
850
  if (sorted.length === 0)
848
851
  return [];
849
852
  const maxGapMs = MAX_ACTIVE_GAP_MINUTES * 60 * 1000;
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import chalk from "chalk";
4
4
  import { createInterface } from "readline";
5
5
  import { markWrappedShared, STANDOUT_API_URL } from "./api.js";
6
6
  import { gatherFullEnrichment, gatherProfileBasics, gatherUsageStats, } from "./gather.js";
7
+ import { ensureHeapHeadroom } from "./heap.js";
7
8
  import { startMcpServer } from "./mcp.js";
8
9
  import { configureProxyFromEnv } from "./proxy.js";
9
10
  import { SYSTEM_PROMPT } from "./prompt.js";
@@ -326,6 +327,7 @@ async function runAgent(jobId) {
326
327
  console.log(` Tokens: ${totalInputTokens.toLocaleString()} in / ${totalOutputTokens.toLocaleString()} out${cacheNote} in ${elapsed}s\n`);
327
328
  }
328
329
  async function main() {
330
+ ensureHeapHeadroom();
329
331
  const args = process.argv.slice(2);
330
332
  // Route fetch through a configured proxy before any network call — Node's fetch
331
333
  // ignores HTTP(S)_PROXY otherwise, which breaks users behind a corporate proxy.
package/dist/heap.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function ensureHeapHeadroom(): void;
package/dist/heap.js ADDED
@@ -0,0 +1,25 @@
1
+ import { spawnSync } from "child_process";
2
+ import { totalmem } from "os";
3
+ // Node caps old-space at ~4GB by default regardless of physical RAM, so a big
4
+ // machine OOMs at the same point as a small one. On machines with headroom,
5
+ // re-exec once with a higher --max-old-space-size (half of RAM, capped at 8GB)
6
+ // so heavy log archives have room. The real bound is algorithmic (see
7
+ // ai-usage.ts); this is just a safety net.
8
+ export function ensureHeapHeadroom() {
9
+ if (process.env.STANDOUT_HEAP_BOOSTED === "1")
10
+ return;
11
+ if (process.execArgv.some((a) => a.startsWith("--max-old-space-size")))
12
+ return;
13
+ const totalMb = Math.floor(totalmem() / (1024 * 1024));
14
+ const desiredMb = Math.min(8192, Math.floor(totalMb / 2));
15
+ if (desiredMb <= 4096)
16
+ return;
17
+ const entry = process.argv[1];
18
+ if (!entry)
19
+ return;
20
+ const res = spawnSync(process.execPath, [`--max-old-space-size=${desiredMb}`, entry, ...process.argv.slice(2)], {
21
+ stdio: "inherit",
22
+ env: { ...process.env, STANDOUT_HEAP_BOOSTED: "1" },
23
+ });
24
+ process.exit(res.status ?? 0);
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "standout",
3
- "version": "0.5.28",
3
+ "version": "0.5.29",
4
4
  "description": "Build your developer profile with AI. One command, zero friction.",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",