claude-rpc 0.17.0 → 0.17.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-rpc",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "description": "Discord Rich Presence for Claude Code — live model, project, tokens, and lifetime stats driven by Claude Code's hook system.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/daemon.js CHANGED
@@ -3,7 +3,7 @@ import { writeFileSync, readFileSync, existsSync, unlinkSync, watch, appendFileS
3
3
  import { basename, dirname } from 'node:path';
4
4
  import { Client } from './discord-ipc.js';
5
5
  import { readState, sweepStaleStateTmp } from './state.js';
6
- import { makeRotationCursor, pickFrames, selectFrame, resolveLargeImageKey } from './presence.js';
6
+ import { makeRotationCursor, pickFrames, selectFrame, resolveLargeImageKey, shouldShowGithubButton } from './presence.js';
7
7
  import { buildVars, fillTemplate, framePasses, applyIdle, applyShipped, applyTrigger } from './format.js';
8
8
  import { scan, readAggregate, findLiveSessions, readSessionTokens } from './scanner.js';
9
9
  import { detectGithubUrl } from './git.js';
@@ -238,12 +238,12 @@ function buildActivity(opts = {}) {
238
238
  if (typeof config.activityType === 'number') activity.type = config.activityType;
239
239
 
240
240
  // Buttons: static configured set, optionally augmented with a per-project
241
- // GitHub button when the current cwd has a github origin. Privacy mode
242
- // suppresses the GitHub button entirely (else clicking it leaks the
243
- // project name we're trying to hide).
244
- const isPrivacyConstrained = state._privacy && state._privacy.visibility !== 'public';
241
+ // GitHub button when the current cwd has a github origin. Suppressed under any
242
+ // non-public privacy verdict (else the link leaks the project we're hiding),
243
+ // while stale, and when presence.githubButton is set to false (the explicit
244
+ // off switch works even without the gh CLI that private-repo detection needs).
245
245
  const buttons = Array.isArray(p.buttons) ? p.buttons.slice() : [];
246
- const gh = (!isPrivacyConstrained && state.status !== 'stale') ? detectGithubUrl(state.cwd) : null;
246
+ const gh = shouldShowGithubButton(p, state) ? detectGithubUrl(state.cwd) : null;
247
247
  if (gh && !buttons.some((b) => /github\.com/i.test(b.url || ''))) {
248
248
  buttons.unshift({ label: 'View on GitHub →', url: gh });
249
249
  }
@@ -204,6 +204,12 @@ export const DEFAULT_CONFIG = {
204
204
  },
205
205
  },
206
206
 
207
+ // Auto-prepend a "View on GitHub →" button when the cwd is a github repo.
208
+ // Set false to never show it — the privacy-safe off switch for machines
209
+ // without the `gh` CLI, where private repos can't be auto-detected and would
210
+ // otherwise have their link appear on the card.
211
+ githubButton: true,
212
+
207
213
  buttons: [
208
214
  // The card others see in Discord is the project's main distribution
209
215
  // surface — make the button a real call-to-action, not a bare repo link.
package/src/doctor.js CHANGED
@@ -15,7 +15,8 @@ import {
15
15
  CLAUDE_HOME, CLAUDE_PROJECTS, CLAUDE_SETTINGS,
16
16
  } from './paths.js';
17
17
  import { findLiveSessions } from './scanner.js';
18
- import { resolveVisibility, listPrivateCwds } from './privacy.js';
18
+ import { detectGithubUrl } from './git.js';
19
+ import { resolveVisibility, listPrivateCwds, detectGithubPrivate } from './privacy.js';
19
20
  import { readClaudeCredentials, readUsageCache } from './usage.js';
20
21
  import { c, check as uiCheck } from './ui.js';
21
22
 
@@ -330,6 +331,18 @@ function checkPrivacy(cfg) {
330
331
  } else {
331
332
  check('private-list entries', 'pass', 'none');
332
333
  }
334
+ // Private-repo guard. The "View on GitHub →" button URL is read from
335
+ // .git/config (no gh needed), but auto-hiding a PRIVATE repo needs the gh
336
+ // CLI. On a github repo here where the button would show (public verdict,
337
+ // button enabled) and gh can't answer, a private repo's link could leak.
338
+ const ghUrl = detectGithubUrl(cwd);
339
+ if (ghUrl && visibility === 'public' && cfg?.presence?.githubButton !== false
340
+ && cfg?.privacy?.autoDetectGithubPrivate !== false
341
+ && detectGithubPrivate(cwd) === null) {
342
+ check('private-repo guard', 'warn',
343
+ 'gh CLI unavailable — a private repo here can\'t be auto-detected, so its GitHub link may appear on the card',
344
+ 'run `gh auth login`, set presence.githubButton:false, or `claude-rpc private` in private repos');
345
+ }
333
346
  } catch (e) {
334
347
  check('privacy check', 'warn', `lookup failed: ${e.message}`);
335
348
  }
package/src/format.js CHANGED
@@ -448,11 +448,16 @@ export function buildVars(state, config, aggregate) {
448
448
  const usageWeeklyPct = usage?.weeklyPct ?? '';
449
449
  let usageStateLabel = '';
450
450
  if (usage) {
451
+ // The usage rotation frame's DETAILS line already shows "{usageWeeklyPct}%
452
+ // weekly", so this state line must NOT repeat weekly% — it complements with
453
+ // session% + reset day. (A weekly-only fallback lives below so the line
454
+ // isn't empty when session% is absent and we'd otherwise show nothing.)
451
455
  const bits = [];
452
456
  if (usage.sessionPct != null) bits.push(`session ${usage.sessionPct}%`);
453
- if (usage.weeklyPct != null) bits.push(`weekly ${usage.weeklyPct}%`);
454
457
  const day = fmtResetDay(usage.weeklyResetsAt);
455
458
  if (day) bits.push(`resets ${day}`);
459
+ // Only fall back to weekly% when the line would otherwise be empty.
460
+ if (!bits.length && usage.weeklyPct != null) bits.push(`weekly ${usage.weeklyPct}%`);
456
461
  usageStateLabel = bits.join(' · ');
457
462
  }
458
463
 
package/src/presence.js CHANGED
@@ -55,6 +55,19 @@ export function selectFrame(rawFrames, vars, status, cursor, intervalMs, framePa
55
55
  return frames[cursor.index % frames.length] || {};
56
56
  }
57
57
 
58
+ // Should the daemon auto-add a "View on GitHub →" button for this cwd? The
59
+ // button URL is read from .git/config (no `gh` needed), but private-repo
60
+ // detection DOES need the gh CLI — so on a machine without gh a private repo
61
+ // can't be detected and its link would leak onto the card. This is the explicit
62
+ // kill switch (`presence.githubButton: false`), independent of gh; it also stays
63
+ // suppressed while stale or under any non-public privacy verdict.
64
+ export function shouldShowGithubButton(p, state) {
65
+ if (p.githubButton === false) return false;
66
+ if (!state || state.status === 'stale') return false;
67
+ if (state._privacy && state._privacy.visibility !== 'public') return false;
68
+ return true;
69
+ }
70
+
58
71
  // Large-image key precedence (returns the TEMPLATE string; the caller fills it):
59
72
  // 1. statusAssets[status] per-status art ("working" gif, etc.)
60
73
  // 2. modelAssets[fable|opus|sonnet|haiku|default] per-model art, never stale
package/src/privacy.js CHANGED
@@ -178,7 +178,7 @@ export function listVisibility() {
178
178
 
179
179
  // ── GitHub-private detection (best-effort, gh CLI) ──────────────────────
180
180
 
181
- function detectGithubPrivate(cwd) {
181
+ export function detectGithubPrivate(cwd) {
182
182
  if (!cwd) return null;
183
183
  const cached = ghPrivateCache.get(cwd);
184
184
  if (cached && Date.now() - cached.ts < TTL_MS) return cached.value;
package/src/version.js CHANGED
@@ -11,7 +11,7 @@ import { readFileSync } from 'node:fs';
11
11
  import { join } from 'node:path';
12
12
  import { ROOT } from './paths.js';
13
13
 
14
- const BAKED = '0.17.0';
14
+ const BAKED = '0.17.2';
15
15
 
16
16
  function readPkgVersion() {
17
17
  try {