behavior-wrapped 0.5.16 → 0.5.18
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/assets/{index-ChdYH7Sm.css → index-CQfAT656.css} +1 -1
- package/dist/assets/{index-DIrSEDB2.js → index-Cz-I5Bz_.js} +2 -2
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/cli.mjs +2 -0
- package/server/launcher.mjs +10 -0
- package/server/local-helper-runtime.mjs +32 -0
package/dist/index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<meta name="theme-color" content="#0d0b1b" />
|
|
7
7
|
<meta name="description" content="A local-first behavior report for you and your AI agents." />
|
|
8
8
|
<title>Behavior Wrapped</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-Cz-I5Bz_.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CQfAT656.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
package/package.json
CHANGED
package/server/cli.mjs
CHANGED
|
@@ -129,6 +129,8 @@ async function createWrapped() {
|
|
|
129
129
|
progress.start("Preparing local donation helper", `localhost:${port}`);
|
|
130
130
|
await ensureServer(demo);
|
|
131
131
|
progress.succeed("Local donation helper ready");
|
|
132
|
+
const helperHeartbeat = setInterval(() => { void fetch(`${loopbackUrl}/api/health`).catch(() => {}); }, 30_000);
|
|
133
|
+
helperHeartbeat.unref();
|
|
132
134
|
const daysArgument = process.argv.find((argument) => argument.startsWith("--days="));
|
|
133
135
|
const windowDays = daysArgument ? Number(daysArgument.split("=")[1]) : DEFAULT_WINDOW_DAYS;
|
|
134
136
|
if (!Number.isInteger(windowDays) || windowDays < 1 || windowDays > 3650) throw new Error("--days must be a whole number from 1 to 3650.");
|
package/server/launcher.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { deleteDonationReceipt, getOrCreateClientId, loadDonationReceipt, loadRe
|
|
|
11
11
|
import { deleteResearchDonation, RESEARCH_DONATION_URL, submitResearchDonation } from "./research-donation.mjs";
|
|
12
12
|
import { APP_VERSION, LOCAL_DONATION_PROTOCOL } from "./runtime-version.mjs";
|
|
13
13
|
import { makeWorkaroundEvidencePreview } from "./workaround-evidence.mjs";
|
|
14
|
+
import { createIdleShutdownController } from "./local-helper-runtime.mjs";
|
|
14
15
|
|
|
15
16
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
16
17
|
const root = path.dirname(here);
|
|
@@ -21,6 +22,8 @@ const coworkFixtureRoot = path.join(root, "fixtures", "cowork-sessions");
|
|
|
21
22
|
const demo = process.argv.includes("--demo");
|
|
22
23
|
const portArg = process.argv.find((arg) => arg.startsWith("--port="));
|
|
23
24
|
const port = Number(portArg?.split("=")[1] || 4317);
|
|
25
|
+
const configuredIdleMs = Number(process.env.BEHAVIOR_WRAPPED_HELPER_IDLE_MS);
|
|
26
|
+
const helperIdleMs = Number.isFinite(configuredIdleMs) && configuredIdleMs >= 100 ? configuredIdleMs : 5 * 60 * 1_000;
|
|
24
27
|
let catalog = await loadCatalog();
|
|
25
28
|
|
|
26
29
|
async function loadCatalog() {
|
|
@@ -91,6 +94,7 @@ const mime = { ".html": "text/html; charset=utf-8", ".js": "text/javascript; cha
|
|
|
91
94
|
const server = http.createServer(async (request, response) => {
|
|
92
95
|
try {
|
|
93
96
|
if (!new Set([`127.0.0.1:${port}`, `localhost:${port}`]).has(request.headers.host || "")) return json(response, 403, { error: "Local access only" });
|
|
97
|
+
idleShutdown.touch();
|
|
94
98
|
const url = new URL(request.url || "/", `http://${request.headers.host}`);
|
|
95
99
|
if (request.method === "GET" && url.pathname === "/api/health") return json(response, 200, { app: "behavior-wrapped", version: APP_VERSION, local: true, purpose: "research-donation", donationProtocol: LOCAL_DONATION_PROTOCOL, pid: process.pid, demo });
|
|
96
100
|
if (request.method === "GET" && url.pathname === "/api/discover") {
|
|
@@ -169,6 +173,12 @@ const server = http.createServer(async (request, response) => {
|
|
|
169
173
|
}
|
|
170
174
|
});
|
|
171
175
|
|
|
176
|
+
const idleShutdown = createIdleShutdownController({
|
|
177
|
+
enabled: process.env.BEHAVIOR_WRAPPED_DAEMON === "1",
|
|
178
|
+
idleMs: helperIdleMs,
|
|
179
|
+
onIdle: () => server.close(() => process.exit(0)),
|
|
180
|
+
});
|
|
181
|
+
|
|
172
182
|
server.listen(port, "127.0.0.1", () => {
|
|
173
183
|
const url = `http://localhost:${port}`;
|
|
174
184
|
console.log(`Behavior Wrapped donation helper is ready at ${url}`);
|
|
@@ -9,6 +9,38 @@ export function helperHealthMatches(value, { version, protocol, demo }) {
|
|
|
9
9
|
&& value.version === version && value.donationProtocol === protocol && Boolean(value.demo) === Boolean(demo));
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export function createIdleShutdownController({
|
|
13
|
+
enabled,
|
|
14
|
+
idleMs,
|
|
15
|
+
onIdle,
|
|
16
|
+
setTimeoutImpl = setTimeout,
|
|
17
|
+
clearTimeoutImpl = clearTimeout,
|
|
18
|
+
}) {
|
|
19
|
+
let timer = null;
|
|
20
|
+
let stopped = false;
|
|
21
|
+
|
|
22
|
+
function schedule() {
|
|
23
|
+
if (!enabled || stopped) return;
|
|
24
|
+
if (timer) clearTimeoutImpl(timer);
|
|
25
|
+
timer = setTimeoutImpl(() => {
|
|
26
|
+
timer = null;
|
|
27
|
+
stopped = true;
|
|
28
|
+
onIdle();
|
|
29
|
+
}, idleMs);
|
|
30
|
+
timer?.unref?.();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function touch() { schedule(); }
|
|
34
|
+
function stop() {
|
|
35
|
+
stopped = true;
|
|
36
|
+
if (timer) clearTimeoutImpl(timer);
|
|
37
|
+
timer = null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
schedule();
|
|
41
|
+
return { touch, stop };
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
export function isVerifiedLauncherCommand(command, port) {
|
|
13
45
|
const escapedPort = String(port).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14
46
|
return new RegExp(`(?:/(?:agent-)?behavior-wrapped/|(?:^|\\s))server/launcher\\.mjs(?:\\s|$)`, "i").test(command || "")
|