@howaboua/opencode-usage-plugin 0.1.2 → 0.1.3

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.
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * providers/copilot/auth.ts
3
3
  * Provides authentication and configuration helpers for GitHub Copilot.
4
- * Handles local auth token discovery and standard OpenCode credentials.
5
4
  */
6
5
  import { type CopilotAuthData } from "./types.js";
7
6
  export declare function readCopilotAuth(): Promise<CopilotAuthData | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/providers/copilot/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,wBAAsB,eAAe,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAevE"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/providers/copilot/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,wBAAsB,eAAe,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAevE"}
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * providers/copilot/auth.ts
3
3
  * Provides authentication and configuration helpers for GitHub Copilot.
4
- * Handles local auth token discovery and standard OpenCode credentials.
5
4
  */
6
5
  import { existsSync } from "fs";
7
6
  import { readFile } from "fs/promises";
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * providers/copilot/index.ts
3
3
  * Main entry point for the GitHub Copilot usage provider.
4
- * Orchestrates token exchange and fetching from the internal API.
5
4
  */
6
5
  import type { UsageProvider } from "../base.js";
7
6
  export declare const CopilotProvider: UsageProvider<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/copilot/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AA8D/C,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,IAAI,CAuD/C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/copilot/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AA8D/C,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,IAAI,CAuD/C,CAAA"}
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * providers/copilot/index.ts
3
3
  * Main entry point for the GitHub Copilot usage provider.
4
- * Orchestrates token exchange and fetching from the internal API.
5
4
  */
6
5
  import { readCopilotAuth } from "./auth.js";
7
6
  import { toCopilotQuotaFromInternal, } from "./response.js";
@@ -21,7 +21,7 @@ export function toCopilotQuotaFromInternal(data) {
21
21
  percentRemaining: chatTotal > 0 ? Math.round((chatRemaining / chatTotal) * 100) : 0,
22
22
  resetTime: data.limited_user_reset_date || data.quota_reset_date,
23
23
  completionsUsed: completionsRemaining,
24
- completionsTotal: completionsTotal,
24
+ completionsTotal,
25
25
  };
26
26
  }
27
27
  if (data.quota_snapshots?.premium_interactions) {
@@ -6,5 +6,5 @@ import type { UsageProvider } from "../base";
6
6
  export type { ProxyResponse } from "./types";
7
7
  export { fetchProxyLimits } from "./fetch";
8
8
  export { formatProxyLimits } from "./format";
9
- export declare const ProxyProvider: UsageProvider;
9
+ export declare const ProxyProvider: UsageProvider<void>;
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAM5C,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAyI5C,eAAO,MAAM,aAAa,EAAE,aAwB3B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAM5C,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAyI5C,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,IAAI,CAwB7C,CAAA"}
@@ -136,7 +136,7 @@ export const ProxyProvider = {
136
136
  const config = await loadUsageConfig();
137
137
  const data = await fetchProxyLimits(config);
138
138
  return {
139
- timestamp: data.timestamp * 1000,
139
+ timestamp: (data.timestamp || Date.now() / 1000) * 1000,
140
140
  provider: "proxy",
141
141
  planType: null,
142
142
  primary: null,
package/dist/types.d.ts CHANGED
@@ -63,6 +63,7 @@ export interface UsageSnapshot {
63
63
  proxyQuota?: ProxyQuota;
64
64
  copilotQuota?: CopilotQuota;
65
65
  updatedAt: number;
66
+ isMissing?: boolean;
66
67
  }
67
68
  export interface UsageEntry {
68
69
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/ui/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,KAAK,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AAExC,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAqChB;AAyID,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BhB"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/ui/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAGtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,KAAK,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AAExC,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAqChB;AA4KD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BhB"}
package/dist/ui/status.js CHANGED
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Renders usage snapshots into readable status text.
3
3
  */
4
+ import { platform, homedir } from "os";
5
+ import { join } from "path";
4
6
  export async function sendStatusMessage(options) {
5
7
  // @ts-ignore
6
8
  const bus = options.client.bus;
@@ -120,7 +122,41 @@ function formatCopilotSnapshot(snapshot) {
120
122
  }
121
123
  return lines;
122
124
  }
125
+ function getAppDataPath() {
126
+ const home = homedir();
127
+ const plat = platform();
128
+ if (plat === "darwin")
129
+ return join(home, ".config", "opencode");
130
+ if (plat === "win32")
131
+ return join(process.env.APPDATA || join(home, "AppData", "Roaming"), "opencode");
132
+ return join(process.env.XDG_CONFIG_HOME || join(home, ".config"), "opencode");
133
+ }
134
+ function formatMissingSnapshot(snapshot) {
135
+ const provider = snapshot.provider;
136
+ const label = provider === "codex" ? "codex" : provider === "proxy" ? "proxy" : "gh";
137
+ const configPath = join(getAppDataPath(), "usage-config.jsonc");
138
+ let providerInstruction = "";
139
+ if (provider === "codex") {
140
+ providerInstruction = "if you dont have codex oauth, please set your usage-config.jsonc to openai: false";
141
+ }
142
+ else if (provider === "proxy") {
143
+ providerInstruction = "if you are not running Mirrowel's proxy, please set your usage-config.jsonc to proxy: false";
144
+ }
145
+ else if (provider === "copilot") {
146
+ providerInstruction = "if you are not running GitHub Copilot, please set your usage-config.jsonc to copilot: false";
147
+ }
148
+ return [
149
+ `→ [${provider.toUpperCase()}] - ${providerInstruction}`,
150
+ "",
151
+ `The file can be found in ${configPath}. Please read the comments in the file or visit the repo for the readme.`,
152
+ "",
153
+ "If you are seeing empty usages or errors, despite having everything set up correctly, please fire an issue at https://github.com/IgorWarzocha/opencode-usage-plugin/issues - thank you!",
154
+ ];
155
+ }
123
156
  function formatSnapshot(snapshot) {
157
+ if (snapshot.isMissing) {
158
+ return formatMissingSnapshot(snapshot);
159
+ }
124
160
  if (snapshot.provider === "proxy" && snapshot.proxyQuota) {
125
161
  return formatProxySnapshot(snapshot);
126
162
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/usage/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAI3C,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CA6C5D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/usage/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAiB3C,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CA0C5D"}
@@ -2,15 +2,22 @@
2
2
  * Configuration management for the Usage Plugin.
3
3
  */
4
4
  import { join } from "path";
5
- import { homedir } from "os";
6
- const CONFIG_PATH = join(homedir(), ".config", "opencode", "usage-config.jsonc");
5
+ import { homedir, platform } from "os";
6
+ function getConfigPath() {
7
+ const plat = platform();
8
+ const home = homedir();
9
+ if (plat === "win32") {
10
+ return join(process.env.APPDATA || join(home, "AppData", "Roaming"), "opencode", "usage-config.jsonc");
11
+ }
12
+ // For macOS, Linux, and other Unix-like systems, use XDG_CONFIG_HOME or default to ~/.config
13
+ const configHome = process.env.XDG_CONFIG_HOME || join(home, ".config");
14
+ return join(configHome, "opencode", "usage-config.jsonc");
15
+ }
16
+ const CONFIG_PATH = getConfigPath();
7
17
  export async function loadUsageConfig() {
8
18
  const file = Bun.file(CONFIG_PATH);
9
19
  if (!(await file.exists())) {
10
- const content = `/**
11
- * Usage Plugin Configuration
12
- */
13
- {
20
+ const content = `{
14
21
  "endpoint": "",
15
22
  "apiKey": "",
16
23
  "timeout": 10000,
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAI7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAe5C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAWlD,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA2CnF;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC,CASrD"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAI7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAe5C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAWlD,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA4EnF;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC,CA8BrD"}
@@ -5,7 +5,7 @@
5
5
  import z from "zod";
6
6
  import { providers } from "../providers";
7
7
  import { loadUsageConfig } from "./config";
8
- import { getAuthFilePath } from "../utils";
8
+ import { getPossibleAuthPaths } from "../utils";
9
9
  import { resolveProviderAuths } from "./registry";
10
10
  const authEntrySchema = z
11
11
  .object({
@@ -51,6 +51,8 @@ export async function fetchUsageSnapshots(filter) {
51
51
  const auths = await loadAuths();
52
52
  const entries = resolveProviderAuths(auths, null);
53
53
  const snapshots = [];
54
+ const coreProviders = ["codex", "proxy", "copilot"];
55
+ const fetchedProviders = new Set();
54
56
  const fetches = entries
55
57
  .filter((entry) => !targetProvider || entry.providerID === targetProvider)
56
58
  .filter((entry) => isProviderEnabled(entry.providerID))
@@ -58,9 +60,15 @@ export async function fetchUsageSnapshots(filter) {
58
60
  const provider = providers[entry.providerID];
59
61
  if (!provider?.fetchUsage)
60
62
  return;
61
- const snapshot = await provider.fetchUsage(entry.auth).catch(() => null);
62
- if (snapshot)
63
- snapshots.push(snapshot);
63
+ try {
64
+ const snapshot = await provider.fetchUsage(entry.auth);
65
+ if (snapshot) {
66
+ snapshots.push(snapshot);
67
+ fetchedProviders.add(entry.providerID);
68
+ }
69
+ }
70
+ catch {
71
+ }
64
72
  });
65
73
  const specialProviders = ["proxy", "copilot"];
66
74
  for (const id of specialProviders) {
@@ -70,27 +78,84 @@ export async function fetchUsageSnapshots(filter) {
70
78
  fetches.push(provider
71
79
  .fetchUsage(undefined)
72
80
  .then((snapshot) => {
73
- if (snapshot)
81
+ if (snapshot) {
74
82
  snapshots.push(snapshot);
83
+ fetchedProviders.add(id);
84
+ }
75
85
  })
76
86
  .catch(() => { }));
77
87
  }
78
88
  }
79
89
  }
80
90
  await Promise.race([Promise.all(fetches), timeout(5000)]);
91
+ for (const id of coreProviders) {
92
+ if (isProviderEnabled(id) && !fetchedProviders.has(id)) {
93
+ if (!targetProvider || targetProvider === id) {
94
+ snapshots.push({
95
+ timestamp: Date.now(),
96
+ provider: id,
97
+ planType: null,
98
+ primary: null,
99
+ secondary: null,
100
+ codeReview: null,
101
+ credits: null,
102
+ updatedAt: Date.now(),
103
+ isMissing: true,
104
+ });
105
+ }
106
+ }
107
+ }
81
108
  return snapshots;
82
109
  }
83
110
  export async function loadAuths() {
84
- const authPath = getAuthFilePath();
85
- const data = await Bun.file(authPath)
86
- .json()
87
- .catch(() => ({}));
88
- if (!data || typeof data !== "object")
89
- return {};
90
- const parsed = authRecordSchema.safeParse(data);
111
+ const possiblePaths = getPossibleAuthPaths();
112
+ const mergedAuth = {};
113
+ for (const authPath of possiblePaths.reverse()) {
114
+ try {
115
+ const file = Bun.file(authPath);
116
+ if (!(await file.exists()))
117
+ continue;
118
+ const data = await file.json();
119
+ if (data && typeof data === "object") {
120
+ if (authPath.includes(".codex")) {
121
+ const codexAuth = transformCodexAuth(data);
122
+ if (codexAuth) {
123
+ Object.assign(mergedAuth, codexAuth);
124
+ }
125
+ continue;
126
+ }
127
+ const parsed = authRecordSchema.safeParse(data);
128
+ if (parsed.success) {
129
+ Object.assign(mergedAuth, parsed.data);
130
+ }
131
+ }
132
+ }
133
+ catch {
134
+ continue;
135
+ }
136
+ }
137
+ return mergedAuth;
138
+ }
139
+ function transformCodexAuth(data) {
140
+ const codexAuthSchema = z.object({
141
+ tokens: z.object({
142
+ access_token: z.string(),
143
+ account_id: z.string().optional(),
144
+ refresh_token: z.string().optional(),
145
+ }),
146
+ });
147
+ const parsed = codexAuthSchema.safeParse(data);
91
148
  if (!parsed.success)
92
- return {};
93
- return parsed.data;
149
+ return null;
150
+ const { access_token, account_id, refresh_token } = parsed.data.tokens;
151
+ return {
152
+ openai: {
153
+ type: "oauth",
154
+ access: access_token,
155
+ accountId: account_id,
156
+ refresh: refresh_token,
157
+ },
158
+ };
94
159
  }
95
160
  function timeout(ms) {
96
161
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -1,3 +1,3 @@
1
1
  export { parseBooleanHeader, parseIntegerHeader, parseNumberHeader } from "./headers";
2
- export { getAppDataPath, getAuthFilePath } from "./paths";
2
+ export { getAppDataPath, getAuthFilePath, getPossibleAuthPaths } from "./paths";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACrF,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACrF,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA"}
@@ -1,2 +1,2 @@
1
1
  export { parseBooleanHeader, parseIntegerHeader, parseNumberHeader } from "./headers";
2
- export { getAppDataPath, getAuthFilePath } from "./paths";
2
+ export { getAppDataPath, getAuthFilePath, getPossibleAuthPaths } from "./paths";
@@ -3,5 +3,13 @@
3
3
  * Centralizes platform-specific paths so callers stay simple and consistent.
4
4
  */
5
5
  export declare function getAppDataPath(): string;
6
+ /**
7
+ * Returns all possible auth file paths for the current platform.
8
+ * On macOS, OpenCode uses Linux-style paths, so we check both.
9
+ */
10
+ export declare function getPossibleAuthPaths(): string[];
11
+ /**
12
+ * Returns the first existing auth file path, or the default location if none exist.
13
+ */
6
14
  export declare function getAuthFilePath(): string;
7
15
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,wBAAgB,cAAc,IAAI,MAAM,CAiBvC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,wBAAgB,cAAc,IAAI,MAAM,CAiBvC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAyB/C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAYxC"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { homedir, platform } from "os";
6
6
  import { join } from "path";
7
+ import { existsSync } from "fs";
7
8
  export function getAppDataPath() {
8
9
  const plat = platform();
9
10
  const home = homedir();
@@ -19,6 +20,47 @@ export function getAppDataPath() {
19
20
  }
20
21
  return join(home, ".local", "share", "opencode");
21
22
  }
23
+ /**
24
+ * Returns all possible auth file paths for the current platform.
25
+ * On macOS, OpenCode uses Linux-style paths, so we check both.
26
+ */
27
+ export function getPossibleAuthPaths() {
28
+ const plat = platform();
29
+ const home = homedir();
30
+ const pathSet = new Set();
31
+ if (plat === "darwin") {
32
+ // OpenCode on macOS uses Linux-style paths
33
+ pathSet.add(join(home, ".local", "share", "opencode", "auth.json"));
34
+ // Standard macOS location (fallback)
35
+ pathSet.add(join(home, "Library", "Application Support", "opencode", "auth.json"));
36
+ // Codex-specific auth (fallback)
37
+ pathSet.add(join(home, ".codex", "auth.json"));
38
+ }
39
+ else if (plat === "win32") {
40
+ pathSet.add(join(process.env.APPDATA || join(home, "AppData", "Roaming"), "opencode", "auth.json"));
41
+ }
42
+ else {
43
+ // Linux/other
44
+ const xdgData = process.env.XDG_DATA_HOME;
45
+ if (xdgData) {
46
+ pathSet.add(join(xdgData, "opencode", "auth.json"));
47
+ }
48
+ pathSet.add(join(home, ".local", "share", "opencode", "auth.json"));
49
+ pathSet.add(join(home, ".codex", "auth.json"));
50
+ }
51
+ return Array.from(pathSet);
52
+ }
53
+ /**
54
+ * Returns the first existing auth file path, or the default location if none exist.
55
+ */
22
56
  export function getAuthFilePath() {
23
- return join(getAppDataPath(), "auth.json");
57
+ const possiblePaths = getPossibleAuthPaths();
58
+ // Return the first existing path
59
+ for (const path of possiblePaths) {
60
+ if (existsSync(path)) {
61
+ return path;
62
+ }
63
+ }
64
+ // Return the default (first) path if none exist
65
+ return possiblePaths[0];
24
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/opencode-usage-plugin",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "opencode plugin for tracking AI provider usage, rate limits, and quotas",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",