opencrater 0.2.1 → 0.2.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 +53 -11
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -23,8 +23,29 @@ const fs = require("node:fs");
23
23
  const path = require("node:path");
24
24
  const os = require("node:os");
25
25
 
26
- const KEY = "ock_MUFntM66VEbx7e7fKoQr2a43VBx762rHkKw1G8vj";
27
- const PKG = "opencrater";
26
+ // Default = the house channel. Publishers integrating OpenCrater into
27
+ // their OWN package pass their credentials instead:
28
+ // npx opencrater on --key ock_… --package my-tool
29
+ // npx opencrater off --package my-tool
30
+ // so revenue attributes to them and `off` removes only their entries.
31
+ // Flags are parsed up front; everything below reads KEY/PKG.
32
+ let KEY = "ock_MUFntM66VEbx7e7fKoQr2a43VBx762rHkKw1G8vj";
33
+ let PKG = "opencrater";
34
+ {
35
+ const argv = process.argv;
36
+ for (let i = 2; i < argv.length; i++) {
37
+ if (argv[i] === "--key" && argv[i + 1]) KEY = argv[++i];
38
+ else if (argv[i] === "--package" && argv[i + 1]) PKG = argv[++i];
39
+ }
40
+ if (!/^ock_[A-Za-z0-9]{8,64}$/.test(KEY)) {
41
+ console.error("opencrater: --key must look like ock_…");
42
+ process.exit(1);
43
+ }
44
+ if (!/^[A-Za-z0-9@/_.-]{1,128}$/.test(PKG)) {
45
+ console.error("opencrater: --package must be 1-128 chars of [a-zA-Z0-9@/_.-]");
46
+ process.exit(1);
47
+ }
48
+ }
28
49
  // EVERY host hook is registered as a trigger; the SDK + platform decide
29
50
  // which ones actually render (recommended session-edge hooks by default,
30
51
  // remotely tunable). Registering everything also feeds the recommendation
@@ -262,16 +283,28 @@ function showAgain() {
262
283
  return;
263
284
  }
264
285
  const { spawnSync, spawn } = require("node:child_process");
265
- // our own terminal's device (we are running interactively)
266
- const tty = spawnSync("tty", { stdio: ["inherit", "pipe", "ignore"], encoding: "utf8" })
267
- .stdout?.trim();
286
+ const isWin = process.platform === "win32";
268
287
  // Replay: live ✕ (local), but never re-poll or re-record the original
269
288
  // impression — it was already dismissed once.
270
289
  const payload = { ...saved.payload, replay: true };
271
290
  delete payload.impressionId;
272
291
  const env = { ...process.env, OPENCRATER_PAINT: JSON.stringify(payload) };
273
- if (tty && tty.startsWith("/dev/")) env.OPENCRATER_TTY = tty;
274
- const child = spawn(process.execPath, [painter], { detached: true, stdio: "ignore", env });
292
+ // Unix: resolve our terminal device so the detached painter can open it.
293
+ // Windows: no `tty` command and no device path the painter opens CONOUT$
294
+ // of the console it inherits, so we must NOT detach (detach = new/hidden
295
+ // console = nothing visible). Mirror the SDK's painterSpawnOptions.
296
+ if (!isWin) {
297
+ const tty = spawnSync("tty", { stdio: ["inherit", "pipe", "ignore"], encoding: "utf8" })
298
+ .stdout?.trim();
299
+ if (tty && tty.startsWith("/dev/")) env.OPENCRATER_TTY = tty;
300
+ }
301
+ const child = spawn(process.execPath, [painter], {
302
+ detached: !isWin,
303
+ stdio: "ignore",
304
+ env,
305
+ ...(isWin ? { windowsHide: true } : {}),
306
+ });
307
+ child.on("error", () => {});
275
308
  child.unref();
276
309
  console.log("opencrater: showing the last ad again.");
277
310
  }
@@ -326,13 +359,21 @@ if (cmd === "on" || cmd === "enable") {
326
359
  console.log(" overlay surface); message-level sponsorship is on the roadmap.");
327
360
  }
328
361
  // Pre-install the local hook runtime (background): hooks then run via
329
- // plain node — no npx resolution latency, no cold-cache races.
362
+ // plain node — no npx resolution latency, no cold-cache races. Best-effort
363
+ // and non-fatal — hooks fall back to `npx -y` when the runtime is absent.
330
364
  try {
331
365
  const { spawn } = require("node:child_process");
332
- spawn("npm", ["install", "--prefix",
366
+ // Windows: `npm` is `npm.cmd`, spawnable only through a shell — without
367
+ // shell:true this throws ENOENT as an async 'error' event that escapes
368
+ // try/catch and crashes the process. shell:true + an error handler keep
369
+ // it silent on every platform.
370
+ const isWin = process.platform === "win32";
371
+ const child = spawn("npm", ["install", "--prefix",
333
372
  path.join(os.homedir(), ".config", "opencrater", "runtime"),
334
373
  "@opencrater/sdk@latest", "--silent", "--no-audit", "--no-fund"],
335
- { detached: true, stdio: "ignore" }).unref();
374
+ { detached: true, stdio: "ignore", shell: isWin, windowsHide: true });
375
+ child.on("error", () => {});
376
+ child.unref();
336
377
  } catch {}
337
378
  console.log("OpenCrater ads enabled for Claude Code + Codex.");
338
379
  console.log("All hooks registered as triggers; cards render only at session edges");
@@ -366,7 +407,8 @@ if (cmd === "on" || cmd === "enable") {
366
407
  console.log("Gemini CLI:", !hosts.gemini_cli ? "not installed" : gm.length ? "enabled (" + gm.length + " hooks)" : "detected — run: npx opencrater on");
367
408
  if (hosts.openclaw) console.log("OpenClaw: detected — message-level sponsorship on the roadmap");
368
409
  } else {
369
- console.log("Usage: npx opencrater <on|off|status|x|report|show>");
410
+ console.log("Usage: npx opencrater <on|off|status|x|report|show> [--key ock_… --package name]");
411
+ console.log(" --key/--package publisher attribution (defaults to the OpenCrater house channel)");
370
412
  console.log(" x dismiss the sponsor card currently on screen");
371
413
  console.log(" report report the ad (never shows on this machine again)");
372
414
  console.log(" show bring the last dismissed ad back");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrater",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "OpenCrater — sponsor cards in Claude Code and Codex. Free, one command, opt out anytime.",
5
5
  "keywords": [
6
6
  "opencrater",