@vibescore/tracker 0.1.2 → 0.2.1
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/cli.js +1 -0
- package/src/commands/init.js +47 -3
- package/src/lib/insforge.js +8 -2
- package/src/lib/vibescore-api.js +28 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -48,6 +48,7 @@ function printHelp() {
|
|
|
48
48
|
'',
|
|
49
49
|
'Notes:',
|
|
50
50
|
' - init installs a Codex notify hook and issues a device token (default: browser sign in/up).',
|
|
51
|
+
' - optional: pass --link-code <code> to skip browser login when provided by Dashboard.',
|
|
51
52
|
' - when ~/.code/config.toml exists, init also installs an Every Code notify hook.',
|
|
52
53
|
' - optional: set VIBESCORE_DASHBOARD_URL (or --dashboard-url) to use a hosted landing page.',
|
|
53
54
|
' - sync parses ~/.codex/sessions/**/rollout-*.jsonl and ~/.code/sessions/**/rollout-*.jsonl (Every Code), then uploads token_count deltas.',
|
package/src/commands/init.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require('node:path');
|
|
|
3
3
|
const fs = require('node:fs/promises');
|
|
4
4
|
const fssync = require('node:fs');
|
|
5
5
|
const cp = require('node:child_process');
|
|
6
|
+
const crypto = require('node:crypto');
|
|
6
7
|
|
|
7
8
|
const { ensureDir, writeFileAtomic, readJson, writeJson, chmod600IfPossible } = require('../lib/fs');
|
|
8
9
|
const { prompt, promptHidden } = require('../lib/prompt');
|
|
@@ -14,7 +15,11 @@ const {
|
|
|
14
15
|
} = require('../lib/codex-config');
|
|
15
16
|
const { upsertClaudeHook, buildClaudeHookCommand } = require('../lib/claude-config');
|
|
16
17
|
const { beginBrowserAuth } = require('../lib/browser-auth');
|
|
17
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
issueDeviceTokenWithPassword,
|
|
20
|
+
issueDeviceTokenWithAccessToken,
|
|
21
|
+
issueDeviceTokenWithLinkCode
|
|
22
|
+
} = require('../lib/insforge');
|
|
18
23
|
|
|
19
24
|
async function cmdInit(argv) {
|
|
20
25
|
const opts = parseArgs(argv);
|
|
@@ -29,6 +34,7 @@ async function cmdInit(argv) {
|
|
|
29
34
|
|
|
30
35
|
const configPath = path.join(trackerDir, 'config.json');
|
|
31
36
|
const notifyOriginalPath = path.join(trackerDir, 'codex_notify_original.json');
|
|
37
|
+
const linkCodeStatePath = path.join(trackerDir, 'link_code_state.json');
|
|
32
38
|
|
|
33
39
|
const baseUrl = opts.baseUrl || process.env.VIBESCORE_INSFORGE_BASE_URL || 'https://5tmappuk.us-east.insforge.app';
|
|
34
40
|
let dashboardUrl = opts.dashboardUrl || process.env.VIBESCORE_DASHBOARD_URL || null;
|
|
@@ -44,7 +50,36 @@ async function cmdInit(argv) {
|
|
|
44
50
|
|
|
45
51
|
await installLocalTrackerApp({ appDir });
|
|
46
52
|
|
|
47
|
-
if (!deviceToken &&
|
|
53
|
+
if (!deviceToken && opts.linkCode) {
|
|
54
|
+
const deviceName = opts.deviceName || os.hostname();
|
|
55
|
+
const platform = normalizePlatform(process.platform);
|
|
56
|
+
const linkCode = String(opts.linkCode);
|
|
57
|
+
const linkCodeHash = crypto.createHash('sha256').update(linkCode).digest('hex');
|
|
58
|
+
const existingLinkState = await readJson(linkCodeStatePath);
|
|
59
|
+
let requestId =
|
|
60
|
+
existingLinkState?.linkCodeHash === linkCodeHash && existingLinkState?.requestId
|
|
61
|
+
? existingLinkState.requestId
|
|
62
|
+
: null;
|
|
63
|
+
if (!requestId) {
|
|
64
|
+
requestId = crypto.randomUUID();
|
|
65
|
+
await writeJson(linkCodeStatePath, {
|
|
66
|
+
linkCodeHash,
|
|
67
|
+
requestId,
|
|
68
|
+
createdAt: new Date().toISOString()
|
|
69
|
+
});
|
|
70
|
+
await chmod600IfPossible(linkCodeStatePath);
|
|
71
|
+
}
|
|
72
|
+
const issued = await issueDeviceTokenWithLinkCode({
|
|
73
|
+
baseUrl,
|
|
74
|
+
linkCode,
|
|
75
|
+
requestId,
|
|
76
|
+
deviceName,
|
|
77
|
+
platform
|
|
78
|
+
});
|
|
79
|
+
deviceToken = issued.token;
|
|
80
|
+
deviceId = issued.deviceId;
|
|
81
|
+
await fs.rm(linkCodeStatePath, { force: true });
|
|
82
|
+
} else if (!deviceToken && !opts.noAuth) {
|
|
48
83
|
const deviceName = opts.deviceName || os.hostname();
|
|
49
84
|
|
|
50
85
|
if (opts.email || opts.password) {
|
|
@@ -171,6 +206,7 @@ function parseArgs(argv) {
|
|
|
171
206
|
email: null,
|
|
172
207
|
password: null,
|
|
173
208
|
deviceName: null,
|
|
209
|
+
linkCode: null,
|
|
174
210
|
noAuth: false,
|
|
175
211
|
noOpen: false
|
|
176
212
|
};
|
|
@@ -182,6 +218,7 @@ function parseArgs(argv) {
|
|
|
182
218
|
else if (a === '--email') out.email = argv[++i] || null;
|
|
183
219
|
else if (a === '--password') out.password = argv[++i] || null;
|
|
184
220
|
else if (a === '--device-name') out.deviceName = argv[++i] || null;
|
|
221
|
+
else if (a === '--link-code') out.linkCode = argv[++i] || null;
|
|
185
222
|
else if (a === '--no-auth') out.noAuth = true;
|
|
186
223
|
else if (a === '--no-open') out.noOpen = true;
|
|
187
224
|
else throw new Error(`Unknown option: ${a}`);
|
|
@@ -194,6 +231,13 @@ function maskSecret(s) {
|
|
|
194
231
|
return `${s.slice(0, 4)}…${s.slice(-4)}`;
|
|
195
232
|
}
|
|
196
233
|
|
|
234
|
+
function normalizePlatform(value) {
|
|
235
|
+
if (value === 'darwin') return 'macos';
|
|
236
|
+
if (value === 'win32') return 'windows';
|
|
237
|
+
if (value === 'linux') return 'linux';
|
|
238
|
+
return 'unknown';
|
|
239
|
+
}
|
|
240
|
+
|
|
197
241
|
function buildNotifyHandler({ trackerDir, packageName }) {
|
|
198
242
|
// Keep this file dependency-free: Node built-ins only.
|
|
199
243
|
// It must never block Codex; it spawns sync in the background and exits 0.
|
|
@@ -377,7 +421,7 @@ async function installLocalTrackerApp({ appDir }) {
|
|
|
377
421
|
|
|
378
422
|
function spawnInitSync({ trackerBinPath, packageName }) {
|
|
379
423
|
const fallbackPkg = packageName || '@vibescore/tracker';
|
|
380
|
-
const argv = ['sync'];
|
|
424
|
+
const argv = ['sync', '--drain'];
|
|
381
425
|
const hasLocalRuntime = typeof trackerBinPath === 'string' && fssync.existsSync(trackerBinPath);
|
|
382
426
|
const cmd = hasLocalRuntime
|
|
383
427
|
? [process.execPath, trackerBinPath, ...argv]
|
package/src/lib/insforge.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { issueDeviceToken, signInWithPassword } = require('./vibescore-api');
|
|
1
|
+
const { exchangeLinkCode, issueDeviceToken, signInWithPassword } = require('./vibescore-api');
|
|
2
2
|
|
|
3
3
|
async function issueDeviceTokenWithPassword({ baseUrl, email, password, deviceName }) {
|
|
4
4
|
const accessToken = await signInWithPassword({ baseUrl, email, password });
|
|
@@ -11,7 +11,13 @@ async function issueDeviceTokenWithAccessToken({ baseUrl, accessToken, deviceNam
|
|
|
11
11
|
return issued;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
async function issueDeviceTokenWithLinkCode({ baseUrl, linkCode, requestId, deviceName, platform }) {
|
|
15
|
+
const issued = await exchangeLinkCode({ baseUrl, linkCode, requestId, deviceName, platform });
|
|
16
|
+
return { token: issued.token, deviceId: issued.deviceId };
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
module.exports = {
|
|
15
20
|
issueDeviceTokenWithPassword,
|
|
16
|
-
issueDeviceTokenWithAccessToken
|
|
21
|
+
issueDeviceTokenWithAccessToken,
|
|
22
|
+
issueDeviceTokenWithLinkCode
|
|
17
23
|
};
|
package/src/lib/vibescore-api.js
CHANGED
|
@@ -35,6 +35,33 @@ async function issueDeviceToken({ baseUrl, accessToken, deviceName, platform = '
|
|
|
35
35
|
return { token, deviceId };
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
async function exchangeLinkCode({ baseUrl, linkCode, requestId, deviceName, platform = 'macos' }) {
|
|
39
|
+
const data = await invokeFunction({
|
|
40
|
+
baseUrl,
|
|
41
|
+
accessToken: null,
|
|
42
|
+
slug: 'vibescore-link-code-exchange',
|
|
43
|
+
method: 'POST',
|
|
44
|
+
body: {
|
|
45
|
+
link_code: linkCode,
|
|
46
|
+
request_id: requestId,
|
|
47
|
+
device_name: deviceName,
|
|
48
|
+
platform
|
|
49
|
+
},
|
|
50
|
+
errorPrefix: 'Link code exchange failed'
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const token = data?.token;
|
|
54
|
+
const deviceId = data?.device_id;
|
|
55
|
+
if (typeof token !== 'string' || token.length === 0) {
|
|
56
|
+
throw new Error('Link code exchange failed: missing token');
|
|
57
|
+
}
|
|
58
|
+
if (typeof deviceId !== 'string' || deviceId.length === 0) {
|
|
59
|
+
throw new Error('Link code exchange failed: missing device_id');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { token, deviceId, userId: data?.user_id || null };
|
|
63
|
+
}
|
|
64
|
+
|
|
38
65
|
async function ingestHourly({ baseUrl, deviceToken, hourly }) {
|
|
39
66
|
const data = await invokeFunctionWithRetry({
|
|
40
67
|
baseUrl,
|
|
@@ -72,6 +99,7 @@ async function syncHeartbeat({ baseUrl, deviceToken }) {
|
|
|
72
99
|
module.exports = {
|
|
73
100
|
signInWithPassword,
|
|
74
101
|
issueDeviceToken,
|
|
102
|
+
exchangeLinkCode,
|
|
75
103
|
ingestHourly,
|
|
76
104
|
syncHeartbeat
|
|
77
105
|
};
|