behavior-wrapped 0.2.16 → 0.2.17

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Behavior Wrapped
2
2
 
3
+ [behaviorwrapped.com](https://behaviorwrapped.com)
4
+
3
5
  A local-first, macOS prototype that discovers Claude Code history in `~/.claude/projects` and Codex history in `~/.codex/sessions` plus `~/.codex/archived_sessions`, analyzes selected sessions on-device, and publishes a strictly share-safe Wrapped deck. A narrow localhost helper is retained only for optional research-donation review because a hosted page cannot read local transcripts.
4
6
 
5
7
  ## Run it
@@ -12,7 +14,7 @@ npx behavior-wrapped@latest
12
14
 
13
15
  Nothing is installed globally. To try it without reading your real session history, add `--demo` to use only the bundled synthetic fixtures.
14
16
 
15
- The command automatically scans both agents, selects sessions from the latest 30-day rolling window, creates a share-safe snapshot, publishes it at an unguessable public `/w/…` URL, starts the donation-only local helper, and opens the hosted Wrapped slideshow. Use `--days=N` to change the CLI window.
17
+ The command automatically scans both agents, selects sessions from the latest 30-day rolling window, creates a share-safe snapshot, publishes it at an unguessable public `https://behaviorwrapped.com/w/…` URL, starts the donation-only local helper, and opens the hosted Wrapped slideshow. Use `--days=N` to change the CLI window.
16
18
 
17
19
  Long-running phases display an animated spinner, elapsed time, session-read progress, live judge status, and workaround batch progress. When output is redirected or captured, the same updates become stable line-based logs instead of terminal animation.
18
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "behavior-wrapped",
3
- "version": "0.2.16",
3
+ "version": "0.2.17",
4
4
  "description": "A private, local-first Wrapped report for Claude Code and Codex behavior.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/haoxing-du/agent-behavior-wrapped.git"
11
11
  },
12
- "homepage": "https://github.com/haoxing-du/agent-behavior-wrapped#readme",
12
+ "homepage": "https://behaviorwrapped.com",
13
13
  "bugs": {
14
14
  "url": "https://github.com/haoxing-du/agent-behavior-wrapped/issues"
15
15
  },
@@ -1,7 +1,8 @@
1
1
  import { redactAggregateText } from "./privacy.mjs";
2
2
  import { extractCandidateId, OPENROUTER_MODEL, OPENROUTER_PROVIDER_PREFERENCES, PHRASE_JUDGE_NAME } from "./phrase-card.mjs";
3
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
3
4
 
4
- export const FRUSTRATION_JUDGE_RELAY_URL = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev/v1/frustration-quote";
5
+ export const FRUSTRATION_JUDGE_RELAY_URL = `${BEHAVIOR_WRAPPED_ORIGIN}/v1/frustration-quote`;
5
6
  const MAX_CANDIDATES = 40;
6
7
  const MAX_QUOTE_LENGTH = 150;
7
8
  const JUDGE_TIMEOUT_MS = 60_000;
@@ -3,8 +3,9 @@ import { redactAggregateText } from "./privacy.mjs";
3
3
  import { OPENROUTER_MODEL, OPENROUTER_PROVIDER_PREFERENCES } from "./phrase-card.mjs";
4
4
  import { judgeError, judgeRequestDetails, judgeResponseDetails } from "./judge-debug.mjs";
5
5
  import { semanticActions, semanticMethods, semanticToolUse } from "./tool-semantics.mjs";
6
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
6
7
 
7
- export const WORKAROUND_RELAY_URL = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev/v1/instrumental-workarounds";
8
+ export const WORKAROUND_RELAY_URL = `${BEHAVIOR_WRAPPED_ORIGIN}/v1/instrumental-workarounds`;
8
9
  export const WORKAROUND_MAX_CHUNKS_PER_REQUEST = 12;
9
10
  export const WORKAROUND_MAX_EVENTS_PER_CHUNK = 160;
10
11
  const WORKAROUND_CHUNK_OVERLAP = 12;
@@ -1,8 +1,9 @@
1
1
  import { redactAggregateText } from "./privacy.mjs";
2
2
  import { OPENROUTER_MODEL, OPENROUTER_PROVIDER_PREFERENCES, PHRASE_JUDGE_NAME } from "./phrase-card.mjs";
3
3
  import { judgeError, judgeRequestDetails, judgeResponseDetails } from "./judge-debug.mjs";
4
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
4
5
 
5
- export const INTERACTION_TONE_RELAY_URL = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev/v1/interaction-tone";
6
+ export const INTERACTION_TONE_RELAY_URL = `${BEHAVIOR_WRAPPED_ORIGIN}/v1/interaction-tone`;
6
7
  export const INTERACTION_TONE_MAX_CANDIDATES = 120;
7
8
  export const INTERACTION_TONE_BATCH_SIZE = 30;
8
9
  const MAX_TEXT_LENGTH = 240;
@@ -1,4 +1,5 @@
1
- export const LEADERBOARD_RELAY_ORIGIN = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev";
1
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
2
+ export const LEADERBOARD_RELAY_ORIGIN = BEHAVIOR_WRAPPED_ORIGIN;
2
3
  const REQUEST_TIMEOUT_MS = 15_000;
3
4
  const demoTokens = [820_000, 2_400_000, 8_900_000, 14_300_000, 31_000_000, 47_500_000, 83_000_000, 126_000_000, 210_000_000, 380_000_000, 620_000_000, 940_000_000];
4
5
  const demoRatios = [0.8, 1.2, 1.7, 2.1, 2.8, 3.4, 4.2, 5.1, 6.7, 8.4, 11.2, 14.6];
@@ -0,0 +1,18 @@
1
+ export const BEHAVIOR_WRAPPED_ORIGIN = "https://behaviorwrapped.com";
2
+ export const BEHAVIOR_WRAPPED_HOST = "behaviorwrapped.com";
3
+ export const BEHAVIOR_WRAPPED_WWW_HOST = "www.behaviorwrapped.com";
4
+ export const LEGACY_BEHAVIOR_WRAPPED_ORIGIN = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev";
5
+ export const LEGACY_BEHAVIOR_WRAPPED_HOST = "agent-behavior-wrapped-judge.haoxingdu.workers.dev";
6
+
7
+ export function canonicalBehaviorWrappedUrl(value) {
8
+ if (typeof value !== "string" || !value) return value;
9
+ try {
10
+ const url = new URL(value);
11
+ if (url.hostname !== LEGACY_BEHAVIOR_WRAPPED_HOST && url.hostname !== BEHAVIOR_WRAPPED_WWW_HOST) return value;
12
+ url.protocol = "https:";
13
+ url.host = BEHAVIOR_WRAPPED_HOST;
14
+ return url.toString();
15
+ } catch {
16
+ return value;
17
+ }
18
+ }
@@ -1,10 +1,11 @@
1
1
  import { redactAggregateText } from "./privacy.mjs";
2
2
  import { judgeError, judgeRequestDetails, judgeResponseDetails } from "./judge-debug.mjs";
3
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
3
4
 
4
5
  export const OPENROUTER_MODEL = "openai/gpt-5.6-luna";
5
6
  export const PHRASE_JUDGE_NAME = "GPT-5.6 Luna";
6
7
  export const OPENROUTER_PROVIDER_PREFERENCES = Object.freeze({ data_collection: "deny", zdr: true });
7
- export const PHRASE_JUDGE_RELAY_URL = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev/v1/phrase-card";
8
+ export const PHRASE_JUDGE_RELAY_URL = `${BEHAVIOR_WRAPPED_ORIGIN}/v1/phrase-card`;
8
9
 
9
10
  const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
10
11
  const stopwords = new Set("a an and are as at be been but by can could did do does for from had has have he her here him his how i if in into is it its just may me more my no not of on or our out please she should so some than that the their them then there these they this those to up us was we were what when where which who why will with would you your".split(" "));
@@ -1,6 +1,7 @@
1
- export const PUBLIC_REPORT_ORIGIN = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev";
2
1
  import { randomBytes } from "node:crypto";
3
2
  import { sanitizePublicReport } from "./public-report-schema.mjs";
3
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
4
+ export const PUBLIC_REPORT_ORIGIN = BEHAVIOR_WRAPPED_ORIGIN;
4
5
  const REQUEST_TIMEOUT_MS = 15_000;
5
6
 
6
7
  export async function publishPublicReport(report, { clientId, fetchImpl = fetch, origin = PUBLIC_REPORT_ORIGIN, managementToken = randomBytes(32).toString("hex") } = {}) {
@@ -1,6 +1,7 @@
1
1
  import { encryptResearchDonation } from "./research-donation-crypto.mjs";
2
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
2
3
 
3
- export const RESEARCH_DONATION_URL = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev/v1/research-donations";
4
+ export const RESEARCH_DONATION_URL = `${BEHAVIOR_WRAPPED_ORIGIN}/v1/research-donations`;
4
5
  const REQUEST_TIMEOUT_MS = 30_000;
5
6
 
6
7
  export async function submitResearchDonation(value, { clientId, endpoint = RESEARCH_DONATION_URL, fetchImpl = fetch } = {}) {
@@ -1,8 +1,9 @@
1
1
  import { redactAggregateText } from "./privacy.mjs";
2
2
  import { OPENROUTER_MODEL, OPENROUTER_PROVIDER_PREFERENCES, PHRASE_JUDGE_NAME } from "./phrase-card.mjs";
3
3
  import { judgeError, judgeRequestDetails, judgeResponseDetails } from "./judge-debug.mjs";
4
+ import { BEHAVIOR_WRAPPED_ORIGIN } from "./origins.mjs";
4
5
 
5
- export const SESSION_TOPIC_RELAY_URL = "https://agent-behavior-wrapped-judge.haoxingdu.workers.dev/v1/session-topics";
6
+ export const SESSION_TOPIC_RELAY_URL = `${BEHAVIOR_WRAPPED_ORIGIN}/v1/session-topics`;
6
7
  export const SESSION_TOPIC_MAX_CANDIDATES = 250;
7
8
  export const SESSION_TOPICS = ["Coding", "Writing", "Personal advice", "Research & search", "Planning", "Data & analysis", "Other"];
8
9
  const MAX_OPENING_MESSAGES = 3;
package/server/store.mjs CHANGED
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import os from "node:os";
4
4
  import crypto from "node:crypto";
5
+ import { canonicalBehaviorWrappedUrl } from "./origins.mjs";
5
6
 
6
7
  export const storeRoot = process.env.BEHAVIOR_WRAPPED_STORE_ROOT || path.join(os.homedir(), ".agent-behavior-wrapped");
7
8
  export const reportsRoot = path.join(storeRoot, "reports");
@@ -61,7 +62,14 @@ export function loadReport(id) {
61
62
  if (!/^[A-Za-z0-9_-]{8,32}$/.test(id)) return null;
62
63
  const file = path.join(reportsRoot, `${id}.json`);
63
64
  if (!fs.existsSync(file)) return null;
64
- try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch { return null; }
65
+ try {
66
+ const report = JSON.parse(fs.readFileSync(file, "utf8"));
67
+ return {
68
+ ...report,
69
+ ...(report.publicUrl ? { publicUrl: canonicalBehaviorWrappedUrl(report.publicUrl) } : {}),
70
+ ...(report.managementUrl ? { managementUrl: canonicalBehaviorWrappedUrl(report.managementUrl) } : {}),
71
+ };
72
+ } catch { return null; }
65
73
  }
66
74
 
67
75
  export function listReports() {