ai-native-profile 0.1.3 → 0.2.0

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 CHANGED
@@ -33,6 +33,14 @@ The command prints a short-lived verification URL and code. Open the URL, sign i
33
33
  npx --yes ai-native-profile@latest sync
34
34
  ```
35
35
 
36
+ For the fastest setup, sign in on the website and choose **Power up with coding activity**. The site creates a one-time command like this:
37
+
38
+ ```bash
39
+ npx --yes ai-native-profile@latest connect --code ABCD1234
40
+ ```
41
+
42
+ That command is already associated with the signed-in profile. It detects sources, displays the privacy boundary, asks once before the first sync, and updates the open card without requiring the code to be entered again.
43
+
36
44
  To keep syncing every 15 minutes while the command is running:
37
45
 
38
46
  ```bash
package/bin/anp.mjs CHANGED
@@ -8,7 +8,7 @@ import { createInterface } from 'node:readline';
8
8
  import { resolveCodexExecutable } from '../src/codex-executable.mjs';
9
9
  import { addClaudeSessionEvent, createClaudeSessionAccumulator, finalizeClaudeSessionUsage, mergeClaudeUsage, parseClaudeStatsCache } from '../src/claude-usage.mjs';
10
10
 
11
- const VERSION = '0.1.3';
11
+ const VERSION = '0.2.0';
12
12
  const DEFAULT_API_URL = 'https://ai-native-profile.vercel.app';
13
13
  const configDir = join(homedir(), '.config', 'ai-native-profile');
14
14
  const configFile = join(configDir, 'config.json');
@@ -181,9 +181,49 @@ function printSources() {
181
181
  console.log('\nOnly aggregate activity leaves this device. Run `anp preview` to inspect it.');
182
182
  }
183
183
 
184
+ function confirmFirstSync() {
185
+ if (process.argv.includes('--yes')) return Promise.resolve(true);
186
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return Promise.resolve(false);
187
+ const prompt = createInterface({ input:process.stdin, output:process.stdout });
188
+ return new Promise((resolve) => prompt.question('\nSync these aggregate activity counts now? [Y/n] ', (answer) => {
189
+ prompt.close();
190
+ resolve(!/^n(?:o)?$/i.test(answer.trim()));
191
+ }));
192
+ }
193
+
194
+ function printPrivacyPreview() {
195
+ console.log('\nMay sync: dates, provider/model IDs, token and activity counts, duration, coverage, and collector version.');
196
+ console.log('Never syncs: prompts, responses, source code, commands, paths, repositories, credentials, or environment variables.');
197
+ }
198
+
199
+ async function claimWebConnection(apiUrl, userCode) {
200
+ const response = await fetch(`${apiUrl.replace(/\/$/, '')}/api/v1/device/claim`, {
201
+ method:'POST',
202
+ headers:{ 'content-type':'application/json' },
203
+ body:JSON.stringify({ userCode, deviceName:process.env.USER ?? 'Developer device', collectorVersion:VERSION }),
204
+ });
205
+ const body = await response.json().catch(() => ({}));
206
+ if (!response.ok || !body.deviceToken || !body.deviceId) throw new Error(body.error ?? `Connection failed (${response.status}).`);
207
+ saveConfig({ apiUrl, deviceId:body.deviceId, deviceToken:body.deviceToken });
208
+ console.log('Connected to your AI Native Profile.');
209
+ printSources();
210
+ printPrivacyPreview();
211
+ if (await confirmFirstSync()) {
212
+ await sync();
213
+ console.log('Your card is updated. Return to the browser to see it.');
214
+ } else {
215
+ console.log('\nConnected without syncing. Run `anp preview` to inspect the payload, then `anp sync` when ready.');
216
+ }
217
+ }
218
+
184
219
  async function connect() {
185
220
  const apiUrl = option('api-url') ?? process.env.ANP_API_URL ?? DEFAULT_API_URL;
186
221
  if (!/^https?:\/\//.test(apiUrl)) throw new Error('The API URL must start with https:// or http://.');
222
+ const connectionCode = option('code');
223
+ if (connectionCode) {
224
+ await claimWebConnection(apiUrl, connectionCode);
225
+ return;
226
+ }
187
227
  const response = await fetch(`${apiUrl.replace(/\/$/, '')}/api/v1/device/pair`, { method:'POST', headers:{ 'content-type':'application/json' }, body:JSON.stringify({ deviceName:process.env.USER ?? 'Developer device', collectorVersion:VERSION }) });
188
228
  if (!response.ok) throw new Error(`Pairing failed (${response.status}).`);
189
229
  const pair = await response.json();
@@ -216,7 +256,7 @@ async function sync() {
216
256
  }
217
257
 
218
258
  function help() {
219
- console.log(`AI Native Profile collector ${VERSION}\n\nUsage: anp <command> [options]\n\n connect Pair this device with the cloud dashboard\n --api-url <url> overrides the hosted dashboard\n sources Detect supported coding tools\n preview Print the exact aggregate payload\n sync Send one aggregate batch\n watch Sync every 15 minutes until stopped\n doctor Check configuration and sources\n sessions Explain selected-session sharing\n share Publish a selected sanitized session\n unshare Revoke a shared session\n export Alias for preview\n disconnect Remove the local platform pairing\n`);
259
+ console.log(`AI Native Profile collector ${VERSION}\n\nUsage: anp <command> [options]\n\n connect Pair this device with the cloud dashboard\n --code <code> claims a command created by the signed-in website\n --api-url <url> overrides the hosted dashboard\n --yes approves the first aggregate sync without prompting\n sources Detect supported coding tools\n preview Print the exact aggregate payload\n sync Send one aggregate batch\n watch Sync every 15 minutes until stopped\n doctor Check configuration and sources\n sessions Explain selected-session sharing\n share Publish a selected sanitized session\n unshare Revoke a shared session\n export Alias for preview\n disconnect Remove the local platform pairing\n`);
220
260
  }
221
261
 
222
262
  function option(name) { const index = process.argv.indexOf(`--${name}`); return index >= 0 ? process.argv[index + 1] : undefined; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-native-profile",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Privacy-first collector for AI Native Profile",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,7 +31,7 @@ export async function readCodexAccountUsage(timeoutMs = 8_000, executable = reso
31
31
  });
32
32
  try {
33
33
  await started;
34
- await request(1, 'initialize', { clientInfo: { name: 'ai_native_profile', title: 'AI Native Profile', version: '0.1.3' } });
34
+ await request(1, 'initialize', { clientInfo: { name: 'ai_native_profile', title: 'AI Native Profile', version: '0.2.0' } });
35
35
  child.stdin.write(`${JSON.stringify({ method: 'initialized', params: {} })}\n`);
36
36
  return await request(2, 'account/usage/read');
37
37
  } finally {