claude-rpc 0.15.2 → 0.15.4

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.15.2",
3
+ "version": "0.15.4",
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/cli.js CHANGED
@@ -1137,22 +1137,49 @@ async function doSquadCmd(argv) {
1137
1137
  });
1138
1138
  }
1139
1139
 
1140
- // ── Link (CLI web pairing) ─────────────────────────────────────────────
1140
+ // ── Link (one profile across machines) ───────────────────────────────────
1141
1141
  //
1142
- // `claude-rpc link <code>`the code comes from claude-rpc.vercel.app/squads
1143
- // while logged in with GitHub. Claims it against this install's instanceId,
1144
- // which verifies the profile (✓) and unlocks managing squads from the
1145
- // browser. Replaces the gist dance for anyone who uses the website.
1146
-
1147
- async function doLink(argv) {
1148
- const code = (argv[0] || '').trim();
1149
- if (!code) {
1150
- fail('usage: claude-rpc link <code>', {
1151
- hint: 'log in at https://claude-rpc.vercel.app/squads — it shows you the code',
1152
- code: EX_USER_ERROR,
1142
+ // Two-sided by designone verb owns the whole story:
1143
+ // claude-rpc link on your MAIN (verified) machine mints a
1144
+ // one-time code, no browser needed
1145
+ // claude-rpc link <code> on the NEW machine claims it, merging this
1146
+ // install into the same leaderboard identity
1147
+ // The browser fallback lives at claude-rpc.vercel.app/link (log in with
1148
+ // GitHub → same code) for when the other machine isn't handy. Claiming
1149
+ // verifies the profile () and unlocks managing squads from the browser.
1150
+
1151
+ const LINK_PAGE = 'https://claude-rpc.vercel.app/link';
1152
+
1153
+ // Mint side: this machine asks the worker for a code. The worker only obliges
1154
+ // when this install's canonical profile is verified — the ✓ a claim grants
1155
+ // has to root in an already-proven identity.
1156
+ async function linkMint(ctx) {
1157
+ const r = await ctx.post('/pair/start', {});
1158
+ if (r.status === 403) {
1159
+ return fail('link codes come from a verified machine — this one isn\'t yet', {
1160
+ hint: `verify here first (claude-rpc profile verify), or mint in the browser: ${LINK_PAGE}`,
1161
+ code: EX_BAD_STATE,
1153
1162
  });
1154
1163
  }
1164
+ if (r.status !== 200 || !r.json?.code) {
1165
+ return fail(`could not mint a link code: ${r.json?.error || r.status}`, { code: EX_SYS_ERROR });
1166
+ }
1167
+ const mins = Math.round((r.json.expiresInSec || 600) / 60);
1168
+ console.log('');
1169
+ console.log(` ${c.green}✓${c.reset} link code: ${c.cyan}${c.bold}${r.json.code}${c.reset} ${c.dim}(expires in ${mins} min)${c.reset}`);
1170
+ console.log('');
1171
+ console.log(` ${c.dim}on the new machine:${c.reset}`);
1172
+ console.log(` npx claude-rpc@latest setup`);
1173
+ console.log(` ${c.cyan}claude-rpc link ${r.json.code}${c.reset}`);
1174
+ console.log('');
1175
+ console.log(` ${c.dim}one leaderboard profile — stats from every linked machine count as one${c.reset}`);
1176
+ console.log('');
1177
+ }
1178
+
1179
+ async function doLink(argv) {
1155
1180
  const ctx = squadAuth();
1181
+ const code = (argv[0] || '').trim();
1182
+ if (!code) return linkMint(ctx);
1156
1183
  // Make sure the profile row exists server-side before claiming — same
1157
1184
  // pre-publish profileVerify does, so link works on a fresh `profile on`.
1158
1185
  if (lb.profileIsPublishable(ctx.cfg.profile || {})) {
@@ -1162,14 +1189,19 @@ async function doLink(argv) {
1162
1189
  const r = await ctx.post('/pair/claim', { code });
1163
1190
  if (r.status !== 200) {
1164
1191
  return fail(`link failed: ${r.json?.error || r.status}`,
1165
- { hint: 'grab a fresh code from https://claude-rpc.vercel.app/squads and try again', code: EX_SYS_ERROR });
1192
+ { hint: `get a fresh code: run \`claude-rpc link\` on your main machine, or ${LINK_PAGE}`, code: EX_SYS_ERROR });
1166
1193
  }
1167
1194
  // Mirror the verified identity locally so `profile status` agrees.
1168
1195
  const userCfg = readJson(CONFIG_PATH, {});
1169
1196
  userCfg.profile = { ...(userCfg.profile || {}), githubUser: r.json.githubUser, verified: true };
1170
1197
  writeFileSync(CONFIG_PATH, JSON.stringify(userCfg, null, 2));
1171
1198
  console.log(` ${c.green}✓${c.reset} linked as ${c.cyan}@${r.json.githubUser}${c.reset} — profile verified, squads unlocked in the browser`);
1172
- console.log(` ${c.dim}head back to https://claude-rpc.vercel.app/squads — it picks the link up automatically${c.reset}`);
1199
+ if (r.json.merged) {
1200
+ // This machine joined an existing identity: its stats now roll up under the
1201
+ // canonical handle, one board row across all your machines.
1202
+ console.log(` ${c.green}✓${c.reset} this machine now merges into ${c.cyan}@${r.json.handle}${c.reset} ${c.dim}— stats from all your machines count as one${c.reset}`);
1203
+ }
1204
+ console.log(` ${c.dim}started in a browser tab? it picks the link up automatically${c.reset}`);
1173
1205
  }
1174
1206
 
1175
1207
  // ── Community totals ─────────────────────────────────────────────────────
@@ -1318,7 +1350,7 @@ function profileNextStep() {
1318
1350
  if (!next) {
1319
1351
  console.log(` ${c.dim}→ all set — you're live at${c.reset} ${c.cyan}https://claude-rpc.vercel.app/u/${encodeURIComponent(p.handle)}${c.reset}`);
1320
1352
  } else if (next.key === 'verify') {
1321
- console.log(` ${c.dim}→ next: log in at${c.reset} ${c.cyan}https://claude-rpc.vercel.app/squads${c.reset}${c.dim}, then${c.reset} ${c.cyan}claude-rpc link <code>${c.reset}`);
1353
+ console.log(` ${c.dim}→ next: run${c.reset} ${c.cyan}claude-rpc link${c.reset} ${c.dim}on a machine that's already verified, then${c.reset} ${c.cyan}claude-rpc link <code>${c.reset} ${c.dim}here — first machine? ${LINK_PAGE}${c.reset}`);
1322
1354
  } else {
1323
1355
  console.log(` ${c.dim}→ next:${c.reset} ${c.cyan}${next.cmd}${c.reset} ${c.dim}(${next.label})${c.reset}`);
1324
1356
  }
@@ -1361,12 +1393,12 @@ function profileStatus() {
1361
1393
  : `${c.cyan}${s.cmd}${c.reset}${i === nextIdx ? ` ${c.dim}← next${c.reset}` : ''}`;
1362
1394
  return `${mark} ${i + 1}. ${label}${' '.repeat(Math.max(1, 20 - s.label.length))}${tail}`;
1363
1395
  });
1364
- // Web pairing is the primary verify path; the gist dance stays available
1396
+ // Link codes are the primary verify path; the gist dance stays available
1365
1397
  // for terminals with no browser nearby.
1366
1398
  if (!steps[2].done) {
1367
1399
  lines.push('');
1368
- lines.push(`${c.dim}the code comes from${c.reset} ${c.cyan}https://claude-rpc.vercel.app/squads${c.reset} ${c.dim}(log in with GitHub)${c.reset}`);
1369
- lines.push(`${c.dim}no browser? fall back to${c.reset} ${c.cyan}claude-rpc profile verify${c.reset}`);
1400
+ lines.push(`${c.dim}the code comes from${c.reset} ${c.cyan}claude-rpc link${c.reset} ${c.dim}on a machine you already verified${c.reset}`);
1401
+ lines.push(`${c.dim}first machine? log in at${c.reset} ${c.cyan}${LINK_PAGE}${c.reset} ${c.dim}— or no browser:${c.reset} ${c.cyan}claude-rpc profile verify${c.reset}`);
1370
1402
  }
1371
1403
  box('next steps', lines);
1372
1404
  }
@@ -1699,7 +1731,7 @@ function help() {
1699
1731
  ['community', 'Opt in/out of anonymous community totals (on|off|status|report)'],
1700
1732
  ['profile', 'Public leaderboard identity (status|set|on|off|publish|verify)'],
1701
1733
  ['squad', 'Private mini-leaderboards with friends (create|join|leave|status)'],
1702
- ['link', 'Pair this install with your web login (code from /squads page)'],
1734
+ ['link', 'Link machines into one profile (mints a code; `link <code>` claims it)'],
1703
1735
  ['doctor', 'Run a diagnostic checklist — common-failure triage (--fix to auto-repair)'],
1704
1736
  ['tail', 'Tail the daemon log file'],
1705
1737
  ['daemon', 'Run daemon in foreground (debug)'],
package/src/install.js CHANGED
@@ -570,6 +570,7 @@ export function setupOutro(target, changed = true) {
570
570
  if (IS_PACKAGED) point('start daemon', `"${target}" daemon`, 'also runs automatically at login');
571
571
  else point('manage daemon', 'claude-rpc start · stop · status');
572
572
  point('config', CONFIG_PATH, 'a working Discord app is bundled — set clientId only to use your own');
573
+ point('other machine?', 'claude-rpc link', 'run it there, claim the code here — one leaderboard profile');
573
574
  console.log('');
574
575
  }
575
576
 
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.15.2';
14
+ const BAKED = '0.15.4';
15
15
 
16
16
  function readPkgVersion() {
17
17
  try {