opencrater 0.1.5 → 0.1.7

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 +43 -5
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -201,17 +201,54 @@ function showAgain() {
201
201
  // our own terminal's device (we are running interactively)
202
202
  const tty = spawnSync("tty", { stdio: ["inherit", "pipe", "ignore"], encoding: "utf8" })
203
203
  .stdout?.trim();
204
- const env = { ...process.env, OPENCRATER_PAINT: JSON.stringify(saved.payload) };
204
+ // Replay: live (local), but never re-poll or re-record the original
205
+ // impression — it was already dismissed once.
206
+ const payload = { ...saved.payload, replay: true };
207
+ delete payload.impressionId;
208
+ const env = { ...process.env, OPENCRATER_PAINT: JSON.stringify(payload) };
205
209
  if (tty && tty.startsWith("/dev/")) env.OPENCRATER_TTY = tty;
206
210
  const child = spawn(process.execPath, [painter], { detached: true, stdio: "ignore", env });
207
211
  child.unref();
208
212
  console.log("opencrater: showing the last ad again.");
209
213
  }
210
214
 
215
+ /* Auto-detect which AI CLIs exist on this machine — `on` connects every
216
+ detected host's config automatically, and new hosts picked up here flow
217
+ into detection as we add them. */
218
+ function hasBin(name) {
219
+ try {
220
+ const { spawnSync } = require("node:child_process");
221
+ return spawnSync("which", [name], { stdio: "ignore" }).status === 0;
222
+ } catch { return false; }
223
+ }
224
+ function detectHosts() {
225
+ return {
226
+ claude_code: fs.existsSync(path.join(os.homedir(), ".claude")) || hasBin("claude"),
227
+ codex: fs.existsSync(path.join(process.env.CODEX_HOME || path.join(os.homedir(), ".codex"))) || hasBin("codex"),
228
+ };
229
+ }
230
+
211
231
  const cmd = process.argv[2] || "status";
212
232
  if (cmd === "on" || cmd === "enable") {
213
- enableIn(CLAUDE_SETTINGS, "claude_code");
214
- enableIn(CODEX_HOOKS, "codex");
233
+ const hosts = detectHosts();
234
+ if (!hosts.claude_code && !hosts.codex) {
235
+ console.log("No supported AI CLI found (looked for Claude Code and Codex).");
236
+ console.log("Install one, then run: npx opencrater on");
237
+ process.exit(1);
238
+ }
239
+ if (hosts.claude_code) {
240
+ enableIn(CLAUDE_SETTINGS, "claude_code");
241
+ console.log("✓ Claude Code detected — connected (" + CLAUDE_SETTINGS + ")");
242
+ } else {
243
+ console.log("· Claude Code not found — skipped");
244
+ }
245
+ if (hosts.codex) {
246
+ enableIn(CODEX_HOOKS, "codex");
247
+ console.log("✓ Codex detected — connected (" + CODEX_HOOKS + ")");
248
+ console.log(" Codex will ask once to trust the new hooks on its next launch — approve to enable.");
249
+ } else {
250
+ console.log("· Codex not found — skipped");
251
+ }
215
252
  // Pre-install the local hook runtime (background): hooks then run via
216
253
  // plain node — no npx resolution latency, no cold-cache races.
217
254
  try {
@@ -236,10 +273,11 @@ if (cmd === "on" || cmd === "enable") {
236
273
  } else if (cmd === "show" || cmd === "again") {
237
274
  showAgain();
238
275
  } else if (cmd === "status") {
276
+ const hosts = detectHosts();
239
277
  const cc = statusIn(CLAUDE_SETTINGS);
240
278
  const cx = statusIn(CODEX_HOOKS);
241
- console.log("Claude Code:", cc.length ? "enabled on " + cc.join(", ") : "not enabled");
242
- console.log("Codex: ", cx.length ? "enabled on " + cx.join(", ") : "not enabled");
279
+ console.log("Claude Code:", !hosts.claude_code ? "not installed" : cc.length ? "enabled (" + cc.length + " hooks)" : "detected — run: npx opencrater on");
280
+ console.log("Codex: ", !hosts.codex ? "not installed" : cx.length ? "enabled (" + cx.length + " hooks)" : "detected — run: npx opencrater on");
243
281
  } else {
244
282
  console.log("Usage: npx opencrater <on|off|status|x|show>");
245
283
  console.log(" x dismiss the sponsor card currently on screen");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrater",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "OpenCrater — sponsor cards in Claude Code and Codex. Free, one command, opt out anytime.",
5
5
  "keywords": [
6
6
  "opencrater",