awarts 0.2.8 → 0.3.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/dist/index.js +261 -245
- 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,261 @@ 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(opts = {}) {
|
|
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
|
+
...opts.note && { note: opts.note }
|
|
6754
|
+
});
|
|
6755
|
+
if (!res.ok) {
|
|
6756
|
+
submitSpin.fail("Sync failed.");
|
|
6757
|
+
if (res.status === 401) {
|
|
6758
|
+
error("Authentication expired. Run awarts login to re-authenticate.");
|
|
6759
|
+
} else {
|
|
6760
|
+
error(`Server returned status ${res.status}`);
|
|
6761
|
+
const errorData = res.data;
|
|
6762
|
+
if (errorData?.error)
|
|
6763
|
+
error(String(errorData.error));
|
|
6764
|
+
}
|
|
6765
|
+
return;
|
|
6766
|
+
}
|
|
6767
|
+
const { processed, posts_created, errors } = res.data;
|
|
6768
|
+
if (errors && errors.length > 0) {
|
|
6769
|
+
submitSpin.warn(source_default.yellow(`Synced with ${errors.length} error(s).`));
|
|
6770
|
+
for (const e of errors) {
|
|
6771
|
+
error(` ${e.date} / ${e.provider}: ${e.error}`);
|
|
6772
|
+
}
|
|
6773
|
+
} else {
|
|
6774
|
+
submitSpin.succeed(source_default.green("Sync complete!"));
|
|
6775
|
+
}
|
|
6776
|
+
console.log();
|
|
6777
|
+
kv("Entries processed", processed);
|
|
6778
|
+
kv("Posts created", posts_created);
|
|
6779
|
+
console.log();
|
|
6780
|
+
success("Your usage data is now live on AWARTS.");
|
|
6781
|
+
console.log();
|
|
6782
|
+
try {
|
|
6783
|
+
const existingPid = await readPid();
|
|
6784
|
+
const isRunning = existingPid ? isProcessRunning(existingPid) : false;
|
|
6785
|
+
if (!isRunning) {
|
|
6786
|
+
const pid = await spawnDaemon(DEFAULT_INTERVAL_MS);
|
|
6787
|
+
dim(`Auto-sync daemon started (PID ${pid}, every 5 min)`);
|
|
6788
|
+
dim("Manage with: awarts daemon status | stop | logs");
|
|
6789
|
+
console.log();
|
|
6790
|
+
}
|
|
6791
|
+
} catch {}
|
|
6792
|
+
} catch (err) {
|
|
6793
|
+
submitSpin.fail("Could not reach the AWARTS server.");
|
|
6794
|
+
error(err instanceof Error ? err.message : String(err));
|
|
6795
|
+
dim("Is the backend running? Check AWARTS_API_URL or ~/.awarts/config.json");
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
|
|
6799
|
+
// src/commands/login.ts
|
|
6800
|
+
var POLL_INTERVAL_MS = 2000;
|
|
6801
|
+
var MAX_POLLS = 300;
|
|
6802
|
+
async function stopExistingDaemon() {
|
|
6803
|
+
const pid = await readPid();
|
|
6804
|
+
if (pid && isProcessRunning(pid)) {
|
|
6805
|
+
killProcess(pid);
|
|
6806
|
+
await removePid();
|
|
6807
|
+
} else if (pid) {
|
|
6808
|
+
await removePid();
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
async function loginCommand() {
|
|
6812
|
+
banner();
|
|
6813
|
+
const existing = await loadAuth();
|
|
6814
|
+
if (existing) {
|
|
6815
|
+
info("You are already logged in.");
|
|
6816
|
+
kv("User ID", existing.user_id);
|
|
6817
|
+
kv("Saved at", existing.saved_at);
|
|
6818
|
+
console.log();
|
|
6819
|
+
dim("To switch accounts: awarts login --force");
|
|
6820
|
+
dim("To log out: awarts logout");
|
|
6821
|
+
console.log();
|
|
6822
|
+
return;
|
|
6823
|
+
}
|
|
6824
|
+
await startDeviceAuth();
|
|
6825
|
+
}
|
|
6826
|
+
async function loginForceCommand() {
|
|
6827
|
+
banner();
|
|
6828
|
+
const pid = await readPid();
|
|
6829
|
+
if (pid && isProcessRunning(pid)) {
|
|
6830
|
+
dim("Stopping existing daemon...");
|
|
6831
|
+
await stopExistingDaemon();
|
|
6832
|
+
}
|
|
6833
|
+
await clearAuth();
|
|
6834
|
+
await startDeviceAuth();
|
|
6835
|
+
}
|
|
6836
|
+
async function startDeviceAuth() {
|
|
6837
|
+
const spin = spinner("Starting device authentication...");
|
|
6838
|
+
spin.start();
|
|
6839
|
+
let initData;
|
|
6840
|
+
try {
|
|
6841
|
+
const res = await postUnauthenticated("/api/auth/cli/init");
|
|
6842
|
+
if (!res.ok) {
|
|
6843
|
+
spin.fail("Failed to start authentication.");
|
|
6844
|
+
error(`Server responded with status ${res.status}`);
|
|
6845
|
+
return;
|
|
6846
|
+
}
|
|
6847
|
+
initData = res.data;
|
|
6848
|
+
spin.stop();
|
|
6849
|
+
} catch (err) {
|
|
6850
|
+
spin.fail("Could not reach the AWARTS server.");
|
|
6851
|
+
error(err instanceof Error ? err.message : String(err));
|
|
6852
|
+
dim("Is the backend running? Check AWARTS_API_URL or ~/.awarts/config.json");
|
|
6853
|
+
return;
|
|
6854
|
+
}
|
|
6855
|
+
const FRONTEND_URL = process.env.AWARTS_FRONTEND_URL ?? "https://awarts.vercel.app";
|
|
6856
|
+
const verifyUrl = `${FRONTEND_URL}/cli/verify?code=${initData.code}`;
|
|
6857
|
+
console.log();
|
|
6858
|
+
console.log(` ${source_default.bold("Your verification code:")} ${source_default.bgWhite.black.bold(` ${initData.code} `)}`);
|
|
6859
|
+
console.log();
|
|
6860
|
+
info(`Open this URL to verify: ${source_default.cyan.underline(verifyUrl)}`);
|
|
6861
|
+
console.log();
|
|
6862
|
+
try {
|
|
6863
|
+
await open_default(verifyUrl);
|
|
6864
|
+
dim("Browser opened automatically. Waiting for verification...");
|
|
6865
|
+
} catch {
|
|
6866
|
+
dim("Could not open browser. Please visit the URL above manually.");
|
|
6867
|
+
}
|
|
6868
|
+
console.log();
|
|
6869
|
+
const pollSpin = spinner("Waiting for verification...");
|
|
6870
|
+
pollSpin.start();
|
|
6871
|
+
for (let attempt = 0;attempt < MAX_POLLS; attempt++) {
|
|
6872
|
+
await sleep(POLL_INTERVAL_MS);
|
|
6873
|
+
try {
|
|
6874
|
+
const res = await postUnauthenticated("/api/auth/cli/poll", {
|
|
6875
|
+
device_token: initData.device_token
|
|
6876
|
+
});
|
|
6877
|
+
if (!res.ok) {
|
|
6878
|
+
pollSpin.fail("Poll request failed.");
|
|
6879
|
+
error(`Status ${res.status}`);
|
|
6880
|
+
return;
|
|
6881
|
+
}
|
|
6882
|
+
const { status, token, user_id } = res.data;
|
|
6883
|
+
if (status === "verified" && token && user_id) {
|
|
6884
|
+
await stopExistingDaemon();
|
|
6885
|
+
await saveAuth({
|
|
6886
|
+
token,
|
|
6887
|
+
user_id,
|
|
6888
|
+
saved_at: new Date().toISOString()
|
|
6889
|
+
});
|
|
6890
|
+
pollSpin.succeed(source_default.green("Authenticated successfully!"));
|
|
6891
|
+
console.log();
|
|
6892
|
+
kv("User ID", user_id);
|
|
6893
|
+
success("Token saved to ~/.awarts/auth.json");
|
|
6894
|
+
console.log();
|
|
6895
|
+
try {
|
|
6896
|
+
await syncCommand();
|
|
6897
|
+
} catch {
|
|
6898
|
+
dim("Initial sync skipped — you can run awarts sync manually.");
|
|
6899
|
+
}
|
|
6900
|
+
try {
|
|
6901
|
+
const pid = await spawnDaemon(DEFAULT_INTERVAL_MS);
|
|
6902
|
+
success(`Auto-sync daemon started (PID ${pid}, every 5 min)`);
|
|
6903
|
+
dim("Your usage data will sync automatically in the background.");
|
|
6904
|
+
dim("Manage with: awarts daemon status | stop | logs");
|
|
6905
|
+
} catch {
|
|
6906
|
+
dim("Run awarts daemon start to enable auto-sync.");
|
|
6907
|
+
}
|
|
6908
|
+
console.log();
|
|
6909
|
+
return;
|
|
6910
|
+
}
|
|
6911
|
+
if (status === "expired") {
|
|
6912
|
+
pollSpin.fail("Verification code expired.");
|
|
6913
|
+
error("Please run awarts login again to get a new code.");
|
|
6914
|
+
return;
|
|
6915
|
+
}
|
|
6916
|
+
pollSpin.text = `Waiting for verification... (${Math.floor((attempt + 1) * POLL_INTERVAL_MS / 1000)}s)`;
|
|
6917
|
+
} catch {}
|
|
6918
|
+
}
|
|
6919
|
+
pollSpin.fail("Timed out waiting for verification.");
|
|
6920
|
+
error("Please try awarts login again.");
|
|
6921
|
+
}
|
|
6922
|
+
function sleep(ms) {
|
|
6923
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6924
|
+
}
|
|
6925
|
+
|
|
6784
6926
|
// src/commands/push.ts
|
|
6785
6927
|
var VALID_PROVIDERS = new Set(["claude", "codex", "gemini", "antigravity"]);
|
|
6786
6928
|
async function pushCommand(opts) {
|
|
@@ -6866,7 +7008,8 @@ async function pushCommand(opts) {
|
|
|
6866
7008
|
const res = await post("/api/usage/submit", {
|
|
6867
7009
|
entries: cleanEntries,
|
|
6868
7010
|
source: "cli",
|
|
6869
|
-
hash
|
|
7011
|
+
hash,
|
|
7012
|
+
...opts.note && { note: opts.note }
|
|
6870
7013
|
});
|
|
6871
7014
|
if (!res.ok) {
|
|
6872
7015
|
spin.fail("Submission failed.");
|
|
@@ -6968,133 +7111,6 @@ async function statusCommand() {
|
|
|
6968
7111
|
}
|
|
6969
7112
|
}
|
|
6970
7113
|
|
|
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
7114
|
// src/commands/seed.ts
|
|
7099
7115
|
import fs14 from "node:fs/promises";
|
|
7100
7116
|
import path10 from "node:path";
|
|
@@ -7392,7 +7408,7 @@ async function checkForUpdates() {
|
|
|
7392
7408
|
|
|
7393
7409
|
// src/index.ts
|
|
7394
7410
|
var program2 = new Command;
|
|
7395
|
-
program2.name("awarts").description("Track your AI coding spend across Claude, Codex, Gemini & Antigravity").version("0.
|
|
7411
|
+
program2.name("awarts").description("Track your AI coding spend across Claude, Codex, Gemini & Antigravity").version("0.3.0").hook("preAction", () => checkForUpdates());
|
|
7396
7412
|
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
7413
|
try {
|
|
7398
7414
|
if (opts.force) {
|
|
@@ -7405,7 +7421,7 @@ program2.command("login").description("Authenticate with your AWARTS account via
|
|
|
7405
7421
|
process.exit(1);
|
|
7406
7422
|
}
|
|
7407
7423
|
});
|
|
7408
|
-
program2.command("push").description("Read local usage data and submit to AWARTS").option("-p, --provider <name>", "Only push data from a specific provider (claude, codex, gemini, antigravity)").option("-n, --dry-run", "Show what would be pushed without submitting").action(async (opts) => {
|
|
7424
|
+
program2.command("push").description("Read local usage data and submit to AWARTS").option("-p, --provider <name>", "Only push data from a specific provider (claude, codex, gemini, antigravity)").option("-n, --dry-run", "Show what would be pushed without submitting").option("--note <text>", "Attach a note/description to today's post").action(async (opts) => {
|
|
7409
7425
|
try {
|
|
7410
7426
|
await pushCommand(opts);
|
|
7411
7427
|
} catch (err) {
|
|
@@ -7413,9 +7429,9 @@ program2.command("push").description("Read local usage data and submit to AWARTS
|
|
|
7413
7429
|
process.exit(1);
|
|
7414
7430
|
}
|
|
7415
7431
|
});
|
|
7416
|
-
program2.command("sync").description("Auto-detect all providers and push usage data").action(async () => {
|
|
7432
|
+
program2.command("sync").description("Auto-detect all providers and push usage data").option("--note <text>", "Attach a note/description to today's post").action(async (opts) => {
|
|
7417
7433
|
try {
|
|
7418
|
-
await syncCommand();
|
|
7434
|
+
await syncCommand(opts);
|
|
7419
7435
|
} catch (err) {
|
|
7420
7436
|
error(err instanceof Error ? err.message : String(err));
|
|
7421
7437
|
process.exit(1);
|