@howaboua/opencode-usage-plugin 0.0.1 → 0.0.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,6 +1,6 @@
1
1
  /**
2
- * Hooks for proxy-limits functionality.
3
- * Manages silent responses after tool execution.
2
+ * Hooks for usage status display.
3
+ * Manages silent responses after command execution if needed.
4
4
  */
5
5
  export declare function markSilent(sessionID: string, messageID: string): void;
6
6
  export declare function proxyHooks(): {
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Hooks for proxy-limits functionality.
3
- * Manages silent responses after tool execution.
2
+ * Hooks for usage status display.
3
+ * Manages silent responses after command execution if needed.
4
4
  */
5
5
  const silence = new Map();
6
6
  export function markSilent(sessionID, messageID) {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Plugin entry point for Usage Tracking.
3
- * Wires hooks and tools for live usage snapshots.
3
+ * Wires hooks for live usage snapshots.
4
4
  */
5
5
  import type { Plugin } from "@opencode-ai/plugin";
6
6
  export declare const UsagePlugin: Plugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAOjD,eAAO,MAAM,WAAW,EAAE,MA+CzB,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAMjD,eAAO,MAAM,WAAW,EAAE,MA2CzB,CAAA;AAED,eAAe,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Plugin entry point for Usage Tracking.
3
- * Wires hooks and tools for live usage snapshots.
3
+ * Wires hooks for live usage snapshots.
4
4
  */
5
- import { commandHooks, sessionHooks, proxyHooks, markSilent } from "./hooks";
5
+ import { commandHooks, sessionHooks, proxyHooks } from "./hooks";
6
6
  import { createUsageState } from "./state";
7
- import { usageTool, createProxyLimitsTool } from "./tools";
8
7
  import { loadAuths } from "./usage/fetch";
9
8
  import { loadProxyConfig } from "./providers/proxy/config";
10
9
  export const UsagePlugin = async ({ client }) => {
@@ -44,10 +43,6 @@ export const UsagePlugin = async ({ client }) => {
44
43
  "command.execute.before": commandHookHandlers["command.execute.before"],
45
44
  ...sessionHooks(state),
46
45
  ...proxyHookHandlers,
47
- tool: {
48
- "usage.get": usageTool(),
49
- "proxy-limits": createProxyLimitsTool(sendStatusMessage, markSilent),
50
- },
51
46
  };
52
47
  };
53
48
  export default UsagePlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAI1C,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAkD5D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAI1C,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAwD5D"}
@@ -38,7 +38,10 @@ export async function loadProxyConfig() {
38
38
  }
39
39
  try {
40
40
  const content = await file.text();
41
- const cleanJson = content.replace(/(\".*?\"|\'.*?\')|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g1) => g1 ?? "");
41
+ // Remove comments first (both // and /* */)
42
+ const withoutComments = content.replace(/(\".*?\"|\'.*?\')|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g1) => g1 ?? "");
43
+ // Remove trailing commas before closing brackets/braces
44
+ const cleanJson = withoutComments.replace(/,(\s*[}\]])/g, "$1");
42
45
  const config = JSON.parse(cleanJson);
43
46
  if (!config.endpoint) {
44
47
  throw new Error('Config must contain "endpoint" field');
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA0B,MAAM,SAAS,CAAA;AAiFpE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAiC7D"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA8B,MAAM,SAAS,CAAA;AAsGxE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAkC7D"}
@@ -48,26 +48,44 @@ function aggregateCredentialsByTier(credentials) {
48
48
  };
49
49
  for (const cred of credentials) {
50
50
  const tier = normalizeTier(cred.tier);
51
- const groups = cred.model_groups ?? {};
52
- for (const [name, group] of Object.entries(groups)) {
51
+ const groupUsage = cred.group_usage ?? {};
52
+ for (const [name, groupData] of Object.entries(groupUsage)) {
53
53
  if (!(name in GROUP_MAPPING))
54
54
  continue;
55
55
  const mappedName = GROUP_MAPPING[name];
56
+ // Find the best window from group_usage
57
+ const windows = groupData.windows || {};
58
+ let bestWindow = null;
59
+ // Priority order for windows
60
+ const windowPriority = ["daily", "5h", "1h", "15m"];
61
+ for (const windowName of windowPriority) {
62
+ if (windows[windowName]) {
63
+ bestWindow = windows[windowName];
64
+ break;
65
+ }
66
+ }
67
+ // Fallback to any available window
68
+ if (!bestWindow && Object.keys(windows).length > 0) {
69
+ bestWindow = Object.values(windows)[0];
70
+ }
71
+ if (!bestWindow)
72
+ continue;
56
73
  const existing = result[tier].get(mappedName);
57
74
  if (existing) {
58
- existing.remaining += group.requests_remaining;
59
- existing.max += group.requests_max;
60
- if (group.reset_time_iso) {
61
- if (!existing.resetTime || new Date(group.reset_time_iso) > new Date(existing.resetTime)) {
62
- existing.resetTime = group.reset_time_iso;
75
+ existing.remaining += bestWindow.remaining;
76
+ existing.max += bestWindow.limit || 0;
77
+ if (bestWindow.reset_at) {
78
+ const newResetTime = new Date(bestWindow.reset_at * 1000).toISOString();
79
+ if (!existing.resetTime || new Date(newResetTime) > new Date(existing.resetTime)) {
80
+ existing.resetTime = newResetTime;
63
81
  }
64
82
  }
65
83
  }
66
84
  else {
67
85
  result[tier].set(mappedName, {
68
- remaining: group.requests_remaining,
69
- max: group.requests_max,
70
- resetTime: group.reset_time_iso,
86
+ remaining: bestWindow.remaining,
87
+ max: bestWindow.limit || bestWindow.remaining,
88
+ resetTime: bestWindow.reset_at ? new Date(bestWindow.reset_at * 1000).toISOString() : null,
71
89
  });
72
90
  }
73
91
  }
@@ -84,7 +102,8 @@ export function formatProxyLimits(data) {
84
102
  }
85
103
  for (const [providerName, provider] of Object.entries(data.providers)) {
86
104
  lines.push(`${providerName}:`);
87
- const tierData = aggregateCredentialsByTier(provider.credentials ?? []);
105
+ const credentialsArray = Object.values(provider.credentials ?? {});
106
+ const tierData = aggregateCredentialsByTier(credentialsArray);
88
107
  for (const [tierName, quotas] of Object.entries(tierData)) {
89
108
  if (quotas.size === 0)
90
109
  continue;
@@ -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,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA8F5C,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,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA+I5C,eAAO,MAAM,aAAa,EAAE,aAwB3B,CAAA"}
@@ -11,31 +11,70 @@ const GROUP_MAPPING = {
11
11
  "claude": "claude",
12
12
  "g3-pro": "g3-pro",
13
13
  "g3-flash": "g3-fla",
14
+ "g25-flash": "25-flash",
15
+ "g25-lite": "25-lite",
14
16
  "pro": "g3-pro",
15
- "3-flash": "g3-fla"
17
+ "3-flash": "g3-fla",
18
+ "25-flash": "25-flash",
19
+ "25-lite": "25-lite"
16
20
  };
17
21
  function normalizeTier(tier) {
18
22
  if (!tier)
19
23
  return "free";
20
24
  return tier.includes("free") ? "free" : "paid";
21
25
  }
22
- function parseQuotaGroupsFromCredential(modelGroups) {
23
- if (!modelGroups)
26
+ /**
27
+ * Extract quota groups from group_usage data
28
+ * New API structure: group_usage[groupName].windows[windowName]
29
+ */
30
+ function parseQuotaGroupsFromCredential(groupUsage) {
31
+ if (!groupUsage)
24
32
  return [];
25
- return Object.entries(modelGroups)
26
- .filter(([name]) => name in GROUP_MAPPING)
27
- .map(([name, group]) => {
28
- const realRemainingPct = group.requests_max > 0
29
- ? Math.round((group.requests_remaining / group.requests_max) * 100)
30
- : 0;
31
- return {
32
- name: GROUP_MAPPING[name],
33
- remaining: group.requests_remaining,
34
- max: group.requests_max,
35
- remainingPct: realRemainingPct,
36
- resetTime: group.reset_time_iso,
37
- };
38
- });
33
+ const result = new Map();
34
+ for (const [groupName, groupData] of Object.entries(groupUsage)) {
35
+ const mappedName = GROUP_MAPPING[groupName];
36
+ if (!mappedName)
37
+ continue;
38
+ // Find the window with the best data (prefer daily, then 5h, then any)
39
+ const windows = groupData.windows || {};
40
+ let bestWindow = null;
41
+ // Priority order for windows
42
+ const windowPriority = ["daily", "5h", "1h", "15m"];
43
+ for (const windowName of windowPriority) {
44
+ if (windows[windowName]) {
45
+ bestWindow = windows[windowName];
46
+ break;
47
+ }
48
+ }
49
+ // Fallback to any available window
50
+ if (!bestWindow && Object.keys(windows).length > 0) {
51
+ bestWindow = Object.values(windows)[0];
52
+ }
53
+ if (!bestWindow)
54
+ continue;
55
+ const existing = result.get(mappedName);
56
+ if (existing) {
57
+ existing.remaining += bestWindow.remaining;
58
+ existing.max += bestWindow.limit || 0;
59
+ // Use the latest reset time
60
+ if (bestWindow.reset_at) {
61
+ const newResetTime = new Date(bestWindow.reset_at * 1000).toISOString();
62
+ if (!existing.resetTime || new Date(newResetTime) > new Date(existing.resetTime)) {
63
+ existing.resetTime = newResetTime;
64
+ }
65
+ }
66
+ }
67
+ else {
68
+ result.set(mappedName, {
69
+ name: mappedName,
70
+ remaining: bestWindow.remaining,
71
+ max: bestWindow.limit || bestWindow.remaining,
72
+ remainingPct: bestWindow.limit ? Math.round((bestWindow.remaining / bestWindow.limit) * 100) : 0,
73
+ resetTime: bestWindow.reset_at ? new Date(bestWindow.reset_at * 1000).toISOString() : null,
74
+ });
75
+ }
76
+ }
77
+ return Array.from(result.values());
39
78
  }
40
79
  function aggregateByTier(credentials) {
41
80
  const tiers = {
@@ -44,7 +83,7 @@ function aggregateByTier(credentials) {
44
83
  };
45
84
  for (const cred of credentials) {
46
85
  const tier = normalizeTier(cred.tier);
47
- const groups = parseQuotaGroupsFromCredential(cred.model_groups);
86
+ const groups = parseQuotaGroupsFromCredential(cred.group_usage);
48
87
  for (const group of groups) {
49
88
  const existing = tiers[tier].get(group.name);
50
89
  if (existing) {
@@ -76,13 +115,17 @@ function aggregateByTier(credentials) {
76
115
  function parseProviders(data) {
77
116
  if (!data.providers)
78
117
  return [];
79
- return Object.entries(data.providers).map(([name, provider]) => ({
80
- name,
81
- tiers: aggregateByTier(provider.credentials ?? []),
82
- }));
118
+ return Object.entries(data.providers).map(([name, provider]) => {
119
+ // Convert credentials object to array
120
+ const credentialsArray = Object.values(provider.credentials || {});
121
+ return {
122
+ name,
123
+ tiers: aggregateByTier(credentialsArray),
124
+ };
125
+ });
83
126
  }
84
127
  function parseProxyQuota(data) {
85
- const summary = data.global_summary ?? data.summary;
128
+ const summary = data.summary;
86
129
  return {
87
130
  providers: parseProviders(data),
88
131
  totalCredentials: summary?.total_credentials ?? 0,
@@ -13,96 +13,116 @@ export type ProxyConfig = {
13
13
  };
14
14
  /** Token statistics from the proxy */
15
15
  export type TokenStats = {
16
- input_cached: number;
17
- input_uncached: number;
18
- input_cache_pct: number;
19
- output: number;
16
+ input_cached?: number;
17
+ input_uncached?: number;
18
+ input_cache_pct?: number;
19
+ output?: number;
20
+ prompt_tokens?: number;
21
+ completion_tokens?: number;
22
+ thinking_tokens?: number;
23
+ output_tokens?: number;
24
+ prompt_tokens_cache_read?: number;
25
+ prompt_tokens_cache_write?: number;
26
+ total_tokens?: number;
27
+ request_count?: number;
28
+ success_count?: number;
29
+ failure_count?: number;
30
+ approx_cost?: number;
20
31
  };
21
- /** Quota group aggregation */
22
- export type QuotaGroup = {
23
- avg_remaining_pct: number;
24
- credentials_exhausted: number;
25
- credentials_total: number;
26
- models: string[];
27
- tiers: Record<string, {
28
- active: number;
32
+ /** Window-based quota information */
33
+ export type WindowQuota = {
34
+ limit?: number;
35
+ remaining: number;
36
+ reset_at?: number | null;
37
+ request_count?: number;
38
+ success_count?: number;
39
+ failure_count?: number;
40
+ total_used?: number;
41
+ total_remaining?: number;
42
+ total_max?: number;
43
+ remaining_pct?: number;
44
+ };
45
+ /** Model group usage information */
46
+ export type GroupUsageWindow = {
47
+ [windowName: string]: WindowQuota;
48
+ };
49
+ export type GroupUsage = {
50
+ windows: GroupUsageWindow;
51
+ totals: TokenStats;
52
+ fair_cycle_exhausted?: boolean;
53
+ fair_cycle_reason?: string | null;
54
+ cooldown_remaining?: number | null;
55
+ cooldown_source?: string | null;
56
+ custom_cap?: number | null;
57
+ };
58
+ /** Model usage information */
59
+ export type ModelUsage = {
60
+ windows: GroupUsageWindow;
61
+ totals: TokenStats;
62
+ };
63
+ /** Tier availability info */
64
+ export type TierAvailability = {
65
+ total: number;
66
+ available: number;
67
+ };
68
+ /** Tier window info */
69
+ export type TierWindow = {
70
+ total_used: number;
71
+ total_remaining: number;
72
+ total_max: number;
73
+ remaining_pct: number;
74
+ tier_availability: Record<string, TierAvailability>;
75
+ };
76
+ /** Model group tiers */
77
+ export type GroupTiers = {
78
+ [tierName: string]: {
29
79
  priority: number;
30
80
  total: number;
31
- }>;
32
- total_remaining_pct: number;
33
- total_requests_max: number;
34
- total_requests_remaining: number;
35
- total_requests_used: number;
81
+ };
36
82
  };
37
- /** Model quota information */
38
- export type ModelQuota = {
39
- requests: number;
40
- request_count: number;
41
- success_count: number;
42
- failure_count: number;
43
- prompt_tokens: number;
44
- prompt_tokens_cached: number;
45
- completion_tokens: number;
46
- approx_cost: number;
47
- window_start_ts: number | null;
48
- quota_reset_ts: number | null;
49
- baseline_remaining_fraction: number | null;
50
- baseline_fetched_at: number | null;
51
- quota_max_requests: number;
52
- quota_display: string;
83
+ /** Fair cycle summary */
84
+ export type FairCycleSummary = {
85
+ exhausted_count: number;
86
+ total_count: number;
53
87
  };
54
- /** Model group information */
55
- export type ModelGroup = {
56
- confidence: string;
57
- display: string;
58
- is_exhausted: boolean;
59
- models: string[];
60
- remaining_pct: number;
61
- requests_max: number;
62
- requests_remaining: number;
63
- requests_used: number;
64
- reset_time_iso: string | null;
88
+ /** Model group aggregation */
89
+ export type ModelGroupAggregation = {
90
+ tiers: GroupTiers;
91
+ windows: {
92
+ [windowName: string]: TierWindow;
93
+ };
94
+ fair_cycle_summary: FairCycleSummary;
65
95
  };
66
- /** Credential information */
67
- export type Credential = {
68
- identifier: string;
96
+ /** Credential information from new API */
97
+ export type CredentialData = {
98
+ stable_id: string;
99
+ accessor_masked?: string;
69
100
  full_path: string;
70
- status: string;
71
- last_used_ts: number;
101
+ identifier: string;
102
+ email?: string | null;
72
103
  tier?: string;
73
- requests: number;
74
- tokens: TokenStats;
75
- approx_cost: number | null;
76
- global: {
77
- requests: number;
78
- tokens: TokenStats;
79
- approx_cost: number | null;
80
- };
81
- models: Record<string, ModelQuota>;
82
- model_groups?: Record<string, ModelGroup>;
104
+ priority?: number;
105
+ active_requests?: number;
106
+ status: string;
107
+ totals: TokenStats;
108
+ model_usage?: Record<string, ModelUsage>;
109
+ group_usage?: Record<string, GroupUsage>;
110
+ last_used_at?: number;
111
+ first_used_at?: number;
83
112
  };
84
- /** Provider information */
113
+ /** Provider information from new API */
85
114
  export type Provider = {
115
+ provider: string;
86
116
  credential_count: number;
87
- active_count: number;
88
- on_cooldown_count: number;
89
- exhausted_count: number;
90
- total_requests: number;
91
- tokens: TokenStats;
92
- approx_cost: number | null;
93
- credentials: Credential[];
94
- quota_groups?: Record<string, QuotaGroup>;
95
- global?: {
96
- approx_cost: number | null;
97
- tokens: TokenStats;
98
- total_requests: number;
99
- };
117
+ rotation_mode?: string;
118
+ credentials: Record<string, CredentialData>;
119
+ quota_groups?: Record<string, ModelGroupAggregation>;
100
120
  };
101
- /** Summary statistics */
121
+ /** Summary statistics from new API */
102
122
  export type Summary = {
103
- total_providers: number;
123
+ total_providers?: number;
104
124
  total_credentials: number;
105
- active_credentials?: number;
125
+ active_credentials: number;
106
126
  exhausted_credentials?: number;
107
127
  total_requests: number;
108
128
  tokens: TokenStats;
@@ -112,7 +132,6 @@ export type Summary = {
112
132
  export type ProxyResponse = {
113
133
  providers: Record<string, Provider>;
114
134
  summary: Summary;
115
- global_summary?: Summary;
116
135
  data_source: string;
117
136
  timestamp: number;
118
137
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oEAAoE;AACpE,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE;QACV,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,KAAK,CAAC,EAAE,OAAO,CAAA;KAChB,CAAA;CACF,CAAA;AAED,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,8BAA8B;AAC9B,MAAM,MAAM,UAAU,GAAG;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,iBAAiB,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC1E,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,wBAAwB,EAAE,MAAM,CAAA;IAChC,mBAAmB,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED,8BAA8B;AAC9B,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,iBAAiB,EAAE,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,8BAA8B;AAC9B,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,OAAO,CAAA;IACrB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B,CAAA;AAED,6BAA6B;AAC7B,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,UAAU,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAA;QAChB,MAAM,EAAE,UAAU,CAAA;QAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B,CAAA;IACD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CAC1C,CAAA;AAED,2BAA2B;AAC3B,MAAM,MAAM,QAAQ,GAAG;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;IACzB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,UAAU,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACzC,MAAM,CAAC,EAAE;QACP,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,MAAM,EAAE,UAAU,CAAA;QAClB,cAAc,EAAE,MAAM,CAAA;KACvB,CAAA;CACF,CAAA;AAED,yBAAyB;AACzB,MAAM,MAAM,OAAO,GAAG;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,UAAU,CAAA;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC,CAAA;AAED,2DAA2D;AAC3D,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/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,CAqBhB;AA0GD,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;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,CAwChB;AA0GD,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
@@ -2,25 +2,45 @@
2
2
  * Renders usage snapshots into readable status text.
3
3
  */
4
4
  export async function sendStatusMessage(options) {
5
- const sent = await options.client.session
5
+ // 1. Send to Companion via Bus
6
+ // @ts-ignore
7
+ const bus = options.client.bus;
8
+ if (bus) {
9
+ try {
10
+ await bus.publish({
11
+ topic: "companion.projection",
12
+ body: {
13
+ key: "usage",
14
+ kind: "markdown",
15
+ content: options.text,
16
+ },
17
+ });
18
+ }
19
+ catch { }
20
+ }
21
+ // 2. Send plain message to TUI
22
+ await options.client.session
6
23
  .prompt({
7
24
  path: { id: options.sessionID },
8
25
  body: {
9
26
  noReply: true,
10
- agent: options.state.agent,
11
- model: options.state.model,
12
- parts: [{ type: "text", text: options.text, ignored: true }],
27
+ parts: [
28
+ {
29
+ type: "text",
30
+ text: options.text,
31
+ ignored: true,
32
+ },
33
+ ],
13
34
  },
14
35
  })
15
- .then(() => true)
16
- .catch(() => false);
17
- if (sent)
18
- return;
19
- await options.client.tui
20
- .showToast({
21
- body: { title: "Usage Status", message: options.text, variant: "info" },
22
- })
23
- .catch(() => { });
36
+ .catch(async () => {
37
+ // 3. Fallback: Toast
38
+ await options.client.tui
39
+ .showToast({
40
+ body: { title: "Usage Status", message: options.text, variant: "info" },
41
+ })
42
+ .catch(() => { });
43
+ });
24
44
  }
25
45
  function formatBar(remainingPercent) {
26
46
  const clamped = Math.max(0, Math.min(100, remainingPercent));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/opencode-usage-plugin",
3
- "version": "0.0.1",
3
+ "version": "0.0.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",
@@ -1,3 +0,0 @@
1
- export { usageTool } from "./usage";
2
- export { createProxyLimitsTool } from "./proxy-limits";
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { usageTool } from "./usage";
2
- export { createProxyLimitsTool } from "./proxy-limits";
@@ -1,16 +0,0 @@
1
- /**
2
- * Proxy limits tool for checking quota stats.
3
- */
4
- type SendStatusFn = (sessionID: string, text: string) => Promise<void>;
5
- type MarkSilentFn = (sessionID: string, messageID: string) => void;
6
- export declare function createProxyLimitsTool(sendStatus: SendStatusFn, markSilent: MarkSilentFn): {
7
- description: string;
8
- args: {
9
- refresh: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodBoolean>>;
10
- };
11
- execute(args: {
12
- refresh: boolean;
13
- }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
14
- };
15
- export {};
16
- //# sourceMappingURL=proxy-limits.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"proxy-limits.d.ts","sourceRoot":"","sources":["../../src/tools/proxy-limits.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,KAAK,YAAY,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AACtE,KAAK,YAAY,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;AAElE,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY;;;;;;;;EA+BvF"}
@@ -1,33 +0,0 @@
1
- /**
2
- * Proxy limits tool for checking quota stats.
3
- */
4
- import { tool } from "@opencode-ai/plugin";
5
- import { loadProxyConfig, fetchProxyLimits, formatProxyLimits } from "../providers/proxy";
6
- export function createProxyLimitsTool(sendStatus, markSilent) {
7
- return tool({
8
- description: "Check current usage limits from the antigravity proxy server. Displays results as an inline status message.",
9
- args: {
10
- refresh: tool.schema
11
- .boolean()
12
- .optional()
13
- .default(false)
14
- .describe("Force refresh the limits data (default: false)"),
15
- },
16
- async execute(_args, context) {
17
- markSilent(context.sessionID, context.messageID);
18
- try {
19
- const config = await loadProxyConfig();
20
- const data = await fetchProxyLimits(config);
21
- const message = formatProxyLimits(data);
22
- await sendStatus(context.sessionID, message);
23
- return "";
24
- }
25
- catch (error) {
26
- const message = error instanceof Error ? error.message : String(error);
27
- const errorMessage = `Proxy Limits Error\n\n${message}`;
28
- await sendStatus(context.sessionID, errorMessage);
29
- return "";
30
- }
31
- },
32
- });
33
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Provides a simple tool wrapper for usage info.
3
- * Keeps tool definitions separate from hooks and rendering.
4
- */
5
- export declare const usageTool: () => {
6
- description: string;
7
- args: {};
8
- execute(args: Record<string, never>, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
9
- };
10
- //# sourceMappingURL=usage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/tools/usage.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,SAAS;;;;CAOlB,CAAA"}
@@ -1,12 +0,0 @@
1
- /**
2
- * Provides a simple tool wrapper for usage info.
3
- * Keeps tool definitions separate from hooks and rendering.
4
- */
5
- import { tool } from "@opencode-ai/plugin";
6
- export const usageTool = () => tool({
7
- description: "Get current rate limit snapshots for all providers",
8
- args: {},
9
- async execute() {
10
- return "Run /usage in the chat to see current limits.";
11
- },
12
- });