standout 0.5.27 → 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.
- package/dist/ai-usage.d.ts +1 -1
- package/dist/ai-usage.js +13 -10
- package/dist/cli.js +8 -0
- package/dist/heap.d.ts +1 -0
- package/dist/heap.js +25 -0
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +27 -0
- package/dist/wrapped-client.js +15 -3
- package/package.json +2 -1
package/dist/ai-usage.d.ts
CHANGED
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.
|
|
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: [
|
|
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(
|
|
808
|
-
const sorted = [...new Set(
|
|
809
|
-
.filter((
|
|
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(
|
|
844
|
-
const sorted = [...new Set(
|
|
845
|
-
.filter((
|
|
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,7 +4,9 @@ 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";
|
|
9
|
+
import { configureProxyFromEnv } from "./proxy.js";
|
|
8
10
|
import { SYSTEM_PROMPT } from "./prompt.js";
|
|
9
11
|
import { TOOLS, executeTool } from "./tools.js";
|
|
10
12
|
import { renderWrappedAll } from "./wrapped/render.js";
|
|
@@ -325,7 +327,13 @@ async function runAgent(jobId) {
|
|
|
325
327
|
console.log(` Tokens: ${totalInputTokens.toLocaleString()} in / ${totalOutputTokens.toLocaleString()} out${cacheNote} in ${elapsed}s\n`);
|
|
326
328
|
}
|
|
327
329
|
async function main() {
|
|
330
|
+
ensureHeapHeadroom();
|
|
328
331
|
const args = process.argv.slice(2);
|
|
332
|
+
// Route fetch through a configured proxy before any network call — Node's fetch
|
|
333
|
+
// ignores HTTP(S)_PROXY otherwise, which breaks users behind a corporate proxy.
|
|
334
|
+
const proxy = configureProxyFromEnv();
|
|
335
|
+
if (proxy)
|
|
336
|
+
process.stderr.write(` (using proxy ${proxy})\n`);
|
|
329
337
|
if (args.includes("--gather")) {
|
|
330
338
|
await confirmPrivacy();
|
|
331
339
|
const data = await gatherFullEnrichment();
|
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/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function configureProxyFromEnv(): string | null;
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ProxyAgent, setGlobalDispatcher } from "undici";
|
|
2
|
+
// Node's built-in fetch (undici) ignores HTTP(S)_PROXY env vars by default. On a
|
|
3
|
+
// corporate network that forces traffic through a proxy, undici then connects
|
|
4
|
+
// directly, the proxy/firewall transparently intercepts the TLS handshake, and the
|
|
5
|
+
// client sees a mangled stream (ERR_SSL_PACKET_LENGTH_TOO_LONG / "packet length too
|
|
6
|
+
// long"). Routing fetch through the configured proxy fixes that.
|
|
7
|
+
//
|
|
8
|
+
// Honors the standard env vars (upper + lower case). NO_PROXY is intentionally not
|
|
9
|
+
// handled — we only ever talk to one host (the Standout API), so a global proxy is
|
|
10
|
+
// correct when one is set.
|
|
11
|
+
export function configureProxyFromEnv() {
|
|
12
|
+
const proxy = process.env.HTTPS_PROXY ||
|
|
13
|
+
process.env.https_proxy ||
|
|
14
|
+
process.env.HTTP_PROXY ||
|
|
15
|
+
process.env.http_proxy ||
|
|
16
|
+
process.env.ALL_PROXY ||
|
|
17
|
+
process.env.all_proxy;
|
|
18
|
+
if (!proxy)
|
|
19
|
+
return null;
|
|
20
|
+
try {
|
|
21
|
+
setGlobalDispatcher(new ProxyAgent(proxy));
|
|
22
|
+
return proxy;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/wrapped-client.js
CHANGED
|
@@ -124,9 +124,21 @@ export async function createWrapped(args) {
|
|
|
124
124
|
await sleep(RETRY_BASE_DELAY_MS * attempt);
|
|
125
125
|
continue;
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
const detail = describeError(err);
|
|
128
|
+
// A TLS-layer failure means something is intercepting HTTPS (VPN, proxy, or
|
|
129
|
+
// antivirus), not a transient drop — give targeted guidance.
|
|
130
|
+
const tlsIntercept = /ERR_SSL|ERR_TLS|packet length too long|SSL routines|wrong version number|DECRYPTION|sslv3|unknown ca|self.signed|unable to (verify|get local)/i.test(detail);
|
|
131
|
+
if (tlsIntercept) {
|
|
132
|
+
process.stderr.write(` Couldn't reach Standout: a VPN, proxy, or antivirus is intercepting the HTTPS connection.\n` +
|
|
133
|
+
` Try one of: disconnect the VPN/proxy, switch networks, or set HTTPS_PROXY to your\n` +
|
|
134
|
+
` corporate proxy (and NODE_EXTRA_CA_CERTS if it uses a custom certificate), then re-run: npx standout\n` +
|
|
135
|
+
` (details: ${detail}; body ${bodyKb}KB)\n`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
process.stderr.write(` Couldn't reach Standout to build your wrapped. Your network (or a VPN/proxy) dropped the connection.\n` +
|
|
139
|
+
` Check your connection and re-run: npx standout\n` +
|
|
140
|
+
` (details: ${detail}; body ${bodyKb}KB)\n`);
|
|
141
|
+
}
|
|
130
142
|
return null;
|
|
131
143
|
}
|
|
132
144
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "standout",
|
|
3
|
-
"version": "0.5.
|
|
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",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"gradient-string": "^3.0.0",
|
|
33
33
|
"open": "^11.0.0",
|
|
34
34
|
"playwright-core": "^1.59.1",
|
|
35
|
+
"undici": "^6.26.0",
|
|
35
36
|
"zod": "^3.24.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|