claude-rpc 0.7.1 → 0.7.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.
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/privacy.js +62 -2
- package/src/server/api.js +35 -0
- package/src/server/assets/dashboard.client.js +647 -0
- package/src/server/assets/dashboard.css +326 -0
- package/src/server/assets/dashboard.html +276 -0
- package/src/server/assets.js +36 -0
- package/src/server/page.js +24 -1306
- package/src/server/routes.js +24 -1
- package/src/version.js +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
**Discord Rich Presence for [Claude Code](https://claude.com/claude-code).**
|
|
11
11
|
Your live model, project, current tool, tokens, and lifetime stats — in your Discord profile. Driven by the hooks Claude Code already fires. Zero polling between sessions.
|
|
12
12
|
|
|
13
|
+
**→ [claude-rpc.vercel.app](https://claude-rpc.vercel.app)** — what it looks like, in one page.
|
|
14
|
+
|
|
13
15
|
[](#community-totals) [](#community-totals)
|
|
14
16
|
|
|
15
17
|
<sub>live — on by default for fresh installs, opt out any time. see [community totals](#community-totals)</sub>
|
package/package.json
CHANGED
package/src/privacy.js
CHANGED
|
@@ -25,7 +25,10 @@ import { execFileSync } from 'node:child_process';
|
|
|
25
25
|
import { join, basename, dirname, resolve as resolvePath } from 'node:path';
|
|
26
26
|
import { DATA_DIR } from './paths.js';
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// CLAUDE_RPC_PRIVATE_LIST lets tests point the runtime store at a temp file
|
|
29
|
+
// instead of the user's real ~/.claude-rpc/private-list.json. Unset in normal
|
|
30
|
+
// operation.
|
|
31
|
+
const PRIVATE_LIST_PATH = process.env.CLAUDE_RPC_PRIVATE_LIST || join(DATA_DIR, 'private-list.json');
|
|
29
32
|
const TTL_MS = 5 * 60 * 1000;
|
|
30
33
|
|
|
31
34
|
const projectFileCache = new Map(); // cwd → { ts, value | null }
|
|
@@ -82,7 +85,14 @@ function readPrivateList() {
|
|
|
82
85
|
if (!existsSync(PRIVATE_LIST_PATH)) return { paths: [] };
|
|
83
86
|
try {
|
|
84
87
|
const v = JSON.parse(readFileSync(PRIVATE_LIST_PATH, 'utf8'));
|
|
85
|
-
if (
|
|
88
|
+
if (v && typeof v === 'object') {
|
|
89
|
+
// `paths` is the legacy binary list (presence ≡ hidden). `visibility`
|
|
90
|
+
// is the richer GUI-managed map: { "<abs cwd>": "hidden|name-only|public" }.
|
|
91
|
+
return {
|
|
92
|
+
paths: Array.isArray(v.paths) ? v.paths : [],
|
|
93
|
+
visibility: (v.visibility && typeof v.visibility === 'object') ? v.visibility : undefined,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
86
96
|
} catch { /* broken JSON ≡ no list (treat as empty rather than crash) */ }
|
|
87
97
|
return { paths: [] };
|
|
88
98
|
}
|
|
@@ -122,6 +132,50 @@ function isInPrivateList(cwd) {
|
|
|
122
132
|
);
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
// ── Central visibility map (GUI-managed) ────────────────────────────────
|
|
136
|
+
// A richer alternative to the binary `paths` list: maps an absolute cwd to
|
|
137
|
+
// an explicit visibility level. Highest-priority runtime layer — an explicit
|
|
138
|
+
// 'public' here even overrides config-glob / gh auto-detect, because the user
|
|
139
|
+
// deliberately marked it. Subdirectories inherit a marked parent.
|
|
140
|
+
|
|
141
|
+
const VIS_LEVELS = ['public', 'name-only', 'hidden'];
|
|
142
|
+
function normLevel(v) { return VIS_LEVELS.includes(v) ? v : null; }
|
|
143
|
+
|
|
144
|
+
function visibilityForCwd(cwd) {
|
|
145
|
+
if (!cwd) return null;
|
|
146
|
+
const map = readPrivateList().visibility;
|
|
147
|
+
if (!map) return null;
|
|
148
|
+
const abs = resolvePath(cwd);
|
|
149
|
+
if (map[abs]) return normLevel(map[abs]);
|
|
150
|
+
for (const [p, v] of Object.entries(map)) {
|
|
151
|
+
if (abs === p || abs.startsWith(p + '/') || abs.startsWith(p + '\\')) return normLevel(v);
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Set (or clear) the explicit visibility for a cwd. `level` of null/'default'
|
|
157
|
+
// removes the override so resolution falls back through the lower layers.
|
|
158
|
+
// Returns the updated visibility map.
|
|
159
|
+
export function setCwdVisibility(cwd, level) {
|
|
160
|
+
const abs = resolvePath(cwd);
|
|
161
|
+
const list = readPrivateList();
|
|
162
|
+
const map = (list.visibility && typeof list.visibility === 'object') ? list.visibility : {};
|
|
163
|
+
const norm = normLevel(level);
|
|
164
|
+
if (norm) map[abs] = norm;
|
|
165
|
+
else delete map[abs];
|
|
166
|
+
// A path managed by the map shouldn't also linger in the legacy binary
|
|
167
|
+
// list, where it would always read as hidden regardless of the map value.
|
|
168
|
+
list.paths = (list.paths || []).filter((p) => p !== abs);
|
|
169
|
+
list.visibility = map;
|
|
170
|
+
writePrivateList(list);
|
|
171
|
+
return map;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Read the current explicit-visibility map (abs cwd → level).
|
|
175
|
+
export function listVisibility() {
|
|
176
|
+
return readPrivateList().visibility || {};
|
|
177
|
+
}
|
|
178
|
+
|
|
125
179
|
// ── GitHub-private detection (best-effort, gh CLI) ──────────────────────
|
|
126
180
|
|
|
127
181
|
function detectGithubPrivate(cwd) {
|
|
@@ -151,6 +205,12 @@ export function resolveVisibility(cwd, config = {}) {
|
|
|
151
205
|
if (proj?.visibility) {
|
|
152
206
|
return { visibility: proj.visibility, projectName: proj.projectName, reason: '.claude-rpc.json' };
|
|
153
207
|
}
|
|
208
|
+
// Central GUI-managed map (incl. explicit 'public' as an opt-out of
|
|
209
|
+
// auto-hide). Ranks above the legacy binary list and config/gh layers.
|
|
210
|
+
const mapped = visibilityForCwd(cwd);
|
|
211
|
+
if (mapped) {
|
|
212
|
+
return { visibility: mapped, projectName: proj?.projectName ?? null, reason: 'private-list (visibility)' };
|
|
213
|
+
}
|
|
154
214
|
if (isInPrivateList(cwd)) {
|
|
155
215
|
return { visibility: 'hidden', projectName: proj?.projectName ?? null, reason: 'private-list' };
|
|
156
216
|
}
|
package/src/server/api.js
CHANGED
|
@@ -173,3 +173,38 @@ export function dayDetail(dayKeyStr) {
|
|
|
173
173
|
if (!day) return null;
|
|
174
174
|
return { day: dayKeyStr, ...day };
|
|
175
175
|
}
|
|
176
|
+
|
|
177
|
+
// Flatten the aggregate's byDay map into a daily-rows CSV for spreadsheet /
|
|
178
|
+
// pandas analysis. One row per day, sorted ascending. Date keys are
|
|
179
|
+
// YYYY-MM-DD and all other columns are numeric, so nothing needs quoting.
|
|
180
|
+
export const CSV_COLUMNS = [
|
|
181
|
+
'date', 'activeMs', 'activeHours', 'sessions', 'userMessages', 'toolCalls',
|
|
182
|
+
'linesAdded', 'linesRemoved', 'cost', 'inputTokens', 'outputTokens',
|
|
183
|
+
'cacheReadTokens', 'cacheWriteTokens', 'notifications',
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
export function aggregateToCsv(agg) {
|
|
187
|
+
const byDay = (agg && agg.byDay) || {};
|
|
188
|
+
const rows = [CSV_COLUMNS.join(',')];
|
|
189
|
+
for (const date of Object.keys(byDay).sort()) {
|
|
190
|
+
const d = byDay[date] || {};
|
|
191
|
+
const activeMs = d.activeMs || 0;
|
|
192
|
+
rows.push([
|
|
193
|
+
date,
|
|
194
|
+
activeMs,
|
|
195
|
+
(activeMs / 3_600_000).toFixed(3),
|
|
196
|
+
d.sessions || 0,
|
|
197
|
+
d.userMessages || 0,
|
|
198
|
+
d.toolCalls || 0,
|
|
199
|
+
d.linesAdded || 0,
|
|
200
|
+
d.linesRemoved || 0,
|
|
201
|
+
(d.cost || 0).toFixed(4),
|
|
202
|
+
d.inputTokens || 0,
|
|
203
|
+
d.outputTokens || 0,
|
|
204
|
+
d.cacheReadTokens || 0,
|
|
205
|
+
d.cacheWriteTokens || 0,
|
|
206
|
+
d.notifications || 0,
|
|
207
|
+
].join(','));
|
|
208
|
+
}
|
|
209
|
+
return rows.join('\n') + '\n';
|
|
210
|
+
}
|