zombie-vibe 0.1.6 → 0.1.8
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 +2 -2
- package/examples/config.json +1 -1
- package/lib/installer.mjs +1 -1
- package/package.json +2 -2
- package/runtime/hook.mjs +17 -2
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Zombie Pulse installs deterministic prompt-submission hooks for Codex, Claude Code, Cursor, Windsurf, and Grok. The hook records both the user's local ISO timestamp and an unambiguous UTC audit timestamp, the IANA timezone when available, the local timezone offset, the editor/AI name, the session, and prompt length. It immediately discards the prompt text.
|
|
4
4
|
|
|
5
|
-
Events live in a private persistent queue at `~/.zombie-vibe/events.jsonl`. Each buffered event stores `occurred_at_local`, `occurred_at_utc`, timezone metadata, the machine-readable `agent`, and a display-ready `ai_name`; older events are upgraded automatically when the queue is next read. When the queue reaches 10 events, the oldest 10 are sent to the Zombie Vibe API. Failed uploads stay queued for a later prompt or manual flush.
|
|
5
|
+
Events live in a private persistent queue at `~/.zombie-vibe/events.jsonl`. Each buffered event stores `occurred_at_local`, `occurred_at_utc`, timezone metadata, the machine-readable `agent`, and a display-ready `ai_name`; older events are upgraded automatically when the queue is next read. Uploads use a versioned `events` array and include batch-level local/UTC send times plus the agents represented in the batch. When the queue reaches 10 events, the oldest 10 are sent to the Zombie Vibe API. Failed uploads stay queued for a later prompt or manual flush.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
-
Create an API key after signing in at `https://
|
|
9
|
+
Create an API key after signing in at `https://zombipulse.xyz/#account`, then install globally for every supported editor:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npx zombie-vibe install --api-key zp_live_YOUR_KEY
|
package/examples/config.json
CHANGED
package/lib/installer.mjs
CHANGED
|
@@ -78,7 +78,7 @@ export async function install(args) {
|
|
|
78
78
|
const unknown = editors.filter((editor) => !supported.includes(editor));
|
|
79
79
|
if (unknown.length) throw new Error(`Unsupported editor: ${unknown.join(", ")}. Use ${supported.join(", ")}, or all.`);
|
|
80
80
|
const apiKey = option(args, "--api-key") || process.env.ZOMBIE_VIBE_API_KEY;
|
|
81
|
-
const apiUrl = (option(args, "--api-url") || "https://api.
|
|
81
|
+
const apiUrl = (option(args, "--api-url") || "https://api.zombipulse.xyz/api/v1/events").replace(/\/$/, "");
|
|
82
82
|
|
|
83
83
|
await mkdir(runtimeDir, { recursive: true });
|
|
84
84
|
await copyFile(join(packageRoot, "runtime", "hook.mjs"), runtimePath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zombie-vibe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Install privacy-first prompt timing hooks for Codex, Claude Code, Cursor, Windsurf, and Grok.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"type": "git",
|
|
37
37
|
"url": "git+https://github.com/gunjchhetri/zombie-vibe-skills.git"
|
|
38
38
|
},
|
|
39
|
-
"homepage": "https://
|
|
39
|
+
"homepage": "https://zombipulse.xyz/#integrations"
|
|
40
40
|
}
|
package/runtime/hook.mjs
CHANGED
|
@@ -9,6 +9,7 @@ const configPath = join(dataDir, "config.json");
|
|
|
9
9
|
const queuePath = join(dataDir, "events.jsonl");
|
|
10
10
|
const lockPath = join(dataDir, "events.lock");
|
|
11
11
|
const aiNames = { codex: "Codex", claude: "Claude", cursor: "Cursor", windsurf: "Windsurf", grok: "Grok" };
|
|
12
|
+
const schemaVersion = 2;
|
|
12
13
|
|
|
13
14
|
function localTimestamp(date, offsetMinutes = date.getTimezoneOffset()) {
|
|
14
15
|
const local = new Date(date.getTime() - offsetMinutes * 60_000).toISOString().slice(0, -1);
|
|
@@ -59,6 +60,20 @@ export function eventFromPayload(payload, agent = "unknown", now = new Date()) {
|
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
export function batchPayload(events, now = new Date()) {
|
|
64
|
+
const timezoneOffsetMinutes = now.getTimezoneOffset();
|
|
65
|
+
const normalizedEvents = events.map((event) => normalizeBufferedEvent(event));
|
|
66
|
+
return {
|
|
67
|
+
schema_version: schemaVersion,
|
|
68
|
+
sent_at_utc: now.toISOString(),
|
|
69
|
+
sent_at_local: localTimestamp(now, timezoneOffsetMinutes),
|
|
70
|
+
timezone_offset_minutes: timezoneOffsetMinutes,
|
|
71
|
+
timezone_name: timezoneName(),
|
|
72
|
+
agents: [...new Set(normalizedEvents.map((event) => String(event.agent || "unknown")))],
|
|
73
|
+
events: normalizedEvents,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
async function readJson(path, fallback) {
|
|
63
78
|
try { return JSON.parse(await readFile(path, "utf8")); } catch { return fallback; }
|
|
64
79
|
}
|
|
@@ -96,8 +111,8 @@ async function sendBatch(config, events) {
|
|
|
96
111
|
if (!config.apiKey || !config.apiUrl) return false;
|
|
97
112
|
const response = await fetch(config.apiUrl, {
|
|
98
113
|
method: "POST",
|
|
99
|
-
headers: { authorization: `Bearer ${config.apiKey}`, "content-type": "application/json", "user-agent": "zombie-vibe-hook/0.1.
|
|
100
|
-
body: JSON.stringify(
|
|
114
|
+
headers: { authorization: `Bearer ${config.apiKey}`, "content-type": "application/json", "user-agent": "zombie-vibe-hook/0.1.8" },
|
|
115
|
+
body: JSON.stringify(batchPayload(events)),
|
|
101
116
|
signal: AbortSignal.timeout(3000),
|
|
102
117
|
});
|
|
103
118
|
return response.ok;
|