opencrater 0.1.1 → 0.1.3

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.
Files changed (2) hide show
  1. package/cli.js +58 -5
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -25,7 +25,22 @@ const os = require("node:os");
25
25
 
26
26
  const KEY = "ock_MUFntM66VEbx7e7fKoQr2a43VBx762rHkKw1G8vj";
27
27
  const PKG = "opencrater";
28
- const EVENTS = ["SessionStart", "Stop"];
28
+ // EVERY host hook is registered as a trigger; the SDK + platform decide
29
+ // which ones actually render (recommended session-edge hooks by default,
30
+ // remotely tunable). Registering everything also feeds the recommendation
31
+ // engine's anonymized topic signal so cards match what you're working on.
32
+ const CLAUDE_EVENTS = [
33
+ "SessionStart", "SessionEnd", "Stop", "StopFailure", "SubagentStart", "SubagentStop",
34
+ "Notification", "PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch",
35
+ "PermissionRequest", "PermissionDenied", "UserPromptSubmit", "UserPromptExpansion",
36
+ "PreCompact", "PostCompact", "TaskCreated", "TaskCompleted", "Setup", "TeammateIdle",
37
+ "Elicitation", "ElicitationResult", "ConfigChange", "InstructionsLoaded",
38
+ "WorktreeCreate", "WorktreeRemove", "CwdChanged", "FileChanged", "MessageDisplay",
39
+ ];
40
+ const CODEX_EVENTS = [
41
+ "SessionStart", "Stop", "UserPromptSubmit", "PreToolUse", "PostToolUse",
42
+ "PermissionRequest", "PreCompact", "PostCompact",
43
+ ];
29
44
  const MARKER = "--package " + PKG + " "; // identifies OUR hook entries only
30
45
 
31
46
  const cmdFor = (event, host) =>
@@ -65,10 +80,11 @@ const isOurs = (rule) =>
65
80
  );
66
81
 
67
82
  function enableIn(file, host) {
83
+ const events = host === "codex" ? CODEX_EVENTS : CLAUDE_EVENTS;
68
84
  const settings = readJson(file);
69
85
  if (!settings.hooks) settings.hooks = {};
70
86
  let changed = false;
71
- for (const event of EVENTS) {
87
+ for (const event of events) {
72
88
  const list = Array.isArray(settings.hooks[event]) ? settings.hooks[event] : [];
73
89
  const desired = {
74
90
  matcher: "",
@@ -117,23 +133,60 @@ function statusIn(file) {
117
133
  );
118
134
  }
119
135
 
136
+ /* Dismiss the sponsor card currently on screen — no browser needed.
137
+ Writes the painter's local dismiss signal, records the dismissal against
138
+ the API (idempotent), and SIGTERMs the painter so the card clears NOW. */
139
+ async function dismissNow() {
140
+ const dir = path.join(os.homedir(), ".config", "opencrater");
141
+ let lock = null;
142
+ try {
143
+ lock = JSON.parse(fs.readFileSync(path.join(dir, "painter.lock"), "utf8"));
144
+ } catch {
145
+ console.log("opencrater: no sponsor card is on screen.");
146
+ return;
147
+ }
148
+ try {
149
+ fs.mkdirSync(dir, { recursive: true });
150
+ fs.writeFileSync(
151
+ path.join(dir, "dismiss.signal"),
152
+ JSON.stringify({ impressionId: lock.impressionId, at: Date.now() }),
153
+ );
154
+ } catch {}
155
+ if (lock.impressionId) {
156
+ const origin = process.env.OPENCRATER_API_ORIGIN || "https://api.opencrater.to";
157
+ try {
158
+ await fetch(origin + "/x/" + lock.impressionId, { signal: AbortSignal.timeout(2500) });
159
+ } catch {}
160
+ }
161
+ if (lock.pid && lock.pid > 1) {
162
+ try { process.kill(lock.pid, "SIGTERM"); } catch {}
163
+ }
164
+ console.log("opencrater: ad dismissed — feedback recorded.");
165
+ }
166
+
120
167
  const cmd = process.argv[2] || "status";
121
168
  if (cmd === "on" || cmd === "enable") {
122
169
  enableIn(CLAUDE_SETTINGS, "claude_code");
123
170
  enableIn(CODEX_HOOKS, "codex");
124
- console.log("OpenCrater ads enabled for Claude Code + Codex (SessionStart, Stop).");
125
- console.log("One tasteful card per session edge; machine-wide frequency cap applies.");
171
+ console.log("OpenCrater ads enabled for Claude Code + Codex.");
172
+ console.log("All hooks registered as triggers; cards render only at session edges");
173
+ console.log("(SessionStart, Stop, SessionEnd, Notification) with a machine-wide frequency cap.");
174
+ console.log("Ads are personalized to what you're working on via anonymized keywords");
175
+ console.log("(never raw prompts, paths, or secrets).");
126
176
  console.log("Turn off anytime: npx opencrater off (or OPENCRATER_DISABLE=1)");
127
177
  } else if (cmd === "off" || cmd === "disable") {
128
178
  const a = disableIn(CLAUDE_SETTINGS);
129
179
  const b = disableIn(CODEX_HOOKS);
130
180
  console.log(a || b ? "OpenCrater ads disabled. Publisher hooks (if any) were not touched." : "Nothing to remove — OpenCrater ads were not enabled.");
181
+ } else if (cmd === "x" || cmd === "dismiss") {
182
+ dismissNow();
131
183
  } else if (cmd === "status") {
132
184
  const cc = statusIn(CLAUDE_SETTINGS);
133
185
  const cx = statusIn(CODEX_HOOKS);
134
186
  console.log("Claude Code:", cc.length ? "enabled on " + cc.join(", ") : "not enabled");
135
187
  console.log("Codex: ", cx.length ? "enabled on " + cx.join(", ") : "not enabled");
136
188
  } else {
137
- console.log("Usage: npx opencrater <on|off|status>");
189
+ console.log("Usage: npx opencrater <on|off|status|x>");
190
+ console.log(" x dismiss the sponsor card currently on screen");
138
191
  process.exitCode = 1;
139
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrater",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "OpenCrater — sponsor cards in Claude Code and Codex. Free, one command, opt out anytime.",
5
5
  "keywords": [
6
6
  "opencrater",