agy-cli-usage 0.2.0 → 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/CHANGELOG.md +23 -0
- package/README.md +7 -5
- package/package.json +2 -1
- package/src/main.js +12 -2
- package/src/update.js +87 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## [0.3.0](https://github.com/abruption/agy-cli-usage/compare/v0.2.0...v0.3.0) (2026-06-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add --version flag and self-update command ([#8](https://github.com/abruption/agy-cli-usage/issues/8)) ([811e82f](https://github.com/abruption/agy-cli-usage/commit/811e82f057e709b92b7a7665b01c54dde913b0d4)), closes [#7](https://github.com/abruption/agy-cli-usage/issues/7)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
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
|
+
|
|
13
|
+
## [0.2.0](https://github.com/abruption/agy-cli-usage/compare/v0.1.0...v0.2.0) (2026-06-24)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **credentials:** read agy token file for headless Linux (no keyring) ([5914e6c](https://github.com/abruption/agy-cli-usage/commit/5914e6cc3cf3977bb6c6f974f68bd40209bc376e))
|
|
18
|
+
|
|
19
|
+
## [0.1.0](https://github.com/abruption/agy-cli-usage/compare/e1d582dd618fe06fd607d00846f5b7a66acf924d...v0.1.0) (2026-06-24)
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* headless usage/quota monitor for Antigravity CLI (agy) ([e1d582d](https://github.com/abruption/agy-cli-usage/commit/e1d582dd618fe06fd607d00846f5b7a66acf924d))
|
package/README.md
CHANGED
|
@@ -73,12 +73,14 @@ npm run check # 구문 검사
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
- **CI** (`.github/workflows/ci.yml`): push/PR마다 ubuntu(Node 18/20/22) + macOS/Windows(Node 22)에서 테스트.
|
|
76
|
-
- **Release** (`.github/workflows/release.yml`):
|
|
76
|
+
- **Release** ([release-please](https://github.com/googleapis/release-please), `.github/workflows/release-please.yml`): Conventional Commits 기반 **완전 자동화**. `NPM_TOKEN` 시크릿 필요.
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
릴리스 절차 — 수동 버전 범프 없음:
|
|
79
|
+
|
|
80
|
+
1. `feat:`/`fix:` 등 Conventional Commits를 main에 머지하면, release-please가 **Release PR**(버전 범프 + CHANGELOG 갱신)을 자동 생성·유지.
|
|
81
|
+
2. 그 **Release PR을 머지**하면 → GitHub Release + 태그 생성 → 같은 워크플로가 `npm publish --provenance` 실행.
|
|
82
|
+
|
|
83
|
+
> 즉 "Release PR 머지" 한 번이 전체 배포다.
|
|
82
84
|
|
|
83
85
|
## 주의
|
|
84
86
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agy-cli-usage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Headless usage/quota monitor for the Antigravity CLI (agy) — reads Cloud Code quota directly, with a PTY fallback. No IDE required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"src/",
|
|
21
21
|
"server.js",
|
|
22
22
|
"README.md",
|
|
23
|
+
"CHANGELOG.md",
|
|
23
24
|
"LICENSE"
|
|
24
25
|
],
|
|
25
26
|
"keywords": [
|
package/src/main.js
CHANGED
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
// agy-usage --channel daily|prod Cloud Code host (default: auto-detect)
|
|
10
10
|
// agy-usage --no-cache bypass the 5-minute cache
|
|
11
11
|
// agy-usage --refresh force a fresh fetch (alias for --no-cache)
|
|
12
|
+
// agy-usage update [--check] self-update via npm
|
|
13
|
+
// agy-usage --version | -v print the installed version
|
|
12
14
|
|
|
13
15
|
import { getAccessToken, CredentialError } from './credentials.js';
|
|
14
16
|
import { fetchQuotaSummary } from './api.js';
|
|
15
17
|
import { captureUsageViaPty } from './pty-fallback.js';
|
|
16
18
|
import { fromApi, fromPty } from './quota.js';
|
|
17
19
|
import { renderPanel } from './render.js';
|
|
20
|
+
import { currentVersion, runUpdate } from './update.js';
|
|
18
21
|
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
19
22
|
import { homedir } from 'node:os';
|
|
20
23
|
import { join } from 'node:path';
|
|
@@ -24,16 +27,19 @@ const CACHE_FILE = join(CACHE_DIR, 'quota.json');
|
|
|
24
27
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
25
28
|
|
|
26
29
|
function parseArgs(argv) {
|
|
27
|
-
const o = { json: false, watch: null, source: 'auto', channel: 'auto', cache: true };
|
|
30
|
+
const o = { json: false, watch: null, source: 'auto', channel: 'auto', cache: true, command: null, check: false };
|
|
28
31
|
for (let i = 0; i < argv.length; i++) {
|
|
29
32
|
const a = argv[i];
|
|
30
|
-
if (a === '
|
|
33
|
+
if (a === 'update' && o.command == null) o.command = 'update';
|
|
34
|
+
else if (a === '--json') o.json = true;
|
|
31
35
|
else if (a === '--watch') {
|
|
32
36
|
const n = Number(argv[i + 1]);
|
|
33
37
|
if (Number.isFinite(n)) { o.watch = n; i++; } else o.watch = 60;
|
|
34
38
|
} else if (a === '--source') o.source = argv[++i];
|
|
35
39
|
else if (a === '--channel') o.channel = argv[++i];
|
|
36
40
|
else if (a === '--no-cache' || a === '--refresh') o.cache = false;
|
|
41
|
+
else if (a === '--check') o.check = true;
|
|
42
|
+
else if (a === '-v' || a === '--version') o.version = true;
|
|
37
43
|
else if (a === '-h' || a === '--help') o.help = true;
|
|
38
44
|
}
|
|
39
45
|
return o;
|
|
@@ -47,6 +53,8 @@ const HELP = `agy-usage — Antigravity CLI (agy) usage/quota monitor
|
|
|
47
53
|
agy-usage --source <auto|api|pty>
|
|
48
54
|
agy-usage --channel <auto|daily|prod>
|
|
49
55
|
agy-usage --no-cache | --refresh
|
|
56
|
+
agy-usage update [--check] self-update via npm (--check: report only)
|
|
57
|
+
agy-usage --version | -v
|
|
50
58
|
`;
|
|
51
59
|
|
|
52
60
|
// --- cache -------------------------------------------------------------------
|
|
@@ -104,6 +112,8 @@ async function once(opts) {
|
|
|
104
112
|
async function main() {
|
|
105
113
|
const opts = parseArgs(process.argv.slice(2));
|
|
106
114
|
if (opts.help) { process.stdout.write(HELP); return; }
|
|
115
|
+
if (opts.version) { process.stdout.write(currentVersion() + '\n'); return; }
|
|
116
|
+
if (opts.command === 'update') { process.exit(await runUpdate({ checkOnly: opts.check })); }
|
|
107
117
|
|
|
108
118
|
if (opts.watch != null) {
|
|
109
119
|
const intervalMs = Math.max(5, opts.watch) * 1000;
|
package/src/update.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Self-update + version helpers for the CLI.
|
|
2
|
+
//
|
|
3
|
+
// `agy-cli-usage update` check the registry and `npm install -g` if newer
|
|
4
|
+
// `agy-cli-usage update --check` report only, don't install
|
|
5
|
+
// `agy-cli-usage --version` print the installed version
|
|
6
|
+
|
|
7
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
|
|
10
|
+
const PKG_NAME = 'agy-cli-usage';
|
|
11
|
+
|
|
12
|
+
/** Installed version, read from this package's package.json. */
|
|
13
|
+
export function currentVersion() {
|
|
14
|
+
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
15
|
+
return pkg.version;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Compare two dotted versions numerically (prerelease tags ignored).
|
|
20
|
+
* @returns negative if a<b, 0 if equal, positive if a>b
|
|
21
|
+
*/
|
|
22
|
+
export function semverCompare(a, b) {
|
|
23
|
+
const norm = (v) =>
|
|
24
|
+
String(v)
|
|
25
|
+
.replace(/^v/, '')
|
|
26
|
+
.split('-')[0]
|
|
27
|
+
.split('.')
|
|
28
|
+
.map((n) => parseInt(n, 10) || 0);
|
|
29
|
+
const pa = norm(a);
|
|
30
|
+
const pb = norm(b);
|
|
31
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
32
|
+
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
33
|
+
if (d !== 0) return d;
|
|
34
|
+
}
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Latest published version: prefer the user's configured registry (npm view), fall back to public. */
|
|
39
|
+
export async function latestVersion() {
|
|
40
|
+
try {
|
|
41
|
+
const out = execFileSync('npm', ['view', PKG_NAME, 'version'], {
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
44
|
+
}).trim();
|
|
45
|
+
if (out) return out;
|
|
46
|
+
} catch {
|
|
47
|
+
// npm missing or offline — try the public registry directly
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(`https://registry.npmjs.org/${PKG_NAME}/latest`);
|
|
51
|
+
if (res.ok) return (await res.json()).version;
|
|
52
|
+
} catch {
|
|
53
|
+
// offline
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Run the update flow.
|
|
60
|
+
* @param {{ checkOnly?: boolean }} opts
|
|
61
|
+
* @returns {Promise<number>} process exit code
|
|
62
|
+
*/
|
|
63
|
+
export async function runUpdate({ checkOnly = false } = {}) {
|
|
64
|
+
const current = currentVersion();
|
|
65
|
+
const latest = await latestVersion();
|
|
66
|
+
if (!latest) {
|
|
67
|
+
process.stderr.write('Could not determine the latest version (offline or npm unavailable).\n');
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
if (semverCompare(latest, current) <= 0) {
|
|
71
|
+
process.stdout.write(`agy-cli-usage is up to date (${current}).\n`);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
process.stdout.write(`Update available: ${current} -> ${latest}\n`);
|
|
75
|
+
if (checkOnly) {
|
|
76
|
+
process.stdout.write('Run `agy-cli-usage update` to install it.\n');
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
process.stdout.write(`Installing ${PKG_NAME}@${latest} globally…\n`);
|
|
80
|
+
const r = spawnSync('npm', ['install', '-g', `${PKG_NAME}@${latest}`], { stdio: 'inherit' });
|
|
81
|
+
if (r.error) {
|
|
82
|
+
process.stderr.write(`Failed to run npm: ${r.error.message}\nInstall manually: npm install -g ${PKG_NAME}@latest\n`);
|
|
83
|
+
return 1;
|
|
84
|
+
}
|
|
85
|
+
if (r.status === 0) process.stdout.write(`Updated to ${latest}.\n`);
|
|
86
|
+
return r.status ?? 0;
|
|
87
|
+
}
|