awarts 0.2.8 → 0.2.9

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.
Files changed (2) hide show
  1. package/dist/index.js +255 -241
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5857,119 +5857,6 @@ async function spawnDaemon(intervalMs) {
5857
5857
  return pid;
5858
5858
  }
5859
5859
 
5860
- // src/commands/login.ts
5861
- var POLL_INTERVAL_MS = 2000;
5862
- var MAX_POLLS = 300;
5863
- async function loginCommand() {
5864
- banner();
5865
- const existing = await loadAuth();
5866
- if (existing) {
5867
- info("You are already logged in.");
5868
- kv("User ID", existing.user_id);
5869
- kv("Saved at", existing.saved_at);
5870
- console.log();
5871
- dim("Run awarts login --force to re-authenticate.");
5872
- dim("Run awarts logout to clear your credentials.");
5873
- console.log();
5874
- return;
5875
- }
5876
- await startDeviceAuth();
5877
- }
5878
- async function loginForceCommand() {
5879
- banner();
5880
- await clearAuth();
5881
- await startDeviceAuth();
5882
- }
5883
- async function startDeviceAuth() {
5884
- const spin = spinner("Starting device authentication...");
5885
- spin.start();
5886
- let initData;
5887
- try {
5888
- const res = await postUnauthenticated("/api/auth/cli/init");
5889
- if (!res.ok) {
5890
- spin.fail("Failed to start authentication.");
5891
- error(`Server responded with status ${res.status}`);
5892
- return;
5893
- }
5894
- initData = res.data;
5895
- spin.stop();
5896
- } catch (err) {
5897
- spin.fail("Could not reach the AWARTS server.");
5898
- error(err instanceof Error ? err.message : String(err));
5899
- dim("Is the backend running? Check AWARTS_API_URL or ~/.awarts/config.json");
5900
- return;
5901
- }
5902
- const FRONTEND_URL = process.env.AWARTS_FRONTEND_URL ?? "https://awarts.vercel.app";
5903
- const verifyUrl = `${FRONTEND_URL}/cli/verify?code=${initData.code}`;
5904
- console.log();
5905
- console.log(` ${source_default.bold("Your verification code:")} ${source_default.bgWhite.black.bold(` ${initData.code} `)}`);
5906
- console.log();
5907
- info(`Open this URL to verify: ${source_default.cyan.underline(verifyUrl)}`);
5908
- console.log();
5909
- try {
5910
- await open_default(verifyUrl);
5911
- dim("Browser opened automatically. Waiting for verification...");
5912
- } catch {
5913
- dim("Could not open browser. Please visit the URL above manually.");
5914
- }
5915
- console.log();
5916
- const pollSpin = spinner("Waiting for verification...");
5917
- pollSpin.start();
5918
- for (let attempt = 0;attempt < MAX_POLLS; attempt++) {
5919
- await sleep(POLL_INTERVAL_MS);
5920
- try {
5921
- const res = await postUnauthenticated("/api/auth/cli/poll", {
5922
- device_token: initData.device_token
5923
- });
5924
- if (!res.ok) {
5925
- pollSpin.fail("Poll request failed.");
5926
- error(`Status ${res.status}`);
5927
- return;
5928
- }
5929
- const { status, token, user_id } = res.data;
5930
- if (status === "verified" && token && user_id) {
5931
- await saveAuth({
5932
- token,
5933
- user_id,
5934
- saved_at: new Date().toISOString()
5935
- });
5936
- pollSpin.succeed(source_default.green("Authenticated successfully!"));
5937
- console.log();
5938
- kv("User ID", user_id);
5939
- success("Token saved to ~/.awarts/auth.json");
5940
- console.log();
5941
- try {
5942
- const existingPid = await readPid();
5943
- const isRunning = existingPid ? isProcessRunning(existingPid) : false;
5944
- if (!isRunning) {
5945
- const pid = await spawnDaemon(DEFAULT_INTERVAL_MS);
5946
- success(`Auto-sync daemon started (PID ${pid}, every 5 min)`);
5947
- dim("Your usage data will sync automatically in the background.");
5948
- dim("Manage with: awarts daemon status | stop | logs");
5949
- } else {
5950
- dim("Auto-sync daemon is already running.");
5951
- }
5952
- } catch {
5953
- dim("Run awarts daemon start to enable auto-sync.");
5954
- }
5955
- console.log();
5956
- return;
5957
- }
5958
- if (status === "expired") {
5959
- pollSpin.fail("Verification code expired.");
5960
- error("Please run awarts login again to get a new code.");
5961
- return;
5962
- }
5963
- pollSpin.text = `Waiting for verification... (${Math.floor((attempt + 1) * POLL_INTERVAL_MS / 1000)}s)`;
5964
- } catch {}
5965
- }
5966
- pollSpin.fail("Timed out waiting for verification.");
5967
- error("Please try awarts login again.");
5968
- }
5969
- function sleep(ms) {
5970
- return new Promise((resolve) => setTimeout(resolve, ms));
5971
- }
5972
-
5973
5860
  // src/adapters/claude.ts
5974
5861
  import fs9 from "node:fs/promises";
5975
5862
  import path5 from "node:path";
@@ -6781,6 +6668,260 @@ function hashEntries(entries) {
6781
6668
  return createHash("sha256").update(canonical).digest("hex");
6782
6669
  }
6783
6670
 
6671
+ // src/commands/sync.ts
6672
+ async function syncCommand() {
6673
+ banner();
6674
+ const auth = await loadAuth();
6675
+ if (!auth) {
6676
+ error("Not logged in. Run awarts login first.");
6677
+ return;
6678
+ }
6679
+ const detectSpin = spinner("Detecting installed providers...");
6680
+ detectSpin.start();
6681
+ const results = await detectAll();
6682
+ const detected = results.filter((r) => r.detected);
6683
+ if (detected.length === 0) {
6684
+ detectSpin.warn("No AI coding tools detected.");
6685
+ console.log();
6686
+ dim("Looked for: Claude, Codex, Gemini, Antigravity");
6687
+ dim("Make sure you have used one of these tools on this machine.");
6688
+ console.log();
6689
+ return;
6690
+ }
6691
+ const names = detected.map((r) => providerLabel(r.adapter.name)).join(", ");
6692
+ detectSpin.succeed(`Detected: ${names}`);
6693
+ console.log();
6694
+ const allEntries = [];
6695
+ for (const { adapter } of detected) {
6696
+ const spin = spinner(`Reading ${adapter.displayName} data...`);
6697
+ spin.start();
6698
+ try {
6699
+ const entries = await adapter.read();
6700
+ if (entries.length === 0) {
6701
+ spin.info(source_default.dim(`${adapter.displayName} -- no usage data found`));
6702
+ if (adapter.name === "codex") {
6703
+ dim(` Set your OpenAI API key: ${source_default.cyan("awarts keys set openai <your-key>")}`);
6704
+ dim(` Or create usage files manually — see ${source_default.cyan("awarts.com/docs")}`);
6705
+ } else if (adapter.name === "gemini") {
6706
+ dim(` Set your Google API key: ${source_default.cyan("awarts keys set google <your-key>")}`);
6707
+ dim(` Or create usage files manually — see ${source_default.cyan("awarts.com/docs")}`);
6708
+ } else if (adapter.name === "antigravity") {
6709
+ dim(` Set your API key: ${source_default.cyan("awarts keys set antigravity <your-key>")}`);
6710
+ dim(` Or create usage files manually — see ${source_default.cyan("awarts.com/docs")}`);
6711
+ }
6712
+ } else {
6713
+ allEntries.push(...entries);
6714
+ const totalCost2 = entries.reduce((s, e) => s + e.cost_usd, 0);
6715
+ spin.succeed(`${providerLabel(adapter.name)} ` + `${source_default.bold(String(entries.length))} entries ` + `${source_default.yellow(usd(totalCost2))}`);
6716
+ }
6717
+ } catch (err) {
6718
+ spin.fail(`${adapter.displayName} -- error reading data`);
6719
+ error(err instanceof Error ? err.message : String(err));
6720
+ }
6721
+ }
6722
+ console.log();
6723
+ if (allEntries.length === 0) {
6724
+ warn("No usage data found across any provider.");
6725
+ console.log();
6726
+ info(`Set API keys to fetch real billing data:`);
6727
+ dim(` ${source_default.cyan("awarts keys set openai <key>")} — for Codex / OpenAI`);
6728
+ dim(` ${source_default.cyan("awarts keys set google <key>")} — for Gemini`);
6729
+ dim(` ${source_default.cyan("awarts keys set antigravity <key>")} — for Antigravity`);
6730
+ dim(`Or visit ${source_default.cyan("awarts.com/docs")} for manual import instructions.`);
6731
+ console.log();
6732
+ return;
6733
+ }
6734
+ const totalCost = allEntries.reduce((s, e) => s + e.cost_usd, 0);
6735
+ const totalTokens = allEntries.reduce((s, e) => s + e.input_tokens + e.output_tokens, 0);
6736
+ const uniqueDates = new Set(allEntries.map((e) => e.date));
6737
+ divider();
6738
+ kv("Total entries", allEntries.length);
6739
+ kv("Unique days", uniqueDates.size);
6740
+ kv("Total cost", usd(totalCost));
6741
+ kv("Total tokens", tokens(totalTokens));
6742
+ divider();
6743
+ console.log();
6744
+ const hash = hashEntries(allEntries);
6745
+ const submitSpin = spinner("Syncing with AWARTS...");
6746
+ submitSpin.start();
6747
+ const cleanEntries = allEntries.map(({ cost_source, ...rest }) => rest);
6748
+ try {
6749
+ const res = await post("/api/usage/submit", {
6750
+ entries: cleanEntries,
6751
+ source: "cli",
6752
+ hash
6753
+ });
6754
+ if (!res.ok) {
6755
+ submitSpin.fail("Sync failed.");
6756
+ if (res.status === 401) {
6757
+ error("Authentication expired. Run awarts login to re-authenticate.");
6758
+ } else {
6759
+ error(`Server returned status ${res.status}`);
6760
+ const errorData = res.data;
6761
+ if (errorData?.error)
6762
+ error(String(errorData.error));
6763
+ }
6764
+ return;
6765
+ }
6766
+ const { processed, posts_created, errors } = res.data;
6767
+ if (errors && errors.length > 0) {
6768
+ submitSpin.warn(source_default.yellow(`Synced with ${errors.length} error(s).`));
6769
+ for (const e of errors) {
6770
+ error(` ${e.date} / ${e.provider}: ${e.error}`);
6771
+ }
6772
+ } else {
6773
+ submitSpin.succeed(source_default.green("Sync complete!"));
6774
+ }
6775
+ console.log();
6776
+ kv("Entries processed", processed);
6777
+ kv("Posts created", posts_created);
6778
+ console.log();
6779
+ success("Your usage data is now live on AWARTS.");
6780
+ console.log();
6781
+ try {
6782
+ const existingPid = await readPid();
6783
+ const isRunning = existingPid ? isProcessRunning(existingPid) : false;
6784
+ if (!isRunning) {
6785
+ const pid = await spawnDaemon(DEFAULT_INTERVAL_MS);
6786
+ dim(`Auto-sync daemon started (PID ${pid}, every 5 min)`);
6787
+ dim("Manage with: awarts daemon status | stop | logs");
6788
+ console.log();
6789
+ }
6790
+ } catch {}
6791
+ } catch (err) {
6792
+ submitSpin.fail("Could not reach the AWARTS server.");
6793
+ error(err instanceof Error ? err.message : String(err));
6794
+ dim("Is the backend running? Check AWARTS_API_URL or ~/.awarts/config.json");
6795
+ }
6796
+ }
6797
+
6798
+ // src/commands/login.ts
6799
+ var POLL_INTERVAL_MS = 2000;
6800
+ var MAX_POLLS = 300;
6801
+ async function stopExistingDaemon() {
6802
+ const pid = await readPid();
6803
+ if (pid && isProcessRunning(pid)) {
6804
+ killProcess(pid);
6805
+ await removePid();
6806
+ } else if (pid) {
6807
+ await removePid();
6808
+ }
6809
+ }
6810
+ async function loginCommand() {
6811
+ banner();
6812
+ const existing = await loadAuth();
6813
+ if (existing) {
6814
+ info("You are already logged in.");
6815
+ kv("User ID", existing.user_id);
6816
+ kv("Saved at", existing.saved_at);
6817
+ console.log();
6818
+ dim("To switch accounts: awarts login --force");
6819
+ dim("To log out: awarts logout");
6820
+ console.log();
6821
+ return;
6822
+ }
6823
+ await startDeviceAuth();
6824
+ }
6825
+ async function loginForceCommand() {
6826
+ banner();
6827
+ const pid = await readPid();
6828
+ if (pid && isProcessRunning(pid)) {
6829
+ dim("Stopping existing daemon...");
6830
+ await stopExistingDaemon();
6831
+ }
6832
+ await clearAuth();
6833
+ await startDeviceAuth();
6834
+ }
6835
+ async function startDeviceAuth() {
6836
+ const spin = spinner("Starting device authentication...");
6837
+ spin.start();
6838
+ let initData;
6839
+ try {
6840
+ const res = await postUnauthenticated("/api/auth/cli/init");
6841
+ if (!res.ok) {
6842
+ spin.fail("Failed to start authentication.");
6843
+ error(`Server responded with status ${res.status}`);
6844
+ return;
6845
+ }
6846
+ initData = res.data;
6847
+ spin.stop();
6848
+ } catch (err) {
6849
+ spin.fail("Could not reach the AWARTS server.");
6850
+ error(err instanceof Error ? err.message : String(err));
6851
+ dim("Is the backend running? Check AWARTS_API_URL or ~/.awarts/config.json");
6852
+ return;
6853
+ }
6854
+ const FRONTEND_URL = process.env.AWARTS_FRONTEND_URL ?? "https://awarts.vercel.app";
6855
+ const verifyUrl = `${FRONTEND_URL}/cli/verify?code=${initData.code}`;
6856
+ console.log();
6857
+ console.log(` ${source_default.bold("Your verification code:")} ${source_default.bgWhite.black.bold(` ${initData.code} `)}`);
6858
+ console.log();
6859
+ info(`Open this URL to verify: ${source_default.cyan.underline(verifyUrl)}`);
6860
+ console.log();
6861
+ try {
6862
+ await open_default(verifyUrl);
6863
+ dim("Browser opened automatically. Waiting for verification...");
6864
+ } catch {
6865
+ dim("Could not open browser. Please visit the URL above manually.");
6866
+ }
6867
+ console.log();
6868
+ const pollSpin = spinner("Waiting for verification...");
6869
+ pollSpin.start();
6870
+ for (let attempt = 0;attempt < MAX_POLLS; attempt++) {
6871
+ await sleep(POLL_INTERVAL_MS);
6872
+ try {
6873
+ const res = await postUnauthenticated("/api/auth/cli/poll", {
6874
+ device_token: initData.device_token
6875
+ });
6876
+ if (!res.ok) {
6877
+ pollSpin.fail("Poll request failed.");
6878
+ error(`Status ${res.status}`);
6879
+ return;
6880
+ }
6881
+ const { status, token, user_id } = res.data;
6882
+ if (status === "verified" && token && user_id) {
6883
+ await stopExistingDaemon();
6884
+ await saveAuth({
6885
+ token,
6886
+ user_id,
6887
+ saved_at: new Date().toISOString()
6888
+ });
6889
+ pollSpin.succeed(source_default.green("Authenticated successfully!"));
6890
+ console.log();
6891
+ kv("User ID", user_id);
6892
+ success("Token saved to ~/.awarts/auth.json");
6893
+ console.log();
6894
+ try {
6895
+ await syncCommand();
6896
+ } catch {
6897
+ dim("Initial sync skipped — you can run awarts sync manually.");
6898
+ }
6899
+ try {
6900
+ const pid = await spawnDaemon(DEFAULT_INTERVAL_MS);
6901
+ success(`Auto-sync daemon started (PID ${pid}, every 5 min)`);
6902
+ dim("Your usage data will sync automatically in the background.");
6903
+ dim("Manage with: awarts daemon status | stop | logs");
6904
+ } catch {
6905
+ dim("Run awarts daemon start to enable auto-sync.");
6906
+ }
6907
+ console.log();
6908
+ return;
6909
+ }
6910
+ if (status === "expired") {
6911
+ pollSpin.fail("Verification code expired.");
6912
+ error("Please run awarts login again to get a new code.");
6913
+ return;
6914
+ }
6915
+ pollSpin.text = `Waiting for verification... (${Math.floor((attempt + 1) * POLL_INTERVAL_MS / 1000)}s)`;
6916
+ } catch {}
6917
+ }
6918
+ pollSpin.fail("Timed out waiting for verification.");
6919
+ error("Please try awarts login again.");
6920
+ }
6921
+ function sleep(ms) {
6922
+ return new Promise((resolve) => setTimeout(resolve, ms));
6923
+ }
6924
+
6784
6925
  // src/commands/push.ts
6785
6926
  var VALID_PROVIDERS = new Set(["claude", "codex", "gemini", "antigravity"]);
6786
6927
  async function pushCommand(opts) {
@@ -6968,133 +7109,6 @@ async function statusCommand() {
6968
7109
  }
6969
7110
  }
6970
7111
 
6971
- // src/commands/sync.ts
6972
- async function syncCommand() {
6973
- banner();
6974
- const auth = await loadAuth();
6975
- if (!auth) {
6976
- error("Not logged in. Run awarts login first.");
6977
- return;
6978
- }
6979
- const detectSpin = spinner("Detecting installed providers...");
6980
- detectSpin.start();
6981
- const results = await detectAll();
6982
- const detected = results.filter((r) => r.detected);
6983
- if (detected.length === 0) {
6984
- detectSpin.warn("No AI coding tools detected.");
6985
- console.log();
6986
- dim("Looked for: Claude, Codex, Gemini, Antigravity");
6987
- dim("Make sure you have used one of these tools on this machine.");
6988
- console.log();
6989
- return;
6990
- }
6991
- const names = detected.map((r) => providerLabel(r.adapter.name)).join(", ");
6992
- detectSpin.succeed(`Detected: ${names}`);
6993
- console.log();
6994
- const allEntries = [];
6995
- for (const { adapter } of detected) {
6996
- const spin = spinner(`Reading ${adapter.displayName} data...`);
6997
- spin.start();
6998
- try {
6999
- const entries = await adapter.read();
7000
- if (entries.length === 0) {
7001
- spin.info(source_default.dim(`${adapter.displayName} -- no usage data found`));
7002
- if (adapter.name === "codex") {
7003
- dim(` Set your OpenAI API key: ${source_default.cyan("awarts keys set openai <your-key>")}`);
7004
- dim(` Or create usage files manually — see ${source_default.cyan("awarts.com/docs")}`);
7005
- } else if (adapter.name === "gemini") {
7006
- dim(` Set your Google API key: ${source_default.cyan("awarts keys set google <your-key>")}`);
7007
- dim(` Or create usage files manually — see ${source_default.cyan("awarts.com/docs")}`);
7008
- } else if (adapter.name === "antigravity") {
7009
- dim(` Set your API key: ${source_default.cyan("awarts keys set antigravity <your-key>")}`);
7010
- dim(` Or create usage files manually — see ${source_default.cyan("awarts.com/docs")}`);
7011
- }
7012
- } else {
7013
- allEntries.push(...entries);
7014
- const totalCost2 = entries.reduce((s, e) => s + e.cost_usd, 0);
7015
- spin.succeed(`${providerLabel(adapter.name)} ` + `${source_default.bold(String(entries.length))} entries ` + `${source_default.yellow(usd(totalCost2))}`);
7016
- }
7017
- } catch (err) {
7018
- spin.fail(`${adapter.displayName} -- error reading data`);
7019
- error(err instanceof Error ? err.message : String(err));
7020
- }
7021
- }
7022
- console.log();
7023
- if (allEntries.length === 0) {
7024
- warn("No usage data found across any provider.");
7025
- console.log();
7026
- info(`Set API keys to fetch real billing data:`);
7027
- dim(` ${source_default.cyan("awarts keys set openai <key>")} — for Codex / OpenAI`);
7028
- dim(` ${source_default.cyan("awarts keys set google <key>")} — for Gemini`);
7029
- dim(` ${source_default.cyan("awarts keys set antigravity <key>")} — for Antigravity`);
7030
- dim(`Or visit ${source_default.cyan("awarts.com/docs")} for manual import instructions.`);
7031
- console.log();
7032
- return;
7033
- }
7034
- const totalCost = allEntries.reduce((s, e) => s + e.cost_usd, 0);
7035
- const totalTokens = allEntries.reduce((s, e) => s + e.input_tokens + e.output_tokens, 0);
7036
- const uniqueDates = new Set(allEntries.map((e) => e.date));
7037
- divider();
7038
- kv("Total entries", allEntries.length);
7039
- kv("Unique days", uniqueDates.size);
7040
- kv("Total cost", usd(totalCost));
7041
- kv("Total tokens", tokens(totalTokens));
7042
- divider();
7043
- console.log();
7044
- const hash = hashEntries(allEntries);
7045
- const submitSpin = spinner("Syncing with AWARTS...");
7046
- submitSpin.start();
7047
- const cleanEntries = allEntries.map(({ cost_source, ...rest }) => rest);
7048
- try {
7049
- const res = await post("/api/usage/submit", {
7050
- entries: cleanEntries,
7051
- source: "cli",
7052
- hash
7053
- });
7054
- if (!res.ok) {
7055
- submitSpin.fail("Sync failed.");
7056
- if (res.status === 401) {
7057
- error("Authentication expired. Run awarts login to re-authenticate.");
7058
- } else {
7059
- error(`Server returned status ${res.status}`);
7060
- const errorData = res.data;
7061
- if (errorData?.error)
7062
- error(String(errorData.error));
7063
- }
7064
- return;
7065
- }
7066
- const { processed, posts_created, errors } = res.data;
7067
- if (errors && errors.length > 0) {
7068
- submitSpin.warn(source_default.yellow(`Synced with ${errors.length} error(s).`));
7069
- for (const e of errors) {
7070
- error(` ${e.date} / ${e.provider}: ${e.error}`);
7071
- }
7072
- } else {
7073
- submitSpin.succeed(source_default.green("Sync complete!"));
7074
- }
7075
- console.log();
7076
- kv("Entries processed", processed);
7077
- kv("Posts created", posts_created);
7078
- console.log();
7079
- success("Your usage data is now live on AWARTS.");
7080
- console.log();
7081
- try {
7082
- const existingPid = await readPid();
7083
- const isRunning = existingPid ? isProcessRunning(existingPid) : false;
7084
- if (!isRunning) {
7085
- const pid = await spawnDaemon(DEFAULT_INTERVAL_MS);
7086
- dim(`Auto-sync daemon started (PID ${pid}, every 5 min)`);
7087
- dim("Manage with: awarts daemon status | stop | logs");
7088
- console.log();
7089
- }
7090
- } catch {}
7091
- } catch (err) {
7092
- submitSpin.fail("Could not reach the AWARTS server.");
7093
- error(err instanceof Error ? err.message : String(err));
7094
- dim("Is the backend running? Check AWARTS_API_URL or ~/.awarts/config.json");
7095
- }
7096
- }
7097
-
7098
7112
  // src/commands/seed.ts
7099
7113
  import fs14 from "node:fs/promises";
7100
7114
  import path10 from "node:path";
@@ -7392,7 +7406,7 @@ async function checkForUpdates() {
7392
7406
 
7393
7407
  // src/index.ts
7394
7408
  var program2 = new Command;
7395
- program2.name("awarts").description("Track your AI coding spend across Claude, Codex, Gemini & Antigravity").version("0.2.8").hook("preAction", () => checkForUpdates());
7409
+ program2.name("awarts").description("Track your AI coding spend across Claude, Codex, Gemini & Antigravity").version("0.2.9").hook("preAction", () => checkForUpdates());
7396
7410
  program2.command("login").description("Authenticate with your AWARTS account via device auth").option("--force", "Re-authenticate even if already logged in").action(async (opts) => {
7397
7411
  try {
7398
7412
  if (opts.force) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "awarts",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Track your AI coding across Claude, Codex, Gemini & Antigravity",
5
5
  "type": "module",
6
6
  "license": "MIT",