@vibe-cafe/vibe-usage 0.10.23 → 0.10.24
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 +1 -1
- package/src/api.js +33 -0
- package/src/index.js +28 -1
- package/src/init.js +8 -1
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -280,6 +280,39 @@ export function getJson(apiUrl, apiKey, path, { timeoutMs = 15_000 } = {}) {
|
|
|
280
280
|
});
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Which vibecafe account this API key is bound to.
|
|
285
|
+
*
|
|
286
|
+
* Exists because a key minted against the wrong account is otherwise invisible
|
|
287
|
+
* from the client side: ingest and this endpoint both answer 200 for it, so the
|
|
288
|
+
* user sees a healthy CLI and a healthy desktop app while their uploads pile up
|
|
289
|
+
* on an account they never look at. Ask the backend whose data it is and say so.
|
|
290
|
+
*
|
|
291
|
+
* Returns null when the backend is too old to send `account`, or on any
|
|
292
|
+
* transient failure — identity is a nicety, never a reason to fail a command.
|
|
293
|
+
* UNAUTHORIZED still propagates: that one the caller must not swallow.
|
|
294
|
+
*
|
|
295
|
+
* @param {string} apiUrl
|
|
296
|
+
* @param {string} apiKey
|
|
297
|
+
* @returns {Promise<{handle: string, name: string | null} | null>}
|
|
298
|
+
*/
|
|
299
|
+
export async function fetchAccount(apiUrl, apiKey) {
|
|
300
|
+
let data;
|
|
301
|
+
try {
|
|
302
|
+
// bucketsOnly keeps the response to the aggregate we throw away; days=1
|
|
303
|
+
// keeps the window (and the scan) small.
|
|
304
|
+
data = await getJson(apiUrl, apiKey, '/api/usage?days=1&bucketsOnly=true', { timeoutMs: 10_000 });
|
|
305
|
+
} catch (err) {
|
|
306
|
+
if (err.message === 'UNAUTHORIZED') throw err;
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
const account = data?.account;
|
|
310
|
+
if (account && typeof account.handle === 'string') {
|
|
311
|
+
return { handle: account.handle, name: account.name ?? null };
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
|
|
283
316
|
/**
|
|
284
317
|
* GET user settings from the vibecafe API.
|
|
285
318
|
* Returns null after transient failures are exhausted. A 401 remains distinct
|
package/src/index.js
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
normalizeExtraRoot,
|
|
9
9
|
validateExtraRoot,
|
|
10
10
|
} from './extra-roots.js';
|
|
11
|
-
import { failure, smallHeader } from './output.js';
|
|
11
|
+
import { dim as dimText, failure, smallHeader, warn } from './output.js';
|
|
12
|
+
import { fetchAccount } from './api.js';
|
|
12
13
|
|
|
13
14
|
function printSmallHeader() {
|
|
14
15
|
console.log();
|
|
@@ -27,6 +28,8 @@ async function showStatus() {
|
|
|
27
28
|
console.log(` Config: ${getConfigPath()}`);
|
|
28
29
|
console.log(` API key: ${config.apiKey.slice(0, 8)}...`);
|
|
29
30
|
console.log(` API URL: ${config.apiUrl || 'https://vibecafe.ai'}`);
|
|
31
|
+
// 数据算在谁名下,是这里最该回答、以前偏偏答不出的一件事。
|
|
32
|
+
await printBoundAccount(config.apiUrl || 'https://vibecafe.ai', config.apiKey);
|
|
30
33
|
if (config.codexExtraHome) {
|
|
31
34
|
console.log(` Extra Codex Home: ${config.codexExtraHome}`);
|
|
32
35
|
}
|
|
@@ -62,6 +65,30 @@ async function showStatus() {
|
|
|
62
65
|
console.log();
|
|
63
66
|
}
|
|
64
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Print the account this key uploads to. Never throws: an offline machine or an
|
|
70
|
+
* older backend just means we cannot name the account, which must not make
|
|
71
|
+
* `status` fail — but a revoked/invalid key is worth saying out loud.
|
|
72
|
+
*/
|
|
73
|
+
async function printBoundAccount(apiUrl, apiKey) {
|
|
74
|
+
try {
|
|
75
|
+
const account = await fetchAccount(apiUrl, apiKey);
|
|
76
|
+
if (account) {
|
|
77
|
+
const label = account.name ? `@${account.handle}(${account.name})` : `@${account.handle}`;
|
|
78
|
+
console.log(` 账号: ${label}`);
|
|
79
|
+
console.log(dimText(` 数据都记在这个账号名下,不是它就换个账号重新 init`));
|
|
80
|
+
} else {
|
|
81
|
+
console.log(dimText(' 账号: 服务端未返回(后端版本较旧或网络异常)'));
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
if (err.message === 'UNAUTHORIZED') {
|
|
85
|
+
console.log(warn('账号: Key 已失效,请重新运行 `npx @vibe-cafe/vibe-usage init`'));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(dimText(' 账号: 读取失败'));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
65
92
|
const VALID_CONFIG_KEYS = ['apiKey', 'apiUrl', 'hostname', 'codexExtraHome'];
|
|
66
93
|
|
|
67
94
|
function handleConfig(args) {
|
package/src/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createInterface } from 'node:readline';
|
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
3
|
import { hostname as osHostname, platform } from 'node:os';
|
|
4
4
|
import { loadConfig, saveConfig } from './config.js';
|
|
5
|
-
import { ingest, requestDeviceCode, pollDeviceCode } from './api.js';
|
|
5
|
+
import { fetchAccount, ingest, requestDeviceCode, pollDeviceCode } from './api.js';
|
|
6
6
|
import { runSync } from './sync.js';
|
|
7
7
|
import { detectInstalledTools } from './tools.js';
|
|
8
8
|
import { bigHeader, success, failure, warn, arrow, link, dim, divider } from './output.js';
|
|
@@ -68,6 +68,13 @@ export async function runInit(options = {}) {
|
|
|
68
68
|
try {
|
|
69
69
|
await ingest(apiUrl, apiKey, []);
|
|
70
70
|
console.log(success(`验证通过 ${dim(apiKey.slice(0, 12) + '...')}`));
|
|
71
|
+
// 说清楚数据会算在谁名下 —— 授权那一刻浏览器里登录的可能并不是他自己以为的
|
|
72
|
+
// 那个账号,而这是整条链路上最后一次能当场发现的机会。
|
|
73
|
+
const account = await fetchAccount(apiUrl, apiKey).catch(() => null);
|
|
74
|
+
if (account) {
|
|
75
|
+
console.log(success(`已链接到账号 ${account.name ? `@${account.handle}(${account.name})` : `@${account.handle}`}`));
|
|
76
|
+
console.log(dim(' 数据都会记在这个账号名下;不是你要的账号就退出登录后重新 init。'));
|
|
77
|
+
}
|
|
71
78
|
} catch (err) {
|
|
72
79
|
if (err.message === 'UNAUTHORIZED') {
|
|
73
80
|
console.error(failure('API Key 无效,请检查后重试。'));
|