Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# CI workflow for VSCode z.ai Usage extension
name: CI

on:
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: Biome Check (format + lint)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm

- name: Install dependencies
run: npm ci

- name: Run Biome check
run: npm run check:ci

typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm

- name: Install dependencies
run: npm ci

- name: TypeScript type check
run: npx tsc --noEmit

build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm

- name: Install dependencies
run: npm ci

- name: Compile TypeScript
run: npm run compile

ci-status:
name: CI status check
runs-on: ubuntu-latest
needs: [check, typecheck, build]
if: always()
steps:
- name: Some checks failed
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
run: exit 1
- name: All checks passed
run: exit 0
87 changes: 87 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Publish Extension

on:
push:
branches:
- main

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Get version from package.json
id: package-version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=v$VERSION" >> $GITHUB_OUTPUT

- name: Check if tag exists
id: check-tag
run: |
if git rev-parse "v${{ steps.package-version.outputs.version }}" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi

- name: Exit if tag exists
if: steps.check-tag.outputs.exists == 'true'
run: |
echo "Tag ${{ steps.package-version.outputs.tag }} already exists. Skipping publish."
exit 0

- name: Install dependencies
if: steps.check-tag.outputs.exists == 'false'
run: npm ci

- name: Install vsce
if: steps.check-tag.outputs.exists == 'false'
run: npm install -g @vscode/vsce

- name: Create and push tag
if: steps.check-tag.outputs.exists == 'false'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag ${{ steps.package-version.outputs.tag }}
git push origin ${{ steps.package-version.outputs.tag }}

- name: Publish to VS Marketplace
if: steps.check-tag.outputs.exists == 'false'
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: vsce publish -p $VSCE_PAT

- name: Extract changelog for version
if: steps.check-tag.outputs.exists == 'false'
id: changelog
run: |
VERSION=${{ steps.package-version.outputs.version }}
CHANGELOG=$(awk "/## \[$VERSION\]/,/## \[/" CHANGELOG.md | sed '1d;$d' | sed '/^$/d')
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Create GitHub Release
if: steps.check-tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.package-version.outputs.tag }}
name: Release ${{ steps.package-version.outputs.tag }}
body: ${{ steps.changelog.outputs.content }}
draft: false
prerelease: false
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
out/
*.vsix
.vscode-test/
.worktrees/
.DS_Store
CLAUDE.md
plans/
13 changes: 13 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
]
}
]
}
16 changes: 16 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.vscode/**
.vscode-test/**
src/**
.gitignore
**/tsconfig.json
**/*.map
**/*.ts
!node_modules/**
biome.json
.github/**
CLAUDE.md
assets/icons/**
assets/fonts/*.css
assets/fonts/*.html
assets/fonts/*.json
assets/fonts/*.ts
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0] - 2026-03-12

### Added

- Initial release
- Display z.ai token usage percentage in the VS Code status bar
- Show time remaining until quota resets (`nextResetTime`) when available
- Secure API key storage via VS Code Secret Storage
- Configurable refresh interval (`zaiUsage.refreshInterval`, default 60s)
- Toggle between z.ai icon and text prefix (`zaiUsage.useIcon`)
- Command: `z.ai Usage: Set API Key` — enter and verify your z.ai API token
- Command: `z.ai Usage: Clear API Key` — remove the stored API token
- Status bar click triggers API key setup dialog when unauthenticated
- Cache invalidation when `nextResetTime` has passed
65 changes: 64 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,64 @@
# vscode-zai-usage
<div align="center">

# VSCode z.ai Usage

![Status bar example](./assets/screenshots/statusbar.png)

[![Latest Release](https://img.shields.io/github/v/release/j4rviscmd/vscode-zai-usage?style=for-the-badge&color=green&label=Latest&logo=github&logoColor=white)](https://github.com/j4rviscmd/vscode-zai-usage/releases/latest)
[![Last Commit](https://img.shields.io/github/last-commit/j4rviscmd/vscode-zai-usage/main?style=for-the-badge&color=1F6FEB&label=Last%20Update&logo=git&logoColor=white)](https://github.com/j4rviscmd/vscode-zai-usage/commits/main)
[![License](https://img.shields.io/badge/License-MIT-018FF5?style=for-the-badge&logo=opensourceinitiative&logoColor=white)](LICENSE)

## Display your [z.ai](https://z.ai) token usage percentage directly in the VS Code status bar

</div>

## Features

- Shows token quota usage (e.g. `45% (2h30m)`) in the status bar
- Displays time remaining until quota resets when available
- Automatically refreshes at a configurable interval
- Secure API key storage via VS Code Secret Storage

**Status bar examples:**

| Situation | Display |
| ------------------------------ | --------------- |
| Authenticated, usage available | `⬡ 45% (2h30m)` |
| Authenticated, no reset time | `⬡ 45%` |
| API key not set | `⬡ Set API Key` |
| Error / fetch failed | `⬡ -` |

> The `⬡` icon is the z.ai icon. Set `zaiUsage.useIcon: false` to display `z.ai:` as a text prefix instead.

## Setup

1. Get your API token from the [z.ai API Key page](https://z.ai/manage-apikey/apikey-list)
2. Open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`)
3. Run **z.ai Usage: Set API Key** and paste your token
4. The status bar will update immediately

## Commands

| Command | Description |
| --------------------------- | ------------------------------------ |
| `z.ai Usage: Set API Key` | Enter and verify your z.ai API token |
| `z.ai Usage: Clear API Key` | Remove the stored API token |

## Settings

| Setting | Type | Default | Description |
| -------------------------- | --------- | ------- | -------------------------------------------------- |
| `zaiUsage.refreshInterval` | `number` | `60` | Data refresh interval in seconds |
| `zaiUsage.useIcon` | `boolean` | `true` | Use z.ai icon (`⬡`) instead of text prefix `z.ai:` |

## Requirements

- VS Code 1.85.0 or higher
- A valid z.ai API token

## License

> [!WARNING]
> This extension uses an internal z.ai API (`api.z.ai/api/monitor/usage/quota/limit`) which is not officially documented. The API may change without notice, which could break this extension.

MIT
19 changes: 19 additions & 0 deletions assets/fonts/zai-icons.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@font-face {
font-family: "zai-icons";
src: url("./zai-icons.woff?210fea25eb9b126fc5df23b3039ee6c0") format("woff");
}

i[class^="icon-"]:before, i[class*=" icon-"]:before {
font-family: zai-icons !important;
font-style: normal;
font-weight: normal !important;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

.icon-z-ai:before {
content: "\f101";
}
69 changes: 69 additions & 0 deletions assets/fonts/zai-icons.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>zai-icons</title>

<style>
body {
font-family: sans-serif;
margin: 0;
padding: 10px 20px;
text-align: center;
}
.preview {
width: 100px;
display: inline-block;
margin: 10px;
}
.preview .inner {
display: inline-block;
width: 100%;
text-align: center;
background: #f5f5f5;
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
.preview .inner {
line-height: 85px;
font-size: 40px;
color: #333;
}
.label {
display: inline-block;
width: 100%;
box-sizing: border-box;
padding: 5px;
font-size: 10px;
font-family: Monaco, monospace;
color: #666;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background: #ddd;
-webkit-border-radius: 0 0 3px 3px;
-moz-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
color: #666;
}
</style>

<link rel="stylesheet" type="text/css" href="zai-icons.css" />
</head>
<body>

<h1>zai-icons</h1>


<div class="preview">
<span class="inner">
<i class="icon icon-z-ai"></i>
</span>
<br>
<span class='label'>z-ai</span>
</div>


</body>
</html>
Loading
Loading