agy-cli-usage 0.3.1 → 0.4.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/CHANGELOG.md CHANGED
@@ -10,6 +10,14 @@
10
10
 
11
11
  * use plain v* tags in release-please ([#12](https://github.com/abruption/agy-cli-usage/issues/12)) ([74b648d](https://github.com/abruption/agy-cli-usage/commit/74b648df24967f71a43095a80e7340a6b5ac2e39)), closes [#9](https://github.com/abruption/agy-cli-usage/issues/9)
12
12
 
13
+ ## [0.4.0](https://github.com/abruption/agy-cli-usage/compare/v0.3.1...v0.4.0) (2026-06-24)
14
+
15
+
16
+ ### Features
17
+
18
+ * ship as a compiled TypeScript build with bundled type declarations ([0e253ea](https://github.com/abruption/agy-cli-usage/commit/0e253ea353e99efe5bfd176f5b6775d76718b3a9))
19
+ * ship compiled TypeScript build with type declarations ([#20](https://github.com/abruption/agy-cli-usage/issues/20)) ([0e253ea](https://github.com/abruption/agy-cli-usage/commit/0e253ea353e99efe5bfd176f5b6775d76718b3a9))
20
+
13
21
  ## [0.3.1](https://github.com/abruption/agy-cli-usage/compare/v0.3.0...v0.3.1) (2026-06-24)
14
22
 
15
23
 
package/README.md CHANGED
@@ -1,89 +1,124 @@
1
+ <div align="center">
2
+
1
3
  # agy-cli-usage
2
4
 
5
+ [![npm version](https://img.shields.io/npm/v/agy-cli-usage?color=cb3837&logo=npm)](https://www.npmjs.com/package/agy-cli-usage)
6
+ [![npm downloads](https://img.shields.io/npm/dm/agy-cli-usage?color=cb3837&logo=npm)](https://www.npmjs.com/package/agy-cli-usage)
3
7
  [![CI](https://github.com/abruption/agy-cli-usage/actions/workflows/ci.yml/badge.svg)](https://github.com/abruption/agy-cli-usage/actions/workflows/ci.yml)
4
- [![npm](https://img.shields.io/npm/v/agy-cli-usage.svg)](https://www.npmjs.com/package/agy-cli-usage)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
9
+ [![node](https://img.shields.io/node/v/agy-cli-usage?color=339933&logo=node.js)](https://www.npmjs.com/package/agy-cli-usage)
10
+ [![license](https://img.shields.io/npm/l/agy-cli-usage?color=blue)](LICENSE)
5
11
 
6
- Headless usage/quota monitor for the Antigravity CLI (`agy`). `agy`의 인터랙티브 `/usage` 패널과 동일한 정보(모델 그룹별 주간·5시간 한도, 잔여율, 리프레시 시각)를 **headless로** 조회한다. IDE 불필요.
12
+ **Headless usage & quota monitor for the Antigravity CLI (`agy`).**
7
13
 
8
- `agy -p "<prompt>"`(headless)는 `/usage` 같은 TUI 슬래시 커맨드를 렌더링하지 않으므로 자동화가 불가능하다. 도구는 `agy`가 내부적으로 호출하는 Cloud Code API를 직접 호출하거나, 불가능할 `agy`를 PTY로 구동해 패널을 파싱한다.
14
+ `agy`의 `/usage` 패널(모델 그룹별 주간·5시간 한도, 잔여율, 리프레시 시각)을 **헤드리스로** 조회합니다 IDE 불필요, macOS · Linux · Windows · 헤드리스 서버 지원.
9
15
 
10
- ## 설치
16
+ <sub>Inspired by <a href="https://github.com/skainguyen1412/antigravity-usage">skainguyen1412/antigravity-usage</a> — which targets the Antigravity <b>IDE</b>; this targets the <b>CLI</b> (<code>agy</code>).</sub>
11
17
 
12
- ```bash
13
- # npm 레지스트리에서
14
- npm install -g agy-cli-usage
18
+ </div>
19
+
20
+ ---
15
21
 
16
- # 또는 GitHub에서 직접
17
- npm install -g github:abruption/agy-cli-usage
18
- npx github:abruption/agy-cli-usage
22
+ ```text
23
+ Models & Quota
24
+ Account: you@gmail.com
19
25
 
20
- # 또는 소스에서
21
- git clone https://github.com/abruption/agy-cli-usage && cd agy-cli-usage
22
- npm install && npm link
26
+ GEMINI MODELS
27
+ Weekly Limit
28
+ [██████████████████████████████████████████████░░░░] 91.72%
29
+ 92% remaining · Refreshes in 73h 23m
30
+ Five Hour Limit
31
+ [███████████████████████████████████████████████░░░] 94.63%
32
+ 95% remaining · Refreshes in 2h 2m
33
+
34
+ CLAUDE AND GPT MODELS
35
+ Weekly Limit
36
+ [█████████████████████████████████████████████████░] 97.78%
37
+ 98% remaining · Refreshes in 26h 17m
38
+ Five Hour Limit
39
+ [██████████████████████████████████████████████████] Quota available
23
40
  ```
24
41
 
25
- 전역 명령은 `agy-cli-usage` (별칭 `agy-usage`)로 등록된다. Node.js >= 18 (전역 `fetch` 필요). 사전 조건: 같은 머신에서 `agy`에 로그인되어 있을 것.
42
+ ## 🚀 빠른 시작
26
43
 
27
- ## 사용법
44
+ ```bash
45
+ # 설치 없이 한 번 실행
46
+ npx agy-cli-usage
47
+
48
+ # 전역 설치 → 어디서나 `agy-cli-usage`
49
+ npm install -g agy-cli-usage
50
+ agy-cli-usage
51
+ ```
52
+
53
+ > 사전 조건: 같은 머신에서 `agy`에 로그인되어 있을 것. Node.js >= 18.
54
+
55
+ ## ⚡ 사용법
28
56
 
29
57
  ```bash
30
- agy-usage # /usage 와 동일한 패널 출력 (1회성)
31
- agy-usage --json # 머신 리더블 JSON
32
- agy-usage --watch 60 # 60초 간격 자동 갱신 (5분 캐시 경유)
33
- agy-usage --source api|pty|auto # 데이터 소스 (기본 auto: API → 실패 시 PTY)
34
- agy-usage --channel daily|prod # Cloud Code 호스트 (기본 auto: daily→prod 탐지)
35
- agy-usage --no-cache # 5분 캐시 무시하고 강제 조회
58
+ agy-cli-usage # /usage 와 동일한 패널 (1회성)
59
+ agy-cli-usage --json # 머신 리더블 JSON
60
+ agy-cli-usage --watch 60 # 60초 간격 자동 갱신 (5분 캐시 경유)
61
+ agy-cli-usage update [--check] # 자기 업데이트 (--check: 알림만)
62
+ agy-cli-usage --version # 버전 출력
36
63
  ```
37
64
 
38
- ## 동작 방식
65
+ | 플래그 | 설명 |
66
+ |--------|------|
67
+ | `--json` | 정규화 JSON 출력 (스크립트/대시보드 연동) |
68
+ | `--watch [초]` | N초 간격 갱신 (기본 60) |
69
+ | `--source <auto\|api\|pty>` | 데이터 소스 (기본 `auto`: API → 실패 시 PTY) |
70
+ | `--channel <auto\|daily\|prod>` | Cloud Code 호스트 |
71
+ | `--no-cache` / `--refresh` | 5분 캐시 무시하고 강제 조회 |
72
+
73
+ ## 🤔 왜 필요한가
39
74
 
40
- `agy`는 OAuth 토큰을 OS 키링(zalando/go-keyring 규약, service=`gemini`/account=`antigravity`)에 저장한다.
75
+ `agy -p "<prompt>"`(headless)는 LLM 프롬프트 전용이라 `/usage` 같은 TUI 슬래시 커맨드를 렌더링하지 않습니다. 그래서 사용량을 자동화·폴링할 수 없습니다. 이 도구는 그 공백을 메웁니다.
41
76
 
42
- 1. **직접 API (기본)** — 토큰을 읽고(만료 시 자동 refresh), `agy`와 동일하게:
43
- - `POST https://<host>/v1internal:loadCodeAssist` → `cloudaicompanionProject` 획득
44
- - `POST https://<host>/v1internal:retrieveUserQuotaSummary {project}` → 쿼타
45
- - 호스트: `daily-cloudcode-pa.googleapis.com`(현재 CLI) 또는 `cloudcode-pa.googleapis.com`
46
- - 토큰 소스: **키링 → 토큰 파일 → (실패 시) PTY** 순으로 자동 시도
47
- 2. **PTY 폴백** — 위 토큰 소스를 모두 읽을 수 없거나 내부 API가 바뀌면, `agy`를 가상 터미널로 띄워 `/usage`를 보내고, `@xterm/headless`로 alt-screen을 재구성해 패널을 파싱한다.
77
+ ## 🔍 동작 방식
48
78
 
49
- ### 크로스플랫폼 자격증명
79
+ 1. **직접 API (기본 · 빠름)** — OS에서 `agy`의 OAuth 토큰을 읽어, `agy`가 시작 시 호출하는 것과 동일한 Cloud Code 내부 API를 직접 호출합니다.
80
+ - `POST /v1internal:loadCodeAssist` → `cloudaicompanionProject` 획득
81
+ - `POST /v1internal:retrieveUserQuotaSummary {project}` → 쿼타
82
+ - 만료 시 OAuth refresh 자동 처리.
83
+ 2. **PTY 폴백 (안전망)** — 위 토큰을 못 읽거나 내부 API가 바뀌면, `agy`를 가상 터미널로 띄워 `/usage`를 보내고 `@xterm/headless`로 화면을 재구성해 파싱합니다.
50
84
 
51
- | OS | 토큰 소스 | 비고 |
52
- |----|-----------|------|
53
- | macOS | Keychain | `@napi-rs/keyring`, CLI 폴백 `security` |
54
- | Windows | Credential Manager | `@napi-rs/keyring` |
55
- | Linux (데스크톱) | Secret Service | `@napi-rs/keyring`, CLI 폴백 `secret-tool` |
56
- | Linux (헤드리스) | **토큰 파일** | 키링 부재 시 `~/.gemini/antigravity-cli/antigravity-oauth-token`(순수 JSON) 읽음 → **API 경로 동작**. `AGY_OAUTH_TOKEN_FILE`로 경로 override |
85
+ ## 🔐 크로스플랫폼 자격증명
57
86
 
58
- > 헤드리스 서버에서도 토큰 파일을 읽어 빠른 API 경로를 쓴다. 파일조차 없으면 `--source pty`로 우회(python3 필요).
87
+ 토큰은 `agy`가 저장한 위치에서 **읽기만** 합니다. 플랫폼별로 자동 처리:
59
88
 
60
- ## HTTP 엔드포인트 (선택)
89
+ | OS / 환경 | 저장 위치 | 읽는 방법 |
90
+ |-----------|----------|----------|
91
+ | macOS | Keychain | `@napi-rs/keyring` (폴백 `security`) |
92
+ | Linux 데스크톱 | Secret Service | `@napi-rs/keyring` (폴백 `secret-tool`) |
93
+ | **Windows** | Credential Manager | 내장 `powershell.exe`로 Win32 `CredRead` 호출 |
94
+ | **헤드리스 Linux** | 토큰 파일 | `~/.gemini/antigravity-cli/antigravity-oauth-token` |
95
+
96
+ 읽기 체인: `키링 → OS CLI → Windows credman → 토큰 파일 → PTY`. `AGY_OAUTH_TOKEN_FILE`로 파일 경로 override.
97
+
98
+ ## 🔌 HTTP 엔드포인트 (선택)
61
99
 
62
100
  ```bash
63
101
  PORT=3007 node server.js # GET /quota → 정규화 JSON (5분 캐시), GET /healthz
64
102
  ```
65
103
 
66
- 외부 대시보드/스크립트에서 `GET /quota`로 소비하거나, `agy-usage --json`을 subprocess로 호출한다.
104
+ 외부 대시보드/스크립트에서 `GET /quota`로 소비하거나 `agy-cli-usage --json`을 subprocess로 호출하세요.
67
105
 
68
- ## 개발 / 릴리스
106
+ ## 🛠️ 개발 / 릴리스
69
107
 
70
108
  ```bash
71
109
  npm test # node --test (자격증명·네트워크 불필요, 순수 로직)
72
110
  npm run check # 구문 검사
73
111
  ```
74
112
 
75
- - **CI** (`.github/workflows/ci.yml`): push/PR마다 ubuntu(Node 18/20/22) + macOS/Windows(Node 22)에서 테스트.
76
- - **Release** ([release-please](https://github.com/googleapis/release-please), `.github/workflows/release-please.yml`): Conventional Commits 기반 **완전 자동화**. `NPM_TOKEN` 시크릿 필요.
77
-
78
- 릴리스 절차 — 수동 버전 범프 없음:
113
+ - **CI**: push/PR마다 ubuntu(Node 18/20/22) + macOS/Windows(Node 22)에서 테스트.
114
+ - **Release**: [release-please](https://github.com/googleapis/release-please) — Conventional Commits 기반 완전 자동화. main에 머지된 커밋으로 release-please가 **Release PR**(버전 범프 + CHANGELOG) 유지하고, PR을 머지하면 태그·GitHub Release·`npm publish --provenance`가 자동 실행됩니다.
79
115
 
80
- 1. `feat:`/`fix:` 등 Conventional Commits를 main에 머지하면, release-please가 **Release PR**(버전 범프 + CHANGELOG 갱신)을 자동 생성·유지.
81
- 2. 그 **Release PR을 머지**하면 → GitHub Release + 태그 생성 → 같은 워크플로가 `npm publish --provenance` 실행.
116
+ ## ⚠️ 주의
82
117
 
83
- > "Release PR 머지" 번이 전체 배포다.
118
+ - `v1internal:retrieveUserQuotaSummary`는 **비공개 내부 엔드포인트**입니다. 스키마/호스트가 예고 없이 바뀔 수 있으며, 그때의 안전망이 PTY 폴백입니다. 본인 계정의 사용량 조회 용도로만 사용하세요.
119
+ - 자격증명은 OS 저장소에서 **읽기만** 하며, refresh 토큰을 되쓰지 않아 `agy` 세션과 충돌하지 않습니다.
120
+ - 코드에 포함된 OAuth client_id/secret은 `agy` 바이너리에 들어 있는 **installed-app(public) 값**으로, [Google 문서](https://developers.google.com/identity/protocols/oauth2)상 기밀이 아닙니다. 사용자 식별은 각자의 키링 토큰으로 이뤄집니다.
84
121
 
85
- ## 주의
122
+ ## 📄 License
86
123
 
87
- - `v1internal:retrieveUserQuotaSummary`는 **비공개 내부 엔드포인트**다. 스키마/호스트가 예고 없이 바뀔 수 있으며, 그때를 위한 안전망이 PTY 폴백이다.
88
- - 본인 계정의 키링 토큰만 읽으며(읽기 전용), refresh 토큰을 키링에 되쓰지 않는다.
89
- - OAuth client_secret은 데스크톱(공개) 클라이언트 값이라 비밀이 아니다.
124
+ [MIT](LICENSE) © abruption
@@ -0,0 +1,12 @@
1
+ import type { FetchResult } from './types.js';
2
+ declare class ApiError extends Error {
3
+ status: number;
4
+ constructor(message: string, status: number);
5
+ }
6
+ export interface FetchOptions {
7
+ host?: string;
8
+ channel?: 'daily' | 'prod';
9
+ }
10
+ /** Fetch the raw quota summary from the Cloud Code API. */
11
+ export declare function fetchQuotaSummary(accessToken: string, opts?: FetchOptions): Promise<FetchResult>;
12
+ export { ApiError };
@@ -0,0 +1,84 @@
1
+ // Direct client for the Antigravity / Gemini Code Assist "Cloud Code" internal API.
2
+ //
3
+ // Reproduces exactly what `agy` does on startup to populate its /usage panel:
4
+ // 1. POST /v1internal:loadCodeAssist {metadata:{ideType:"ANTIGRAVITY"}}
5
+ // -> { cloudaicompanionProject, currentTier, ... }
6
+ // 2. POST /v1internal:retrieveUserQuotaSummary {project:<cloudaicompanionProject>}
7
+ // -> { groups:[...], description }
8
+ //
9
+ // Captured from live agy traffic (mitmproxy). The internal endpoint is undocumented;
10
+ // the PTY fallback exists for when it changes.
11
+ const UA = `antigravity-usage-monitor/0.1 ${process.platform}/${process.arch}`;
12
+ // Antigravity ships against the "daily" Cloud Code host; stable builds use the
13
+ // plain host. Try daily first (matches current CLI), fall back to prod.
14
+ const HOSTS = ['daily-cloudcode-pa.googleapis.com', 'cloudcode-pa.googleapis.com'];
15
+ class ApiError extends Error {
16
+ status;
17
+ constructor(message, status) {
18
+ super(message);
19
+ this.status = status;
20
+ }
21
+ }
22
+ function extractEmail(uri) {
23
+ const m = uri?.match(/[?&]Email=([^&]+)/);
24
+ if (!m)
25
+ return null;
26
+ try {
27
+ return decodeURIComponent(m[1]);
28
+ }
29
+ catch {
30
+ return m[1];
31
+ }
32
+ }
33
+ async function postInternal(host, accessToken, method, body) {
34
+ const res = await fetch(`https://${host}/v1internal:${method}`, {
35
+ method: 'POST',
36
+ headers: {
37
+ Authorization: `Bearer ${accessToken}`,
38
+ 'Content-Type': 'application/json',
39
+ 'User-Agent': UA,
40
+ },
41
+ body: JSON.stringify(body),
42
+ });
43
+ if (!res.ok) {
44
+ throw new ApiError(`${method} -> HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`, res.status);
45
+ }
46
+ return (await res.json());
47
+ }
48
+ /** Fetch the raw quota summary from the Cloud Code API. */
49
+ export async function fetchQuotaSummary(accessToken, opts = {}) {
50
+ const candidates = opts.host
51
+ ? [opts.host]
52
+ : opts.channel === 'prod'
53
+ ? ['cloudcode-pa.googleapis.com']
54
+ : opts.channel === 'daily'
55
+ ? ['daily-cloudcode-pa.googleapis.com']
56
+ : HOSTS;
57
+ let lastErr;
58
+ for (const host of candidates) {
59
+ try {
60
+ const lca = await postInternal(host, accessToken, 'loadCodeAssist', {
61
+ metadata: { ideType: 'ANTIGRAVITY' },
62
+ });
63
+ const project = lca.cloudaicompanionProject;
64
+ if (!project)
65
+ throw new ApiError('loadCodeAssist returned no cloudaicompanionProject', 0);
66
+ const raw = await postInternal(host, accessToken, 'retrieveUserQuotaSummary', { project });
67
+ return {
68
+ raw,
69
+ host,
70
+ tier: lca.currentTier?.id ?? null,
71
+ account: extractEmail(lca.currentTier?.upgradeSubscriptionUri),
72
+ };
73
+ }
74
+ catch (err) {
75
+ lastErr = err;
76
+ // 404 / wrong-host -> try next candidate; auth errors -> stop early.
77
+ if (err instanceof ApiError && (err.status === 401 || err.status === 403))
78
+ throw err;
79
+ }
80
+ }
81
+ throw lastErr ?? new ApiError('No Cloud Code host responded', 0);
82
+ }
83
+ export { ApiError };
84
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ declare class CredentialError extends Error {
2
+ }
3
+ interface Cred {
4
+ accessToken: string;
5
+ refreshToken: string | null;
6
+ expiry: Date | null;
7
+ authMethod: string | null;
8
+ }
9
+ export declare function decodeSecret(raw: string): Cred;
10
+ export interface AccessToken {
11
+ accessToken: string;
12
+ authMethod: string | null;
13
+ }
14
+ /**
15
+ * Returns a valid access token for the Cloud Code API, refreshing if needed.
16
+ * Throws CredentialError if no credential can be read from any keyring backend
17
+ * (the caller should then consider the PTY fallback).
18
+ */
19
+ export declare function getAccessToken(): Promise<AccessToken>;
20
+ /** Whether a keyring-based credential is readable at all (no refresh attempted). */
21
+ export declare function hasCredential(): Promise<boolean>;
22
+ export { CredentialError };